C# Elasticsearch帮助类

ElasticsearchConfig

 /// <summary>
    /// ES 连接配置
    /// </summary>
    public class ElasticsearchConfig
    {
        /// <summary>
        /// 节点列表
        /// </summary>
        public IEnumerable<ElasticsearchNode> Nodes { get; set; }

        /// <summary>
        /// 连接池类型
        /// </summary>
        public ElasticsearchConnectionPoolType PoolType { get; set; } = ElasticsearchConnectionPoolType.Static;

        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// 密码
        /// </summary>
        public string Password { get; set; }

        /// <summary>
        /// 显示调试信息
        /// </summary>
        public bool DisableDebugInfo { get; set; } = true;

        /// <summary>
        /// 抛出异常。默认false,错误信息在每个操作的response中
        /// </summary>
        public bool ThrowExceptions { get; set; } = false;

        /// <summary>
        /// 是否禁用Ping。禁用ping 第一次使用节点或使用被标记死亡的节点进行ping
        /// </summary>
        public bool DisablePing { get; set; } = true;
    }

  ElasticsearchConfigProvider

 /// <summary>
    /// Elasticsearch 配置提供程序
    /// </summary>
    public class ElasticsearchConfigProvider : IElasticsearchConfigProvider
    {
        /// <summary>
        /// 配置
        /// </summary>
        private readonly ElasticsearchConfig _config;

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchConfigProvider"/>类型的实例
        /// </summary>
        /// <param name="config">Elasticsearch 连接配置</param>
        public ElasticsearchConfigProvider(ElasticsearchConfig config)
        {
            _config = config;
        }

        /// <summary>
        /// 获取配置
        /// </summary>
        /// <returns></returns>
        public Task<ElasticsearchConfig> GetConfigAsync()
        {
            return Task.FromResult(_config);
        }
    }

  ElasticsearchConnectionPoolType

 /// <summary>
    /// ES 连接池类型。
    /// 支持ping-说明能够发现节点的状态;
    /// 支持嗅探-说明能够发现新的节点
    /// </summary>
    public enum ElasticsearchConnectionPoolType
    {
        /// <summary>
        /// 静态连接池。推荐使用,应用于已知集群,请求时随机请求各个正常节点,支持ping,不支持嗅探
        /// </summary>
        Static,
        /// <summary>
        /// 单节点连接池
        /// </summary>
        SingleNode,
        /// <summary>
        /// 嗅探连接池。可动态嗅探集群,随机请求,支持嗅探、ping
        /// </summary>
        Sniffing,
        /// <summary>
        /// 固定连接池。选择一个可用节点作为请求主节点,支持ping,不支持嗅探
        /// </summary>
        Sticky,
        /// <summary>
        /// 固定嗅探连接池。选择一个可用节点作为请求主节点,支持ping,支持嗅探
        /// </summary>
        StickySniffing
    }

  ElasticsearchNode

/// <summary>
    /// Elasticsearch 节点
    /// </summary>
    public class ElasticsearchNode
    {
        /// <summary>
        /// 主机
        /// </summary>
        public string Host { get; set; }

        /// <summary>
        /// 端口号
        /// </summary>
        public uint Port { get; set; }

        /// <summary>
        /// 输出字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            var port = Port == 0 ? "" : $":{Port}";

            var result = $"{Host}{port}".ToLowerInvariant();
            return result.IndexOf("http", StringComparison.OrdinalIgnoreCase) > -1 ? result : $"http://{result}";
        }
    }

  IElasticsearchConfigProvider

 public interface IElasticsearchConfigProvider
    {
        /// <summary>
        /// 获取配置
        /// </summary>
        /// <returns></returns>
        Task<ElasticsearchConfig> GetConfigAsync();
    }

  ElasticClientExtensions

