Elasticsearch 集群(二) 对接.net 进行 封装 和CRUD 操作
Nuget 下载
Elastic.Clients.Elasticsearch

参考官网
CRUD 使用示例 |Elasticsearch .NET 客户端 [8.9] |弹性的



ESHelper 类库代码
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ESHelper
{
public static class ElasticsearchExtend
{
public static IServiceCollection AddESCoustorm(this IServiceCollection services, IConfiguration config)
{
services.Configure<ESOptions>(config.GetSection(nameof(ESOptions)));
services.AddSingleton<IESCoustorm, ESCoustorm>();
return services;
}
}
}
using Elastic.Clients.Elasticsearch;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ESHelper
{
public interface IESCoustorm
{
/// <summary>
/// 添加索引文档
/// </summary>
/// <typeparam name="TDocument">文档对象泛型</typeparam>
/// <param name="document">文档对象</param>
/// <param name="index">索引名称</param>
/// <returns></returns>
Task<IndexResponse> IndexAsync<TDocument>(TDocument document, IndexName index, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// 获取文档
/// </summary>
/// <typeparam name="TDocument">文档对象</typeparam>
/// <param name="id">文档id</param>
/// <param name="action"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<GetResponse<TDocument>> GetAsync<TDocument>(Id id, Action<GetRequestDescriptor<TDocument>> action, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// 搜索文档
/// </summary>
/// <typeparam name="TDocument"></typeparam>
/// <param name="action"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<SearchResponse<TDocument>> SearchAsync<TDocument>(Action<SearchRequestDescriptor<TDocument>> action, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// 更新索引
/// </summary>
/// <typeparam name="TDocument"></typeparam>
/// <typeparam name="TPartialDocument"></typeparam>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<UpdateResponse<TDocument>> UpdateAsync<TDocument, TPartialDocument>(IndexName index, Id id, Action<UpdateRequestDescriptor<TDocument, TPartialDocument>> configureRequest, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// 删除索引
/// </summary>
/// <param name="index"></param>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<DeleteResponse> DeleteAsync<TDocument>(IndexName index, Id id, CancellationToken cancellationToken = default(CancellationToken));
}
}
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace ESHelper
{
public class ESCoustorm : IESCoustorm
{
private ElasticsearchClient client;
private ILogger<ESCoustorm> logger;
public ESCoustorm(IOptions<ESOptions> options, ILogger<ESCoustorm> logger)
{
if (options.Value == null)
{
logger.LogError("es参数为nul");
throw new ArgumentNullException(nameof(IOptions<ESOptions>));
}
ElasticsearchClientSettings settings = null;
string[] conArray = options.Value.ConnectionIPs;
int length = conArray.Length;
//单节点
if (length == 1)
{
settings = new ElasticsearchClientSettings(new Uri(conArray[0]));
}
else
{
//集群
Uri[] nodes = options.Value.ConnectionIPs.Select(item => new Uri(item)).ToArray();
var pool = new StaticNodePool(nodes);
settings = new ElasticsearchClientSettings(pool);
}
client = new ElasticsearchClient(settings);
}
public async Task<DeleteResponse> DeleteAsync<TDocument>(IndexName index, Id id, CancellationToken cancellationToken = default(CancellationToken))
{
return await client.DeleteAsync(index, id, cancellationToken);
}
public async Task<GetResponse<TDocument>> GetAsync<TDocument>(Id id, Action<GetRequestDescriptor<TDocument>> action, CancellationToken cancellationToken = default)
{
return await client.GetAsync(id, action, cancellationToken);
}
public async Task<IndexResponse> IndexAsync<TDocument>(TDocument document, IndexName index, CancellationToken cancellationToken = default)
{
return await client.IndexAsync(document, index, cancellationToken);
}
public async Task<SearchResponse<TDocument>> SearchAsync<TDocument>(Action<SearchRequestDescriptor<TDocument>> action, CancellationToken cancellationToken = default)
{
return await client.SearchAsync(action, cancellationToken);
}
public async Task<UpdateResponse<TDocument>> UpdateAsync<TDocument, TPartialDocument>(IndexName index, Id id, Action<UpdateRequestDescriptor<TDocument, TPartialDocument>> configureRequest, CancellationToken cancellationToken = default(CancellationToken))
{
return await client.UpdateAsync<TDocument, TPartialDocument>(index, id, configureRequest);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ESHelper
{
public class ESOptions
{
/// <summary>
///集群 连接ip http://192.168.0.168:9200 http://192.168.0.168:9201 http://192.168.0.168:9202
///如果配置一个就是单节点
/// </summary>
public string[] ConnectionIPs { get; set; }
}
}
ElasticsearchWebAPI 代码
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ESOptions": {
"ConnectionIPs": [ "http://192.168.0.168:9200", "http://192.168.0.168:9201", "http://192.168.0.168:9202" ]
}
}
using ESHelper;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "Elasticsearch",
Description = "Elasticsearch",
Version = "v1",
});
string xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Assembly.GetExecutingAssembly().GetName().Name + ".xml");
options.IncludeXmlComments(xmlPath, true);
});
//注入 es
builder.Services.AddESCoustorm(builder.Configuration);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json ", "v1");
});
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
using Elastic.Clients.Elasticsearch;
using ElasticsearchWebAPI.Models;
using ESHelper;
using Microsoft.AspNetCore.Mvc;
using Field = Elastic.Clients.Elasticsearch.Field;
using HighlightField = Elastic.Clients.Elasticsearch.Core.Search.HighlightField;
namespace ElasticsearchWebAPI.Controllers
{
/// <summary>
/// ESTest
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class ESTestController : ControllerBase
{
private readonly IESCoustorm esCoustorm;
private readonly ILogger<ESTestController> logger;
public ESTestController(IESCoustorm esCoustorm, ILogger<ESTestController> logger)
{
this.esCoustorm = esCoustorm;
this.logger = logger;
}
/// <summary>
/// 获取索引 通过索引id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}")]
public async Task<ActionResult> GetAsync([FromRoute] string id)
{
var response = await this.esCoustorm.GetAsync<Books>(id, action => action.Index(nameof(Books).ToLower()));
if (response.IsValidResponse)
{
return Ok(response.Source);
}
return NotFound($"没有查询到索引{nameof(Books).ToLower()}");
}
/// <summary>
/// 查询
/// </summary>
/// <param name="searchOption"></param>
/// <returns></returns>
[HttpGet("Search")]
public async Task<ActionResult> SearchAsync([FromQuery] int currentIndex, [FromQuery] int pageSize, [FromQuery] string search)
{
int index = (currentIndex - 1) * pageSize;
int size = pageSize;
string content = search;
SearchResponse<Books> response = await this.esCoustorm.SearchAsync<Books>(action =>
{
string[] array = { "all" };
Field field = new Field("all");
action.Index(nameof(Books).ToLower())
.From(index)
.Size(size)
.Query(q => q.Match(m => m.Field(f => f.All).Query(content)))
.Highlight(config =>
config
.Fields(f =>
{
f.Add(field, new HighlightField()
{
MatchedFields = array,
PostTags = new string[] { "<span color='red'>" },
PreTags = new string[] { "</span>" }
});
return f;
}))
.Sort(sort =>
{
sort.Field(s => s.Id).Doc(s =>
{
s.Order(SortOrder.Asc);
});
});
});
if (response.IsValidResponse)
{
List<Books?> list = response.Hits.Select(item => item.Source).ToList();
//Books books = response.Hits.FirstOrDefault().Source;
//var hit = response.Hits.FirstOrDefault().Highlight;
return Ok(list);
}
return NotFound();
}
/// <summary>
/// 更新
/// </summary>
/// <param name="id"></param>
/// <param name="book"></param>
/// <returns></returns>
[HttpPut("{id}")]
public async Task<ActionResult> UpdateAsync([FromRoute] string id, [FromBody] Books book)
{
book.All = $"{book.Id} {book.Name} {book.Author} {book.PublicationTime}";
UpdateResponse<Books> response = await this.esCoustorm.UpdateAsync<Books, Books>(nameof(Books).ToLower(), id, u => u.Doc(book));
if (response.IsValidResponse)
{
return Ok("Update document succeeded.");
}
return NotFound();
}
/// <summary>
/// 新增索引
/// </summary>
/// <param name="book"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> IndexAsync([FromBody] Books book)
{
book.All = $"{book.Id} {book.Name} {book.Author} {book.PublicationTime}";
IndexResponse response = await esCoustorm.IndexAsync<Books>(book, nameof(Books).ToLower());
if (response.IsValidResponse)
{
return Ok($"Index document with ID {response.Id} succeeded.");
}
return BadRequest();
}
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteAsync([FromRoute] string id)
{
var response = await esCoustorm.DeleteAsync<Books>(nameof(Books).ToLower(), id);
if (response.IsValidResponse)
{
return Ok("Delete document succeeded.");
}
return NotFound();
}
}
}
namespace ElasticsearchWebAPI.Models
{
public class Books
{
public int Id { get; set; }
/// <summary>
/// 书名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 作者
/// </summary>
public string Author { get; set; }
/// <summary>
/// 出版时间
/// </summary>
public string PublicationTime{ get; set; }
/// <summary>
/// 查询所有字段内容
/// </summary>
public string All { get; set; }
}
}

测试结果

总结 这么创建 索引 是自动映射的
手动映射 应该
PUT /books
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"id":{"type": "text"},
"name":{"type": "text"},
"author":{"type": "text"},
"publicationTime":{"type": "text"},
"all":{"type":"text"}
}
}
}
3个 分片1个副本 =6个 分片 ,因为集群3个节点 也可以 设置 2个副本 提前 预设 水平扩展
浙公网安备 33010602011771号