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项目,目录结构如下
1665108538408.png

安装 .NET 客户端

  1. Install-Package Elastic.Clients.Elasticsearch -IncludePrerelease

公共部分

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft.AspNetCore": "Warning"
  6. }
  7. },
  8. "AllowedHosts": "*",
  9. "AppSetting": {
  10. "SqlServerSetting": {
  11. "Connection": "Server=192.168.3.40;Database=webdemo;User=sa;Password=longfuchu;"
  12. },
  13. "MysqlSetting": {
  14. "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"
  15. },
  16. "PostgreSqlSetting": {
  17. "Connection": "HOST=192.168.3.40;PORT=5432;DATABASE=test_db;USER ID=postgres;PASSWORD=123456;Pooling = false"
  18. },
  19. "RedisSetting": {
  20. "Connection": "192.168.3.40:6379,password=",
  21. "Database": "test"
  22. },
  23. "RabbitMQSetting": {
  24. "HostName": "localhost",
  25. "UserName": "admin",
  26. "Password": "admin",
  27. "Port": 5672
  28. },
  29. "MongoDbSetting": {
  30. "Connection": "mongodb://192.168.3.40:2717", ///?replicaSet=rs0
  31. "Database": "test"
  32. },
  33. "KafkaSetting": {
  34. "Servers": "kafka1:9091,kafka2:9092,kafka3:9093"
  35. },
  36. "ElasticSetting": {
  37. "Uri": "http://192.168.3.40:9200",
  38. "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",
  39. "CloudId": "",
  40. "ApiKey": "ZpNle_iRTSmSzk7iAln0DQ",
  41. "UserName": "elastic",
  42. "Password": "F3SXEhBp3Jysfd9OyhJX",
  43. "IndexSetting": {
  44. "TweetIndex": "my-tweet-index",
  45. "BOutIndex": "mssql_blood_boutitem"
  46. }
  47. }
  48. }
  49. }
  1. namespace ES.NET.Client.Demo.Models;
  2. /// <summary>
  3. /// mongodb setting
  4. /// </summary>
  5. public class AppSetting
  6. {
  7. /// <summary>
  8. /// SqlServerSetting
  9. /// </summary>
  10. public SqlServerSetting SqlServerSetting { get; set; }
  11. /// <summary>
  12. /// MysqlSetting
  13. /// </summary>
  14. public MysqlSetting MysqlSetting { get; set; }
  15. /// <summary>
  16. /// RedisSetting
  17. /// </summary>
  18. public RedisSetting RedisSetting { get; set; }
  19. /// <summary>
  20. /// RabbitMQSetting
  21. /// </summary>
  22. public RabbitMQSetting RabbitMQSetting { get; set; }
  23. /// <summary>
  24. /// MongoDbSetting
  25. /// </summary>
  26. public MongoDbSetting MongoDbSetting { get; set; }
  27. /// <summary>
  28. /// KafkaSetting
  29. /// </summary>
  30. public KafkaSetting KafkaSetting { get; set; }
  31. /// <summary>
  32. /// ElasticSetting
  33. /// </summary>
  34. public ElasticSetting ElasticSetting { get; set; }
  35. }
  36. public class RabbitMQSetting
  37. {
  38. /// <summary>
  39. /// HostName
  40. /// </summary>
  41. public string HostName { get; set; } = "localhost";
  42. /// <summary>
  43. /// Password
  44. /// </summary>
  45. public string Password { get; set; }
  46. /// <summary>
  47. /// Username
  48. /// </summary>
  49. public string UserName { get; set; }
  50. /// <summary>
  51. /// The port to connect on.
  52. /// </summary>
  53. public int Port { get; set; } = 5672;
  54. }
  55. public class RedisSetting
  56. {
  57. /// <summary>
  58. /// connection string
  59. /// </summary>
  60. public string Connection { get; set; }
  61. /// <summary>
  62. /// database name
  63. /// </summary>
  64. public string Database { get; set; }
  65. }
  66. public class MongoDbSetting
  67. {
  68. /// <summary>
  69. /// connection string
  70. /// </summary>
  71. public string Connection { get; set; }
  72. /// <summary>
  73. /// database name
  74. /// </summary>
  75. public string Database { get; set; }
  76. }
  77. public class SqlServerSetting
  78. {
  79. /// <summary>
  80. /// connection string
  81. /// </summary>
  82. public string Connection { get; set; }
  83. }
  84. public class MysqlSetting
  85. {
  86. /// <summary>
  87. /// connection string
  88. /// </summary>
  89. public string Connection { get; set; }
  90. }
  91. public class KafkaSetting
  92. {
  93. /// <summary>
  94. /// Servers
  95. /// </summary>
  96. public string Servers { get; set; }
  97. }
  98. public class ElasticSetting
  99. {
  100. /// <summary>
  101. /// Uri
  102. /// </summary>
  103. public string Uri { get; set; }
  104. public string Fingerprint { get; set; }
  105. public string CloudId { get; set; }
  106. public string ApiKey { get; set; }
  107. public string UserName { get; set; }
  108. public string Password { get; set; }
  109. public IndexSetting IndexSetting { get; set; }
  110. }
  111. public class IndexSetting {
  112. public string TweetIndex { get; set; }
  113. public string BOutIndex { get; set; }
  114. }
  1. using Microsoft.AspNetCore.Mvc;
  2. namespace ES.NET.Client.Demo.Controllers;
  3. /// <summary>
  4. /// 基础控制器
  5. /// </summary>
  6. [Route("api/[area]/[controller]/[action]")]
  7. [ApiController]
  8. public abstract class BaseController : ControllerBase
  9. {
  10. }
  1. using ES.NET.Client.Demo.Controllers;
  2. using Microsoft.AspNetCore.Mvc;
  3. namespace ES.NET.Client.Demo.Controllers.ES;
  4. /// <summary>
  5. /// 域控制器
  6. /// </summary>
  7. [Area("ES")]
  8. public abstract class AreaController : BaseController
  9. {
  10. }
  1. using ES.NET.Client.Demo.Models;
  2. using ES.NET.Client.Demo.Services;
  3. using System;
  4. var builder = WebApplication.CreateBuilder(args);
  5. // Add services to the container.
  6. builder.Services.AddControllers();
  7. builder.Services.AddEndpointsApiExplorer();
  8. builder.Services.AddSwaggerGen();
  9. var appSetting = new AppSetting();
  10. builder.Configuration.GetSection("AppSetting").Bind(appSetting);
  11. //把AppSetting实体注入到容器,方便在构造函数里使用IOptionsSnapshot<AppSetting> options
  12. builder.Services.Configure<AppSetting>(builder.Configuration.GetSection("AppSetting"));
  13. //[FromServices] ESClientConnHelp esClientConnHelp
  14. builder.Services.AddScoped<IESClientConnHelp,ESClientConnHelp>();
  15. var app = builder.Build();
  16. // Configure the HTTP request pipeline.
  17. if (app.Environment.IsDevelopment())
  18. {
  19. app.UseSwagger();
  20. app.UseSwaggerUI();
  21. }
  22. app.UseHttpsRedirection();
  23. app.UseAuthorization();
  24. app.MapControllers();
  25. app.Run();