/// <summary>
    /// ES客户端(<see cref="IElasticClient"/>) 扩展
    /// </summary>
    internal static class ElasticClientExtensions
    {
        /// <summary>
        /// 初始化索引映射
        /// </summary>
        /// <param name="client">ES客户端</param>
        /// <param name="indexName">索引名</param>
        public static async Task InitializeIndexMapAsync(this IElasticClient client, string indexName)
        {
            var newName = indexName + DateTime.Now.Ticks;
            var result = await client.CreateIndexAsync(newName,
                t => t.Index(newName).Settings(x => x.NumberOfShards(1).NumberOfReplicas(1).Setting("max_result_window", int.MaxValue)));

            if (result.Acknowledged)
            {
                await client.AliasAsync(x => x.Add(o => o.Index(newName).Alias(indexName)));
                return;
            }
            throw new ElasticsearchException($"创建索引 {indexName} 失败:{result.ServerError.Error.Reason}");
        }

        /// <summary>
        /// 初始化索引映射
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="client">ES客户端</param>
        /// <param name="indexName">索引名</param>
        public static async Task InitializeIndexMapAsync<T>(this IElasticClient client, string indexName) where T : class
        {
            var newName = indexName + DateTime.Now.Ticks;
            var result = await client.CreateIndexAsync(newName,
                t => t.Index(newName)
                .Settings(o => o.NumberOfShards(1).NumberOfReplicas(1)
                .Setting("max_result_window", int.MaxValue))
                .Mappings(m => m.Map<T>(mm => mm.AutoMap())));
            if (result.Acknowledged)
            {
                await client.AliasAsync(x => x.Add(o => o.Index(newName).Alias(indexName)));
                return;
            }
            throw new ElasticsearchException($"创建索引 {indexName} 失败:{result.ServerError.Error.Reason}");
        }

        /// <summary>
        /// 初始化索引映射
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="client">ES客户端</param>
        /// <param name="indexName">索引名</param>
        public static async Task InitializeIndexMapAsync<T>(this IElasticClient client, string indexName, int numberOfShards,
           int numberOfReplicas) where T : class
        {
            var newName = indexName + DateTime.Now.Ticks;
            var result = await client.CreateIndexAsync(newName,
                x => x.Index(newName)
                    .Settings(o =>
                        o.NumberOfShards(numberOfShards)
                            .NumberOfReplicas(numberOfReplicas)
                            .Setting("max_result_window", int.MaxValue))
                    .Mappings(m => m.Map<T>(mm => mm.AutoMap())));
            if (result.Acknowledged)
            {
                await client.AliasAsync(x => x.Add(o => o.Index(newName).Alias(indexName)));
                return;
            }
            throw new ElasticsearchException($"创建索引 {indexName} 失败:{result.ServerError.Error.Reason}");
        }

    }

  HighlightParam

 public class HighlightParam
    {
        /// <summary>
        /// 高亮字段
        /// </summary>
        public string[] Keys { get; set; }

        /// <summary>
        /// 高亮标签
        /// </summary>
        public string PreTags { get; set; } = "<em>";

        /// <summary>
        /// 高亮标签
        /// </summary>
        public string PostTags { get; set; } = "</em>";

        /// <summary>
        /// 高亮字段前缀。
        /// 例如:title 高亮值赋值给 h_title
        /// </summary>
        public string PrefixOfKey { get; set; } = string.Empty;

        /// <summary>
        /// 是否替换原来的值
        /// </summary>
        public bool ReplaceAuto { get; set; } = true;
    }

  IPageParam

 public interface IPageParam
    {
        /// <summary>
        /// 页数,即第几页,从1开始
        /// </summary>
        int Page { get; set; }

        /// <summary>
        /// 每页显示行数
        /// </summary>
        int PageSize { get; set; }

        /// <summary>
        /// 关键词
        /// </summary>
        string Keyword { get; set; }

        /// <summary>
        /// 获取跳过的行数
        /// </summary>
        /// <returns></returns>
        int GetSkipCount();

        /// <summary>
        /// 运算符
        /// </summary>
        Nest.Operator Operator { get; set; }

        /// <summary>
        /// 高亮参数
        /// </summary>
        HighlightParam Highlight { get; set; }
    }

    /// <summary>
    /// 分页参数
    /// </summary>
    public class PageParam : IPageParam
    {
        /// <summary>
        /// 页数,即第几页,从1开始
        /// </summary>
        public int Page { get; set; }

        /// <summary>
        /// 每页显示行数
        /// </summary>
        public int PageSize { get; set; }

        /// <summary>
        /// 关键词
        /// </summary>
        public string Keyword { get; set; }

        /// <summary>
        /// 获取跳过的行数
        /// </summary>
        /// <returns></returns>
        public int GetSkipCount() => (Page - 1) * PageSize;

        /// <summary>
        /// 运算符
        /// </summary>
        public Nest.Operator Operator { get; set; } = Nest.Operator.And;

        /// <summary>
        /// 高亮参数
        /// </summary>
        public HighlightParam Highlight { get; set; }
    }

    /// <summary>
    /// 指定字段查询
    /// </summary>
    public class PageParamWithSearch : PageParam
    {
        /// <summary>
        /// 查询字段列表
        /// </summary>
        public string[] SearchKeys { get; set; }
    }

  IQueryResult

