C# Asp.net中使用Elasticsearch5.x 的NEST客户端

本文中,数据(文档)之间的关系类型,是嵌套文档

事前准备:

1、安装:

elasticsearch-5.6.2

2、引用:

Elasticsearch.Net(这里使用的是6.0.0)

Nest(这里使用的是6.0.0)

一、连接(核心):

public class ESProvider
{
    public ElasticClient ESClient;

    public ESProvider()
    {
        try
        {
            //单个节点
            //var node = new Uri(ElasticSearchSetting.ServerNode);
            //ConnectionSettings settings = new ConnectionSettings(node);
            //settings.RequestTimeout(TimeSpan.FromSeconds(ElasticSearchSetting.RequestTimeout));//超时时间
            //settings.DefaultIndex(ElasticSearchSetting.DefaultIndexName.ToLower());//默认索引(可选,索引名必须小写)
            //ESClient = new ElasticClient(settings);

            //多节点(方便扩展)
            string server = ElasticSearchSetting.ServerNode;
            string[] serverArray = server.Split(',');
            Uri[] nodesArray = new Uri[serverArray.Length];
            for (int i = 0; i < serverArray.Length; i++)
            {
                nodesArray[i] = new Uri(serverArray[i]);
            }
            //var connectionPool = new SniffingConnectionPool(nodesArray);
            var connectionPool = new StaticConnectionPool(nodesArray);
            var settings = new ConnectionSettings(connectionPool);
            settings.RequestTimeout(TimeSpan.FromSeconds(ElasticSearchSetting.RequestTimeout));//超时时间
            settings.DefaultIndex(ElasticSearchSetting.DefaultIndexName.ToLower());//默认索引(可选,索引名必须小写)
            ESClient = new ElasticClient(settings);
        }
        catch (Exception ex)
        {
        }
    }
}

配置文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <ElasticSearch>
    <!--节点(如果多个节点则用","号隔开)-->
    <add key="ServerNode" value="http://localhost:9200"></add>
    <!--超时时间(单位秒)-->
    <add key="RequestTimeout" value="120"></add>
  </ElasticSearch>
</configuration>

二、映射(简单映射,sku嵌套在spu中):

//IdProperty指定主键
    [ElasticsearchType(Name = "ES_GoodsSpu", IdProperty = "GoodsSpuId")]
    public class ES_GoodsSpu
    {
        [Keyword(Index = true)]
        public string GoodsSpuId { get; set; }
        
        [Text(Index = true, Analyzer = "ik_max_word")]
        public string GoodsName { get; set; }

        [Keyword(Index = false)]
        public string GoodsMainImg { get; set; }

        [Text(Index = true, Analyzer = "ik_max_word")]
        public string GoodsContent { get; set; }

        [Text(Index = true, Analyzer = "ik_max_word")]
        public string GoodsLabel { get; set; }
        
        [Keyword(Index = true)]
        public string AddTime { get; set; }
        
        [Nested]
        public List<ES_GoodsSku> GoodsSkuList { get; set; }
    }

    public class ES_GoodsSku
    {
        [Keyword(Index = true)]
        public string GoodsSpuId { get; set; }
        [Keyword(Index = true)]
        public string GoodsSkuId { get; set; }
        [Keyword(Index = true)]
        public string SkuName { get; set; }
        [Number(NumberType.Double, Index = false)]
        public double SkuWidth { get; set; }
        [Number(NumberType.Double, Index = false)]
        public double SkuLength { get; set; }
        [Number(NumberType.Double, Index = false)]
        public double SkuHeight { get; set; }
        [Number(NumberType.Byte, Index = true)]
        public byte SkuColor { get; set; } 
        //Elasticsearch中没有decimal等效的类型,最近的类型是double
        [Number(NumberType.Double, Index = true)]
        public decimal SkuPrice { get; set; }
        [Keyword(Index = false)]
        public DateTime AddTime { get; set; }
    }

三、创建索引:

public bool CreateIndex<ES_GoodsSku>(string indexName) 
{
    try
    {
        if (string.IsNullOrWhiteSpace(indexName))
            return false;

        indexName = indexName.ToLower();

        IndexState indexState = new IndexState
        {
            Settings = new IndexSettings
            {
                NumberOfReplicas = 1, //副本数
                NumberOfShards = 5 //分片数
            }
        };

        ICreateIndexResponse response = ESClient.CreateIndex(indexName, p => p
            //.InitializeUsing(indexState)//默认即可
            .Mappings(m => m.Map<ES_GoodsSku>(mp => mp.AutoMap()))//自动映射
        );

        return response.IsValid;
    }
    catch (Exception ex)
    {
        return false;
    }
}

四、添加:

public bool Add(ES_GoodsSku model,string indexName)
{
    try
    {
        if (model == null)
            return false;

        var response = ESClient.Index(model, x => x.Index(indexName));
        return response.IsValid;
    }
    catch (Exception ex)
    {
        return false;
    }
}

五、删除:

