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个副本 提前 预设 水平扩展

 

posted on 2023-11-30 16:51  是水饺不是水饺  阅读(508)  评论(0)    收藏  举报

导航