/// <summary>
    /// 查询结果
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    public interface IQueryResult<T>
    {
        /// <summary>
        /// 总行数
        /// </summary>
        long TotalCount { get; set; }

        /// <summary>
        /// 查询占用时间
        /// </summary>
        long Took { get; set; }

        /// <summary>
        /// 数据
        /// </summary>
        IEnumerable<T> Data { get; }
    }

    /// <summary>
    /// 自定义查询结果
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    public class CustomQueryResult<T> : IQueryResult<T>
    {
        /// <summary>
        /// 总行数
        /// </summary>
        public long TotalCount { get; set; }

        /// <summary>
        /// 查询占用时间
        /// </summary>
        public long Took { get; set; }

        /// <summary>
        /// 数据
        /// </summary>
        public IEnumerable<T> Data { get; set; }
    }

  ElasticsearchClient

/// <summary>
    /// ES客户端
    /// </summary>
    public class ElasticsearchClient : IElasticsearchClient
    {
        /// <summary>
        /// ES客户端生成器
        /// </summary>
        private ElasticsearchClientBuilder _builder;

        /// <summary>
        /// 配置提供程序
        /// </summary>
        private IElasticsearchConfigProvider _configProvider;

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchClient"/>类型的实例
        /// </summary>
        /// <param name="configProvider">配置提供程序</param>
        public ElasticsearchClient(IElasticsearchConfigProvider configProvider)
        {
            _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider));
            _builder = new ElasticsearchClientBuilder(configProvider);
        }

        /// <summary>
        /// 是否存在指定索引
        /// </summary>
        /// <param name="indexName">索引名</param>
        /// <returns></returns>
        public async Task<bool> ExistsAsync(string indexName)
        {
            var client = await _builder.GetClientAsync();
            var result = await client.IndexExistsAsync(indexName);
            return result.Exists;
        }

        /// <summary>
        /// 添加索引。不映射
        /// </summary>
        /// <param name="indexName">索引名</param>
        public async Task AddAsync(string indexName)
        {
            var client = await _builder.GetClientAsync();
            if (await ExistsAsync(indexName)) return;
            await client.InitializeIndexMapAsync(indexName);
        }

        /// <summary>
        /// 添加索引。自动映射实体属性
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        public async Task AddAsync<T>(string indexName) where T : class
        {
            var client = await _builder.GetClientAsync();
            if (await ExistsAsync(indexName)) return;

            await client.InitializeIndexMapAsync<T>(indexName);
        }

        /// <summary>
        /// 添加索引。自动映射实体属性并赋值
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entity">实体</param>
        public async Task AddAsync<T>(string indexName, T entity) where T : class
        {
            var client = await _builder.GetClientAsync();
            if (!await ExistsAsync(indexName))
                await client.InitializeIndexMapAsync<T>(indexName);

            var response = await client.IndexAsync(entity, x => x.Index(indexName));
            if(!response.IsValid)
                throw new ElasticsearchException($"新增数据[{indexName}]失败 : {response.ServerError.Error.Reason}");
        }

        /// <summary>
        /// 更新索引。
        /// 由于是普通的简单更新,当ID已经存在时,则会更新文档,所以这里直接调用index方法(复杂方法待研究)
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entity">实体</param>
        public async Task UpdateAsync<T>(string indexName, T entity) where T : class
            =>await AddAsync<T>(indexName, entity);

        /// <summary>
        /// 删除索引
        /// </summary>
        /// <param name="indexName">索引名</param>
        public async Task DeleteAsync(string indexName)
        {
            var client = await _builder.GetClientAsync();
            var response = await client.DeleteIndexAsync(indexName);

            if (response.Acknowledged) return;
        }

        /// <summary>
        /// 删除索引
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entity">实体</param>
        public async Task DeleteAsync<T>(string indexName, T entity) where T : class
        {
            var client = await _builder.GetClientAsync();
            var response = await client.DeleteAsync(new DeleteRequest(indexName, typeof(T), new Id(entity)));
            if (response.ServerError == null) return;

            throw new ElasticsearchException($"删除索引[{indexName}]失败 : {response.ServerError.Error.Reason}");
        }

        /// <summary>
        /// 删除索引
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="id">主键ID</param>
        public async Task DeleteAsync<T>(string indexName, long id) where T : class
        {
            var client = await _builder.GetClientAsync();
            var response = await client.DeleteAsync(DocumentPath<T>.Id(new Id(id)), x => x.Type<T>().Index(indexName));

            if (response.ServerError == null) return;

            throw new ElasticsearchException($"删除索引[{indexName}]失败 : {response.ServerError.Error.Reason}");
        }

        /// <summary>
        /// 查询实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="id">主键ID</param>
        /// <returns></returns>
        public async Task<T> FindAsync<T>(string indexName, long id) where T : class
        {
            var client = await _builder.GetClientAsync();
            var response = await client.GetAsync<T>(id, x => x.Type<T>().Index(indexName));
            return response?.Source;
        }

        /// <summary>
        /// 查询。单一条件查询,一般是精确查询
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="field">字段名</param>
        /// <param name="value">查询值</param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> QueryAsync<T>(string indexName, string field, object value) where T : class
        {
            if (string.IsNullOrWhiteSpace(field))
                return null;

            var client = await _builder.GetClientAsync();
            var searchRequest = new SearchDescriptor<T>()
                .Index(indexName)
                .PostFilter(t => t.Term(x => x.Field(field).Value(value)));
            var response = await client.SearchAsync<T>(searchRequest);
            return response.Documents;
        }

        /// <summary>
        /// 查找实体列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="ids">主键值</param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> FindByIdsAsync<T>(string indexName, params long[] ids) where T : class
        {
            var client = await _builder.GetClientAsync();
            var searchRequest = new SearchDescriptor<T>().Index(indexName).Query(t => t.Ids(x => x.Values(ids)));
            var response = await client.SearchAsync<T>(searchRequest);
            return response.Documents;
        }

        /// <summary>
        /// 查找实体列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="ids">主键值</param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> FindByIdsAsync<T>(string indexName, params string[] ids) where T : class
        {
            var client = await _builder.GetClientAsync();
            var searchRequest = new SearchDescriptor<T>().Index(indexName).Query(t => t.Ids(x => x.Values(ids)));
            var response = await client.SearchAsync<T>(searchRequest);
            return response.Documents;
        }

        /// <summary>
        /// 查找实体列表
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="ids">主键值</param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> FindByIdsAsync<T>(string indexName, params Guid[] ids) where T : class
        {
            var client = await _builder.GetClientAsync();
            var searchRequest = new SearchDescriptor<T>().Index(indexName).Query(q => q.Ids(x => x.Values(ids)));
            var response = await client.SearchAsync<T>(searchRequest);
            return response.Documents;
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="param">分页参数</param>
        /// <param name="indexName">索引名</param>
        /// <returns></returns>
        public async Task<IQueryResult<T>> PageQueryAsync<T>(IPageParam param, string indexName) where T : class
        {
            if (param == null)
            {
                param = new PageParam()
                {
                    Page = 1,
                    PageSize = 20
                };
            }

            var searchRequest = new SearchDescriptor<T>()
                .Type<T>()
                .Index(indexName)
                .From(param.GetSkipCount())
                 .Size(param.PageSize);

            if (param is PageParamWithSearch pageSearch)
                ConfigPageRequest(pageSearch, ref searchRequest);
            else if(param is PageParam pageParam)
                ConfigPageRequest(pageParam, ref searchRequest);

            // 是否需要高亮
            bool hasHighlight = param.Highlight?.Keys?.Length > 0;
            if(hasHighlight)
                BuildHighLightQuery(param, ref searchRequest);

            var client = await _builder.GetClientAsync();
            var response = await client.SearchAsync<T>(x => searchRequest);

            //if (hasHighlight)
            //{
            //    var listWithHightlight = new List<T>();
            //    response.Hits.ToList().ForEach(x =>
            //    {
            //        if (x.Highlights?.Count > 0)
            //        {
            //            PropertyInfo[] properties = typeof(T).GetProperties();
            //            foreach (string key in pageParams.Highlight?.Keys)
            //            {
            //                //先得到要替换的内容
            //                if (x.Highlights.ContainsKey(key))
            //                {
            //                    string value = string.Join("", x.Highlights[key]?.Highlights);
            //                    PropertyInfo info = properties.FirstOrDefault(p => p.Name == pageParams.Highlight.PrefixOfKey + key);
            //                    //没找到带前缀的属性,则替换之前的
            //                    if (info == null && pageParams.Highlight.ReplaceAuto)
            //                    {
            //                        info = properties.FirstOrDefault(p => p.Name == key);
            //                    }
            //                    if (info?.CanWrite == true)
            //                    {
            //                        if (!string.IsNullOrEmpty(value))
            //                        {
            //                            //如果高亮字段不为空,才赋值,否则就赋值成空
            //                            info.SetValue(x.Source, value);
            //                        }
            //                    }
            //                }
            //            }
            //        }
            //        listWithHightlight.Add(x.Source);
            //    });
            //}

            return new CustomQueryResult<T>()
            {
                Data = response.Documents,
                Took = response.Took,
                TotalCount = response.Total
            };
        }

        /// <summary>
        /// 配置指定字段的分页请求
        /// </summary>
        private void ConfigPageRequest<T>(PageParamWithSearch param, ref SearchDescriptor<T> searchRequest) where T : class
        {
            searchRequest = searchRequest.Query(t=>
              t.QueryString(x =>
                    x.Fields(param.SearchKeys)
                        .Query(param.Keyword)
                        .DefaultOperator(param.Operator)));
        }

        /// <summary>
        /// 配置分页请求
        /// </summary>
        private void ConfigPageRequest<T>(PageParam param, ref SearchDescriptor<T> searchRequest) where T : class
        {
            searchRequest= searchRequest.Query(
                t=>t.QueryString(q=>q.Query(param.Keyword)
            .DefaultOperator(param.Operator)));
        }

        /// <summary>
        /// 构造高亮查询
        /// </summary>
        private void BuildHighLightQuery<T>(IPageParam param, ref SearchDescriptor<T> searchRequest) where T : class
        {
            var keysLength = param.Highlight?.Keys?.Length ?? 0;
            var fieldDescriptor = new Func<HighlightFieldDescriptor<T>, IHighlightField>[keysLength];
            var keysIndex = 0;

            foreach (var key in param.Highlight?.Keys)
            {
                fieldDescriptor[keysIndex] = hf => hf.Field(key)
                    .HighlightQuery(q => q.Match(m => m.Field(key).Query(param.Keyword)));
                keysIndex++;
            }

            IHighlight highlight = new HighlightDescriptor<T>()
               .PreTags(param.Highlight.PreTags)
               .PostTags(param.Highlight.PostTags)
               .Fields(fieldDescriptor);
            searchRequest = searchRequest.Highlight(s => highlight);
        }

        /// <summary>
        /// 批量保存
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entities">实体列表</param>
        public async Task BulkSaveAsync<T>(string indexName, IEnumerable<T> entities) where T : class
        {
            var client = await _builder.GetClientAsync();

            if (!await ExistsAsync(indexName))
            {
                await client.InitializeIndexMapAsync<T>(indexName);
            }
            var bulk = new BulkRequest(indexName)
            {
                Operations = new List<IBulkOperation>()
            };
            foreach (var entity in entities)
            {
                bulk.Operations.Add(new BulkIndexOperation<T>(entity));
            }

            var response = await client.BulkAsync(bulk);
            if (response.Errors)
            {
                throw new ElasticsearchException($"批量保存文档在索引 {indexName} 失败:{response.ServerError.Error.Reason}");
            }
        }
    }

  ElasticsearchClientBuilder