/// <summary>
/// 单个删除
/// </summary>
/// <param name="spuId"></param>
/// <returns></returns>
public bool DeleteBySpuId(string spuId)
{
    try
    {
        if (string.IsNullOrWhiteSpace(spuId))
            return false;

        var response = ESClient.DeleteByQuery<ES_GoodsSpu>(
            i => i
                .Index(indexName)
                .Query(q => q.Term(t => t.Field(f => f.GoodsSpuId).Value(spuId)))
            );

        return response.IsValid;
    }
    catch (Exception ex)
    {
        return false;
    }
}

/// <summary>
/// 批量删除
/// </summary>
/// <param name="idArray"></param>
/// <returns></returns>
public bool DeleteBySpuIdArray(string[] spuIdArray)
{
    try
    {
        if (spuIdArray.Length <= 0)
            return false;

        var response = ESClient.DeleteByQuery<ES_GoodsSpu>(
            i => i
                .Index(indexName)
                .Query(q => q.Terms(t => t.Field(f => f.GoodsSpuId).Terms(spuIdArray)))
            );

        return response.IsValid;
    }
    catch (Exception ex)
    {
        return false;
    }
}

六、查询:

1、简单查询(包括嵌套查询)

/// <summary>
/// 查询
/// </summary>
/// <param name="param">参数</param>
/// <param name="num">数量</param>
/// <param name="orderby">排序</param>
/// <returns></returns>
public List<ES_GoodsSpu> GetList(Dictionary<string, object> param, int num, string orderby = null)
{
    try
    {
        //排序
        Func<SortDescriptor<ES_GoodsSpu>, IPromise<IList<ISort>>> sortDesc = sd =>
        {
            //根据分值排序
            sd.Ascending(SortSpecialField.Score);
                    
            sd.Ascending(d => d.AddTime);
 
            return sd;
        };
 
        //must 条件
        var mustQuerys = new List<Func<QueryContainerDescriptor<ES_GoodsSpu>, QueryContainer>>();
        //must not 条件
        var mustNotQuerys = new List<Func<QueryContainerDescriptor<ES_GoodsSpu>, QueryContainer>>();
        //should 条件
        var shouldQuerys = new List<Func<QueryContainerDescriptor<ES_GoodsSpu>, QueryContainer>>();
 
        if (param != null && param.Count > 0)
        {
            foreach (KeyValuePair<String, object> kvp in param)
            {
                if (string.IsNullOrWhiteSpace(kvp.Value.ToString()))
                    continue;
 
                string key = kvp.Key.ToLower();
                string value = kvp.Value.ToString();
                switch (key)
                {
                    case "spuids": //多个id集合
                        if (!string.IsNullOrWhiteSpace(value) && value.Split(',').Count() > 0)
                        {
                            string[] spuIdArray = value.Split(',');
                            mustQuerys.Add(mq => mq.Terms(tm => tm.Field(f => f.GoodsSpuId).Terms(spuIdArray)));
                        }
                        break;
                    case "name":
                        mustQuerys.Add(mq => mq.Term(tm => tm.Field(f => f.GoodsName).Value(value)));
                        break;
                    case "color":
                        byte color = 0;
                        byte.TryParse(value, out color);
                        if (color > 0)
                        {
                            //嵌套查询(Nested,Path)
                            mustQuerys.Add(mq => mq.Nested(n => n.Path(p => p.GoodsSkuList).Query(q => q.Term(tm => tm.Field(f => f.GoodsSkuList.FirstOrDefault().SkuColor).Value(color)))));
                        }
                        break;
                    case "minprice":
                        double minprice = -1;//默认值
                        double.TryParse(value, out minprice);
                        if (minprice > -1)
                        {
                            double maxprice0 = 9999999999;//需要和LessThanOrEquals一起才起作用,所以这里给个最大值
                            //嵌套查询(Nested,Path)
                            mustQuerys.Add(mq => mq.Nested(n => n.Path(p => p.GoodsSkuList).Query(q => q.Range(tm => tm.Field(f => f.GoodsSkuList.FirstOrDefault().SkuPrice).GreaterThanOrEquals(minprice).LessThanOrEquals(maxprice0)))));
                        }
                        break;
                    case "maxprice":
                        double maxprice = -1;//默认值
                        double.TryParse(value, out maxprice);
                        if (maxprice > -1)
                        {
                            //嵌套查询(Nested,Path)
                            mustQuerys.Add(mq => mq.Nested(n => n.Path(p => p.GoodsSkuList).Query(q => q.Range(tm => tm.Field(f => f.GoodsSkuList.FirstOrDefault().SkuPrice).LessThanOrEquals(maxprice)))));
                        }
                        break;
                }
            }
        }
 
        //搜索
        var searchResults = base.ESClient.Search<ES_GoodsSpu>(s => s
            .Index(indexName)
            .Size(num)
            .Query(q => q.Bool(b => b.Must(mustQuerys).MustNot(mustNotQuerys).Should(shouldQuerys)))
            .Sort(sortDesc)
            );
 
        return searchResults.Documents.ToList();
    }
    catch (Exception ex)
    {
        LogObj.GetLogService().LogDetailError(ex);
        return null;
    }
}