初始化ESClient

  1. using Elastic.Clients.Elasticsearch;
  2. namespace ES.NET.Client.Demo.Services;
  3. public abstract class IElasticSearchHelper
  4. {
  5. public ElasticsearchClient ESClient { get; set; }
  6. }
  1. using Elastic.Clients.Elasticsearch;
  2. using Elastic.Transport;
  3. using ES.NET.Client.Demo.Models;
  4. using Microsoft.Extensions.Options;
  5. namespace ES.NET.Client.Demo.Services;
  6. public class ElasticSearchHelper: IElasticSearchHelper
  7. {
  8. //ES连接
  9. /// <summary>
  10. /// ElasticSearchConn
  11. /// builder.Services.Configure<AppSetting>(builder.Configuration.GetSection("AppSetting"));
  12. /// </summary>
  13. /// <param name="options"></param>
  14. public ElasticSearchHelper(IOptionsSnapshot<AppSetting> options)
  15. {
  16. if (options == null || options.Value == null)
  17. throw new ArgumentNullException(nameof(options));
  18. if (this.ESClient == null)
  19. {
  20. ElasticSetting elasticSetting = options.Value.ElasticSetting;
  21. var settings = new ElasticsearchClientSettings(new Uri(elasticSetting.Uri))
  22. .CertificateFingerprint(elasticSetting.Fingerprint)
  23. .Authentication(new BasicAuthentication(elasticSetting.UserName, elasticSetting.Password));
  24. //.Authentication(new ApiKey(elasticSetting.ApiKey));
  25. this.ESClient = new ElasticsearchClient(settings);
  26. }
  27. }
  28. }

简单示例

新建一个实体Tweet和控制器TweetController

  1. namespace ES.NET.Client.Demo.Models;
  2. public class Tweet
  3. {
  4. public int Id { get; set; }
  5. public string User { get; set; }
  6. public DateTime PostDate { get; set; }
  7. public string Message { get; set; }
  8. }

创建索引

  1. /// <summary>
  2. /// 创建索引
  3. /// </summary>
  4. /// <param name="tweet"></param>
  5. /// <returns></returns>
  6. [HttpPut]
  7. public async Task<IActionResult> IndexAsync(Tweet entity)
  8. {
  9. if (entity == null || entity.Id <= 0) return NoContent();
  10. var response = await esClient.IndexAsync(entity, request => request.Index(elasticSetting.IndexSetting.TweetIndex));
  11. if (response.IsValid)
  12. {
  13. Console.WriteLine($"Index document with ID {response.Id} succeeded.");
  14. }
  15. return Ok(response);
  16. }

