Github: https://github.com/elastic/elasticsearch-net
官方文档: https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html
其他: Elasticsearch .net client NEST 5.x 使用总结
示例说明
本示例了解如何使用 .NET 客户端执行各种基本的Elasticsearch CRUD 操作。演示了如何通过将对象索引到 Elasticsearch、读回文档、通过 ID 检索文档或执行搜索、更新文档中的某个字段以及删除特定文档来创建文档。
创建项目
创建一个名为ES.NET.Client.Demo的Web API项目,目录结构如下
安装 .NET 客户端
Install-Package Elastic.Clients.Elasticsearch -IncludePrerelease
公共部分
{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHosts": "*","AppSetting": {"SqlServerSetting": {"Connection": "Server=192.168.3.40;Database=webdemo;User=sa;Password=longfuchu;"},"MysqlSetting": {"Connection": "server=192.168.3.40;port=3306;user=root;password=123456;database=webdemo;charset=utf8;Allow Zero Datetime=true;sslmode=none;Old Guids=true;Allow User Variables=True"},"PostgreSqlSetting": {"Connection": "HOST=192.168.3.40;PORT=5432;DATABASE=test_db;USER ID=postgres;PASSWORD=123456;Pooling = false"},"RedisSetting": {"Connection": "192.168.3.40:6379,password=","Database": "test"},"RabbitMQSetting": {"HostName": "localhost","UserName": "admin","Password": "admin","Port": 5672},"MongoDbSetting": {"Connection": "mongodb://192.168.3.40:2717", ///?replicaSet=rs0"Database": "test"},"KafkaSetting": {"Servers": "kafka1:9091,kafka2:9092,kafka3:9093"},"ElasticSetting": {"Uri": "http://192.168.3.40:9200","Fingerprint": "14:0C:36:4D:47:5F:23:23:FF:30:FD:92:9D:25:C2:1B:F5:56:61:73:56:C5:5F:45:F9:B8:E7:D5:40:BB:65:59","CloudId": "","ApiKey": "ZpNle_iRTSmSzk7iAln0DQ","UserName": "elastic","Password": "F3SXEhBp3Jysfd9OyhJX","IndexSetting": {"TweetIndex": "my-tweet-index","BOutIndex": "mssql_blood_boutitem"}}}}
namespace ES.NET.Client.Demo.Models;/// <summary>/// mongodb setting/// </summary>public class AppSetting{/// <summary>/// SqlServerSetting/// </summary>public SqlServerSetting SqlServerSetting { get; set; }/// <summary>/// MysqlSetting/// </summary>public MysqlSetting MysqlSetting { get; set; }/// <summary>/// RedisSetting/// </summary>public RedisSetting RedisSetting { get; set; }/// <summary>/// RabbitMQSetting/// </summary>public RabbitMQSetting RabbitMQSetting { get; set; }/// <summary>/// MongoDbSetting/// </summary>public MongoDbSetting MongoDbSetting { get; set; }/// <summary>/// KafkaSetting/// </summary>public KafkaSetting KafkaSetting { get; set; }/// <summary>/// ElasticSetting/// </summary>public ElasticSetting ElasticSetting { get; set; }}public class RabbitMQSetting{/// <summary>/// HostName/// </summary>public string HostName { get; set; } = "localhost";/// <summary>/// Password/// </summary>public string Password { get; set; }/// <summary>/// Username/// </summary>public string UserName { get; set; }/// <summary>/// The port to connect on./// </summary>public int Port { get; set; } = 5672;}public class RedisSetting{/// <summary>/// connection string/// </summary>public string Connection { get; set; }/// <summary>/// database name/// </summary>public string Database { get; set; }}public class MongoDbSetting{/// <summary>/// connection string/// </summary>public string Connection { get; set; }/// <summary>/// database name/// </summary>public string Database { get; set; }}public class SqlServerSetting{/// <summary>/// connection string/// </summary>public string Connection { get; set; }}public class MysqlSetting{/// <summary>/// connection string/// </summary>public string Connection { get; set; }}public class KafkaSetting{/// <summary>/// Servers/// </summary>public string Servers { get; set; }}public class ElasticSetting{/// <summary>/// Uri/// </summary>public string Uri { get; set; }public string Fingerprint { get; set; }public string CloudId { get; set; }public string ApiKey { get; set; }public string UserName { get; set; }public string Password { get; set; }public IndexSetting IndexSetting { get; set; }}public class IndexSetting {public string TweetIndex { get; set; }public string BOutIndex { get; set; }}
using Microsoft.AspNetCore.Mvc;namespace ES.NET.Client.Demo.Controllers;/// <summary>/// 基础控制器/// </summary>[Route("api/[area]/[controller]/[action]")][ApiController]public abstract class BaseController : ControllerBase{}
using ES.NET.Client.Demo.Controllers;using Microsoft.AspNetCore.Mvc;namespace ES.NET.Client.Demo.Controllers.ES;/// <summary>/// 域控制器/// </summary>[Area("ES")]public abstract class AreaController : BaseController{}
using ES.NET.Client.Demo.Models;using ES.NET.Client.Demo.Services;using System;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();var appSetting = new AppSetting();builder.Configuration.GetSection("AppSetting").Bind(appSetting);//把AppSetting实体注入到容器,方便在构造函数里使用IOptionsSnapshot<AppSetting> optionsbuilder.Services.Configure<AppSetting>(builder.Configuration.GetSection("AppSetting"));//[FromServices] ESClientConnHelp esClientConnHelpbuilder.Services.AddScoped<IESClientConnHelp,ESClientConnHelp>();var app = builder.Build();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();
初始化ESClient
using Elastic.Clients.Elasticsearch;namespace ES.NET.Client.Demo.Services;public abstract class IElasticSearchHelper{public ElasticsearchClient ESClient { get; set; }}
using Elastic.Clients.Elasticsearch;using Elastic.Transport;using ES.NET.Client.Demo.Models;using Microsoft.Extensions.Options;namespace ES.NET.Client.Demo.Services;public class ElasticSearchHelper: IElasticSearchHelper{//ES连接/// <summary>/// ElasticSearchConn/// builder.Services.Configure<AppSetting>(builder.Configuration.GetSection("AppSetting"));/// </summary>/// <param name="options"></param>public ElasticSearchHelper(IOptionsSnapshot<AppSetting> options){if (options == null || options.Value == null)throw new ArgumentNullException(nameof(options));if (this.ESClient == null){ElasticSetting elasticSetting = options.Value.ElasticSetting;var settings = new ElasticsearchClientSettings(new Uri(elasticSetting.Uri)).CertificateFingerprint(elasticSetting.Fingerprint).Authentication(new BasicAuthentication(elasticSetting.UserName, elasticSetting.Password));//.Authentication(new ApiKey(elasticSetting.ApiKey));this.ESClient = new ElasticsearchClient(settings);}}}
简单示例
新建一个实体Tweet和控制器TweetController
namespace ES.NET.Client.Demo.Models;public class Tweet{public int Id { get; set; }public string User { get; set; }public DateTime PostDate { get; set; }public string Message { get; set; }}
创建索引
/// <summary>/// 创建索引/// </summary>/// <param name="tweet"></param>/// <returns></returns>[HttpPut]public async Task<IActionResult> IndexAsync(Tweet entity){if (entity == null || entity.Id <= 0) return NoContent();var response = await esClient.IndexAsync(entity, request => request.Index(elasticSetting.IndexSetting.TweetIndex));if (response.IsValid){Console.WriteLine($"Index document with ID {response.Id} succeeded.");}return Ok(response);}
获取文档
/// <summary>/// 获取文档/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> GetAsync(string id){var response = await esClient.GetAsync<Tweet>(id, idx => idx.Index(elasticSetting.IndexSetting.TweetIndex));var entity = response.Source;return Ok(entity);}
使用弹性搜索 JSON 响应进行 1 对 1 映射。GetResponse 原始文档被反序列化为 Tweet 类的实例,可通过属性在响应上访问。Source
搜索文档
/// <summary>/// 搜索文档(lambda方式)/// </summary>/// <param name="userName"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> SearchAsync(string userName){var response = await esClient.SearchAsync<Tweet>(s => s.Index(elasticSetting.IndexSetting.TweetIndex).From(0).Size(10).Query(q => q.Term(t => t.User, userName)));if (response.IsValid){var tweet = response.Documents.FirstOrDefault();return Ok(tweet);}else{return NotFound();}}
泛型类型参数指定类,该类在反序列化响应中的命中时使用。Tweet 如果在 上配置了 索引,或者在映射此类型时配置了特定索引,则可以省略该索引。DefaultIndexElasticsearchClientSettings 针对该字段执行术语查询,搜索由用户 stevejgordon 撰写的推文。user 可通过 上的 collection 属性访问与查询匹配的文档。DocumentsSearchResponse
如果 lambda 不是您的对象,您可能更喜欢对请求使用对象初始值设定项语法
/// <summary>/// 搜索文档(实体对象方式)/// </summary>/// <param name="userName"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> SearchRequestAsync(string userName){var request = new SearchRequest(elasticSetting.IndexSetting.TweetIndex){From = 0,Size = 10,Query = new TermQuery("user") { Value = userName }};var response = await esClient.SearchAsync<Tweet>(request);if (response.IsValid){var entity = response.Documents.FirstOrDefault();return Ok(entity);}else{return NotFound();}}
更新文档
/// <summary>/// 更新文档/// </summary>/// <param name="entity"></param>/// <returns></returns>[HttpPost]public async Task<IActionResult> UpdateAsync(Tweet entity){if (entity == null || entity.Id <= 0) return NoContent();var response = await esClient.UpdateAsync<Tweet, object>(elasticSetting.IndexSetting.TweetIndex, entity.Id.ToString(), u => u.Doc(entity));if (response.IsValid){Console.WriteLine("Update document succeeded.");}return Ok(response);}
更新现有推文实例上的属性。
在更新请求中发送更新的推文对象。
删除文档
可以通过提供要删除的文档的 ID 来删除文档。
/// <summary>/// 删除文档/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpPost]public async Task<IActionResult> DeleteAsync(string id){if (string.IsNullOrWhiteSpace(id)) return NoContent();var response = await esClient.DeleteAsync(elasticSetting.IndexSetting.TweetIndex, id);if (response.IsValid){Console.WriteLine("Delete document succeeded.");}return Ok(response);}
完整TweetController
using Elastic.Clients.Elasticsearch;using Elastic.Clients.Elasticsearch.QueryDsl;using ES.NET.Client.Demo.Models;using ES.NET.Client.Demo.Services;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;using System;namespace ES.NET.Client.Demo.Controllers.ES;/// <summary>/// 测试索引控制器/// </summary>public class TweetController : AreaController{private readonly ElasticSetting elasticSetting;private readonly ElasticsearchClient esClient;public TweetController(IOptionsSnapshot<AppSetting> options,IElasticSearchHelper esClientConnHelp){if (options == null || options.Value == null)throw new ArgumentNullException(nameof(options));if(esClientConnHelp.ESClient==null)throw new ArgumentNullException(nameof(esClientConnHelp));elasticSetting = options.Value.ElasticSetting;esClient = esClientConnHelp.ESClient;}/// <summary>/// 创建索引/// </summary>/// <param name="tweet"></param>/// <returns></returns>[HttpPut]public async Task<IActionResult> IndexAsync(Tweet entity){if (entity == null || entity.Id <= 0) return NoContent();var response = await esClient.IndexAsync(entity, request => request.Index(elasticSetting.IndexSetting.TweetIndex));if (response.IsValid){Console.WriteLine($"Index document with ID {response.Id} succeeded.");}return Ok(response);}/// <summary>/// 获取文档/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> GetAsync(string id){var response = await esClient.GetAsync<Tweet>(id, idx => idx.Index(elasticSetting.IndexSetting.TweetIndex));var entity = response.Source;return Ok(entity);}/// <summary>/// 搜索文档(lambda方式)/// </summary>/// <param name="userName"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> SearchAsync(string userName){var response = await esClient.SearchAsync<Tweet>(s => s.Index(elasticSetting.IndexSetting.TweetIndex).From(0).Size(10).Query(q => q.Term(t => t.User, userName)));if (response.IsValid){var tweet = response.Documents.FirstOrDefault();return Ok(tweet);}else{return NotFound();}}/// <summary>/// 搜索文档(实体对象方式)/// </summary>/// <param name="userName"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> SearchRequestAsync(string userName){var request = new SearchRequest(elasticSetting.IndexSetting.TweetIndex){From = 0,Size = 10,Query = new TermQuery("user") { Value = userName }};var response = await esClient.SearchAsync<Tweet>(request);if (response.IsValid){var entity = response.Documents.FirstOrDefault();return Ok(entity);}else{return NotFound();}}/// <summary>/// 更新文档/// </summary>/// <param name="entity"></param>/// <returns></returns>[HttpPost]public async Task<IActionResult> UpdateAsync(Tweet entity){if (entity == null || entity.Id <= 0) return NoContent();var response = await esClient.UpdateAsync<Tweet, object>(elasticSetting.IndexSetting.TweetIndex, entity.Id.ToString(), u => u.Doc(entity));if (response.IsValid){Console.WriteLine("Update document succeeded.");}return Ok(response);}/// <summary>/// 删除文档/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpPost]public async Task<IActionResult> DeleteAsync(string id){if (string.IsNullOrWhiteSpace(id)) return NoContent();var response = await esClient.DeleteAsync(elasticSetting.IndexSetting.TweetIndex, id);if (response.IsValid){Console.WriteLine("Delete document succeeded.");}return Ok(response);}}