/// <summary>
    /// ES客户端生成器
    /// </summary>
    internal class ElasticsearchClientBuilder
    {
        /// <summary>
        /// ES客户端
        /// </summary>
        private IElasticClient _client;

        /// <summary>
        /// 配置提供程序
        /// </summary>
        private readonly IElasticsearchConfigProvider _configProvider;

        /// <summary>
        /// 对象锁
        /// </summary>
        private static object _lock = new object();

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchClientBuilder"/>类型的实例
        /// </summary>
        /// <param name="configProvider">配置提供程序</param>
        public ElasticsearchClientBuilder(IElasticsearchConfigProvider configProvider)
        {
            _configProvider = configProvider;
        }

        /// <summary>
        /// 获取ES客户端
        /// </summary>
        /// <returns></returns>
        public async Task<IElasticClient> GetClientAsync()
        {
            if (_client == null)
            {
                var config = await _configProvider.GetConfigAsync();
                lock (_lock)
                {
                    if (_client == null)
                    {
                        if (config.Nodes == null) throw new ArgumentException("请设置ES客户端节点");

                        _client = CreateClient(config);
                    }
                }
            }

            return _client;
        }

        /// <summary>
        /// 创建ES客户端
        /// </summary>
        /// <param name="config">配置</param>
        /// <returns></returns>
        private IElasticClient CreateClient(ElasticsearchConfig config)
        {
            var connectionPool = CreateConnectionPool(config);
            var settings = new ConnectionSettings(connectionPool);
            ConfigSettings(settings, config);
            return new ElasticClient(settings);
        }

        /// <summary>
        /// 创建连接池
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        private IConnectionPool CreateConnectionPool(ElasticsearchConfig config)
        {
            var nodes = config.Nodes.Select(t => new Uri(t.ToString())).ToList();
            switch (config.PoolType)
            {
                case ElasticsearchConnectionPoolType.Static:
                    return new StaticConnectionPool(nodes);
                case ElasticsearchConnectionPoolType.SingleNode:
                    return new SingleNodeConnectionPool(nodes.FirstOrDefault());
                case ElasticsearchConnectionPoolType.Sniffing:
                    return new SniffingConnectionPool(nodes);
                case ElasticsearchConnectionPoolType.Sticky:
                    return new StickyConnectionPool(nodes);
                case ElasticsearchConnectionPoolType.StickySniffing:
                    return new StickySniffingConnectionPool(nodes, x => 1.0F);
                default:
                    return new StaticConnectionPool(nodes);
            }
        }

        /// <summary>
        /// 配置连接设置
        /// </summary>
        /// <param name="settings">连接设置</param>
        /// <param name="config">配置</param>
        private void ConfigSettings(ConnectionSettings settings, ElasticsearchConfig config)
        {
            // 启用验证
            if (!string.IsNullOrWhiteSpace(config.UserName) && !string.IsNullOrWhiteSpace(config.Password))
                settings.BasicAuthentication(config.UserName, config.Password);

            // 验证证书
            //settings.ClientCertificate("");
            //settings.ClientCertificates(new System.Security.Cryptography.X509Certificates.X509CertificateCollection());
            //settings.ServerCertificateValidationCallback();

            // 开启第一次使用时进行嗅探,需连接池支持
            //settings.SniffOnStartup(false);

            // 链接最大并发数
            //settings.ConnectionLimit(80);

            // 标记为死亡节点的超时时间
            //settings.DeadTimeout(new TimeSpan(10000));
            //settings.MaxDeadTimeout(new TimeSpan(10000));

            // 最大重试次数
            //settings.MaximumRetries(5);

            // 重试超时时间,默认是RequestTimeout
            //settings.MaxRetryTimeout(new TimeSpan(50000));

            // 禁用代理自动检测
            //settings.DisableAutomaticProxyDetection(true);

            // 禁用ping,第一次使用节点或使用被标记死亡的节点进行ping
            settings.DisablePing(config.DisablePing);

            // ping超时设置
            //settings.PingTimeout(new TimeSpan(10000));

            // 选择节点
            //settings.NodePredicate(node => { return true; });

            // 默认操作索引
            //settings.DefaultIndex("");

            // 字段名规则 与model字段同名
            //settings.DefaultFieldNameInferrer(name => name);

            // 根据Type获取类型名
            //settings.DefaultTypeNameInferrer(name => name.Name);

            // 请求超时设置
            //settings.RequestTimeout(new TimeSpan(10000));

            // 调试信息
            settings.DisableDirectStreaming(config.DisableDebugInfo);
            //settings.EnableDebugMode((apiCallDetails) =>
            //{
            //    // 请求完成 返回 apiCallDetails
            //});

            // 抛出异常,默认false,错误信息在每个操作的response中
            settings.ThrowExceptions(config.ThrowExceptions);
            //settings.OnRequestCompleted(apiCallDetails =>
            //{
            //    // 请求完成 返回apiCallDetails
            //});
            //settings.OnRequestDataCreated(requestData =>
            //{
            //    // 请求的数据创建完成 返回请求的数据
            //});
        }

    }

  ElasticsearchException