2、分页查询

/// <summary>
/// 分页查询
/// </summary>
/// <param name="pageIndex">页索引</param>
/// <param name="pageSize">页大小</param>
/// <param name="param">参数</param>
/// <param name="totalCount">总数量</param>
/// <param name="totalPage">总页数</param>
/// <param name="orderby">排序</param>
/// <returns></returns>
public List<ES_GoodsSpu> GetPageList(int pageIndex, int pageSize, Dictionary<string, object> param, ref long totalCount, ref long totalPage, string orderby = null)
{
    try
    {
        //排序
        Func<SortDescriptor<ES_GoodsSpu>, IPromise<IList<ISort>>> sortDesc = sd =>
        {
            //根据分值排序
            sd.Descending(SortSpecialField.Score);
            sd.Descending(d => d.AddTime); 
            return sd;
        };
 
        //must 条件
        var mustQuerys = new List<Func<QueryContainerDescriptor<ES_GoodsSpu>, QueryContainer>>();
        //should 条件
        var shouldQuerys = new List<Func<QueryContainerDescriptor<ES_GoodsSpu>, QueryContainer>>();
 
        if (param != null && param.Count > 0)
        {
            foreach (KeyValuePair<String, object> kvp in param)
            {
                if (string.IsNullOrWhiteSpace(kvp.Value.ToString()))
                    continue;
 
                string key = kvp.Key.ToLower();
                string value = kvp.Value.ToString();
                switch (key)
                {
                    case "name":
                        mustQuerys.Add(mq => mq.Term(tm => tm.Field(f => f.GoodsName).Value(value)));
                        break;
                    case "querykey":
                        //should条件
                        shouldQuerys.Add(mq => mq.Term(tm => tm.Field(f => f.GoodsName).Value(value)));
                        shouldQuerys.Add(mq => mq.Term(tm => tm.Field(f => f.GoodsLabel).Value(value)));
                        shouldQuerys.Add(mq => mq.Term(tm => tm.Field(f => f.GoodsContent).Value(value)));
                        break;
                }
            }
        }               
 
        //搜索
 
        var searchResults = base.ESClient.Search<ES_GoodsSpu>(s => s
            .Index(indexName)
            .From(pageSize * (pageIndex - 1))
            .Size(pageSize)
            .Query(q => q.Bool(b => b.Must(mustQuerys).Should(shouldQuerys)))
            .Sort(sortDesc)
            );//匹配全部
 
                
        //总数、总页数
        totalCount = searchResults.Total;
        totalPage = (long)Math.Ceiling((double)totalCount / (double)pageSize);
 
        //返回
        return searchResults.Documents.ToList();
    }
    catch (Exception ex)
    {
        return null;
    }
}

3、分组查询(根据品牌id,获取每个品牌下商品数量)

/// <summary>
/// 根据品牌id分组,统计每个品牌下的商品数量
/// </summary>
/// <returns></returns>
public List<Tuple<string, long>> GetGroupByBrandId(List<string> brnadIdList)
{
    try
    {
        if (brnadIdList.Count <= 0)
            return null;

        string groupName = "BrandID_group";
        var result = base.ESClient.Search<ES_GoodsSpu>(s => s
            .Index(indexName)
            .Query(q => q.Terms(tm => tm.Field(f => f.BrandID).Terms(brnadIdList.ToArray())))
            .Aggregations(ag => ag
                    .Terms(groupName, t => t
                        .Field("brandID.keyword") //分组字段需要为keyword类型,所以这里加了".keyword"后缀,并且brandID名称要和es相同(包括大小写)
                        .Size(brnadIdList.Count) //分组数(这里和品牌id集合数相同)
                        )
                )
            );

        if (result.IsValid)
        {
            List<Tuple<string, long>> tupleList = new List<Tuple<string, long>>();
            var vendorIdGroup = (BucketAggregate)result.Aggregations[groupName];
            foreach (var bucket in vendorIdGroup.Items)
            {
                var obj = (KeyedBucket<Object>)bucket;
                string brandId = obj.Key.ToString();//品牌id
                long num = (long)obj.DocCount;//该品牌下的商品数量
                Tuple<string, long> tuple = new Tuple<string, long>(brandId, num);
                tupleList.Add(tuple);
            }
            return tupleList;
        }
        return null;
    }
    catch (Exception ex)
    {
        return null;
    }
}

上面代码中Field("brandID.keyword"),如果字段不是keyword类型,会报exception [type=search_phase_execution_exception, reason=all shards failed]错误。

 

最后:

这里只是把我的项目中代码简单复制了过来,也是第一次使用ElasticSearch,各种查资料,终于不负有心人,功能最终都实现了,如果有错误或者有改进之处,望不吝赐教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章