获取文档

  1. /// <summary>
  2. /// 获取文档
  3. /// </summary>
  4. /// <param name="id"></param>
  5. /// <returns></returns>
  6. [HttpGet]
  7. public async Task<IActionResult> GetAsync(string id)
  8. {
  9. var response = await esClient.GetAsync<Tweet>(id, idx => idx.Index(elasticSetting.IndexSetting.TweetIndex));
  10. var entity = response.Source;
  11. return Ok(entity);
  12. }

使用弹性搜索 JSON 响应进行 1 对 1 映射。GetResponse 原始文档被反序列化为 Tweet 类的实例,可通过属性在响应上访问。Source

搜索文档

  1. /// <summary>
  2. /// 搜索文档(lambda方式)
  3. /// </summary>
  4. /// <param name="userName"></param>
  5. /// <returns></returns>
  6. [HttpGet]
  7. public async Task<IActionResult> SearchAsync(string userName)
  8. {
  9. var response = await esClient.SearchAsync<Tweet>(s => s
  10. .Index(elasticSetting.IndexSetting.TweetIndex)
  11. .From(0)
  12. .Size(10)
  13. .Query(q => q
  14. .Term(t => t.User, userName)
  15. )
  16. );
  17. if (response.IsValid)
  18. {
  19. var tweet = response.Documents.FirstOrDefault();
  20. return Ok(tweet);
  21. }
  22. else
  23. {
  24. return NotFound();
  25. }
  26. }

泛型类型参数指定类,该类在反序列化响应中的命中时使用。Tweet 如果在 上配置了 索引,或者在映射此类型时配置了特定索引,则可以省略该索引。DefaultIndexElasticsearchClientSettings 针对该字段执行术语查询,搜索由用户 stevejgordon 撰写的推文。user 可通过 上的 collection 属性访问与查询匹配的文档。DocumentsSearchResponse

如果 lambda 不是您的对象,您可能更喜欢对请求使用对象初始值设定项语法

  1. /// <summary>
  2. /// 搜索文档(实体对象方式)
  3. /// </summary>
  4. /// <param name="userName"></param>
  5. /// <returns></returns>
  6. [HttpGet]
  7. public async Task<IActionResult> SearchRequestAsync(string userName)
  8. {
  9. var request = new SearchRequest(elasticSetting.IndexSetting.TweetIndex)
  10. {
  11. From = 0,
  12. Size = 10,
  13. Query = new TermQuery("user") { Value = userName }
  14. };
  15. var response = await esClient.SearchAsync<Tweet>(request);
  16. if (response.IsValid)
  17. {
  18. var entity = response.Documents.FirstOrDefault();
  19. return Ok(entity);
  20. }
  21. else
  22. {
  23. return NotFound();
  24. }
  25. }

更新文档

  1. /// <summary>
  2. /// 更新文档
  3. /// </summary>
  4. /// <param name="entity"></param>
  5. /// <returns></returns>
  6. [HttpPost]
  7. public async Task<IActionResult> UpdateAsync(Tweet entity)
  8. {
  9. if (entity == null || entity.Id <= 0) return NoContent();
  10. var response = await esClient.UpdateAsync<Tweet, object>(elasticSetting.IndexSetting.TweetIndex, entity.Id.ToString(), u => u
  11. .Doc(entity));
  12. if (response.IsValid)
  13. {
  14. Console.WriteLine("Update document succeeded.");
  15. }
  16. return Ok(response);
  17. }

更新现有推文实例上的属性。
在更新请求中发送更新的推文对象。

删除文档

可以通过提供要删除的文档的 ID 来删除文档。

  1. /// <summary>
  2. /// 删除文档
  3. /// </summary>
  4. /// <param name="id"></param>
  5. /// <returns></returns>
  6. [HttpPost]
  7. public async Task<IActionResult> DeleteAsync(string id)
  8. {
  9. if (string.IsNullOrWhiteSpace(id)) return NoContent();
  10. var response = await esClient.DeleteAsync(elasticSetting.IndexSetting.TweetIndex, id);
  11. if (response.IsValid)
  12. {
  13. Console.WriteLine("Delete document succeeded.");
  14. }
  15. return Ok(response);
  16. }