/// <summary>
    /// Elasticsearch 异常
    /// </summary>
    [Serializable]
    public class ElasticsearchException : Exception
    {
        /// <summary>
        /// 初始化一个<see cref="ElasticsearchException"/>类型的实例
        /// </summary>
        public ElasticsearchException() { }

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchException"/>类型的实例
        /// </summary>
        /// <param name="serializationInfo">序列号信息</param>
        /// <param name="context">流上下文</param>
        public ElasticsearchException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context)
        { 
        
        }

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchException"/>类型的实例
        /// </summary>
        /// <param name="message">错误消息</param>
        public ElasticsearchException(string message) : base(message) { }

        /// <summary>
        /// 初始化一个<see cref="ElasticsearchException"/>类型的实例
        /// </summary>
        /// <param name="message">错误消息</param>
        /// <param name="innerException">内部异常</param>
        public ElasticsearchException(string message, Exception innerException) : base(message, innerException) { }
    }

  IElasticsearchClient

/// <summary>
    /// ES客户端
    /// </summary>
    public interface IElasticsearchClient
    {
        /// <summary>
        /// 是否存在指定索引
        /// </summary>
        /// <param name="indexName">索引名</param>
        /// <returns></returns>
        Task<bool> ExistsAsync(string indexName);

        /// <summary>
        /// 添加索引。不映射
        /// </summary>
        /// <param name="indexName">索引名</param>
        Task AddAsync(string indexName);

        /// <summary>
        /// 添加索引。自动映射实体属性
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        Task AddAsync<T>(string indexName) where T : class;

        /// <summary>
        /// 添加索引。自动映射实体属性并赋值
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        Task AddAsync<T>(string indexName, T entity) where T : class;

        /// <summary>
        /// 更新索引。
        /// 由于是普通的简单更新,当ID已经存在时,则会更新文档,所以这里直接调用index方法(复杂方法待研究)
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="indexName">索引名</param>
        /// <param name="entity">实体</param>
        Task UpdateAsync<T>(string indexName, T entity) where T : class;
    }

  

posted @ 2021-03-03 13:31  石shi  阅读(792)  评论(0编辑  收藏  举报