完整TweetController

  1. using Elastic.Clients.Elasticsearch;
  2. using Elastic.Clients.Elasticsearch.QueryDsl;
  3. using ES.NET.Client.Demo.Models;
  4. using ES.NET.Client.Demo.Services;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Microsoft.Extensions.Options;
  7. using System;
  8. namespace ES.NET.Client.Demo.Controllers.ES;
  9. /// <summary>
  10. /// 测试索引控制器
  11. /// </summary>
  12. public class TweetController : AreaController
  13. {
  14. private readonly ElasticSetting elasticSetting;
  15. private readonly ElasticsearchClient esClient;
  16. public TweetController(IOptionsSnapshot<AppSetting> options,IElasticSearchHelper esClientConnHelp)
  17. {
  18. if (options == null || options.Value == null)
  19. throw new ArgumentNullException(nameof(options));
  20. if(esClientConnHelp.ESClient==null)
  21. throw new ArgumentNullException(nameof(esClientConnHelp));
  22. elasticSetting = options.Value.ElasticSetting;
  23. esClient = esClientConnHelp.ESClient;
  24. }
  25. /// <summary>
  26. /// 创建索引
  27. /// </summary>
  28. /// <param name="tweet"></param>
  29. /// <returns></returns>
  30. [HttpPut]
  31. public async Task<IActionResult> IndexAsync(Tweet entity)
  32. {
  33. if (entity == null || entity.Id <= 0) return NoContent();
  34. var response = await esClient.IndexAsync(entity, request => request.Index(elasticSetting.IndexSetting.TweetIndex));
  35. if (response.IsValid)
  36. {
  37. Console.WriteLine($"Index document with ID {response.Id} succeeded.");
  38. }
  39. return Ok(response);
  40. }
  41. /// <summary>
  42. /// 获取文档
  43. /// </summary>
  44. /// <param name="id"></param>
  45. /// <returns></returns>
  46. [HttpGet]
  47. public async Task<IActionResult> GetAsync(string id)
  48. {
  49. var response = await esClient.GetAsync<Tweet>(id, idx => idx.Index(elasticSetting.IndexSetting.TweetIndex));
  50. var entity = response.Source;
  51. return Ok(entity);
  52. }
  53. /// <summary>
  54. /// 搜索文档(lambda方式)
  55. /// </summary>
  56. /// <param name="userName"></param>
  57. /// <returns></returns>
  58. [HttpGet]
  59. public async Task<IActionResult> SearchAsync(string userName)
  60. {
  61. var response = await esClient.SearchAsync<Tweet>(s => s
  62. .Index(elasticSetting.IndexSetting.TweetIndex)
  63. .From(0)
  64. .Size(10)
  65. .Query(q => q
  66. .Term(t => t.User, userName)
  67. )
  68. );
  69. if (response.IsValid)
  70. {
  71. var tweet = response.Documents.FirstOrDefault();
  72. return Ok(tweet);
  73. }
  74. else
  75. {
  76. return NotFound();
  77. }
  78. }
  79. /// <summary>
  80. /// 搜索文档(实体对象方式)
  81. /// </summary>
  82. /// <param name="userName"></param>
  83. /// <returns></returns>
  84. [HttpGet]
  85. public async Task<IActionResult> SearchRequestAsync(string userName)
  86. {
  87. var request = new SearchRequest(elasticSetting.IndexSetting.TweetIndex)
  88. {
  89. From = 0,
  90. Size = 10,
  91. Query = new TermQuery("user") { Value = userName }
  92. };
  93. var response = await esClient.SearchAsync<Tweet>(request);
  94. if (response.IsValid)
  95. {
  96. var entity = response.Documents.FirstOrDefault();
  97. return Ok(entity);
  98. }
  99. else
  100. {
  101. return NotFound();
  102. }
  103. }
  104. /// <summary>
  105. /// 更新文档
  106. /// </summary>
  107. /// <param name="entity"></param>
  108. /// <returns></returns>
  109. [HttpPost]
  110. public async Task<IActionResult> UpdateAsync(Tweet entity)
  111. {
  112. if (entity == null || entity.Id <= 0) return NoContent();
  113. var response = await esClient.UpdateAsync<Tweet, object>(elasticSetting.IndexSetting.TweetIndex, entity.Id.ToString(), u => u
  114. .Doc(entity));
  115. if (response.IsValid)
  116. {
  117. Console.WriteLine("Update document succeeded.");
  118. }
  119. return Ok(response);
  120. }
  121. /// <summary>
  122. /// 删除文档
  123. /// </summary>
  124. /// <param name="id"></param>
  125. /// <returns></returns>
  126. [HttpPost]
  127. public async Task<IActionResult> DeleteAsync(string id)
  128. {
  129. if (string.IsNullOrWhiteSpace(id)) return NoContent();
  130. var response = await esClient.DeleteAsync(elasticSetting.IndexSetting.TweetIndex, id);
  131. if (response.IsValid)
  132. {
  133. Console.WriteLine("Delete document succeeded.");
  134. }
  135. return Ok(response);
  136. }
  137. }