搜索引擎 ElasticSearch.NET 客户端封装
Posted 程序员共读
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搜索引擎 ElasticSearch.NET 客户端封装相关的知识,希望对你有一定的参考价值。
关键时刻,第一时间送达!
先不说楚枫的这般年纪,能够踏入元武一重说明了什么,最主要的是,楚枫在刚刚踏入核心地带时,明明只是灵武七重,而在这两个月不到的时间,连跳两重修为,又跳过一个大境界,踏入了元武一重,这般进步速度,简直堪称变态啊。
“这楚枫不简单,原来是一位天才,若是让他继续成长下去,绝对能成为一号人物,不过可惜,他太狂妄了,竟与龚师兄定下生死约战,一年时间,他再厉害也无法战胜龚师兄。”有人认识到楚枫的潜力后,为楚枫感到惋惜。
“哼,何须一年,此子今日就必败,巫九与龚师兄关系甚好,早就看他不顺眼了,如今他竟敢登上生死台挑战巫九,巫九岂会放过他?”但也有人认为,楚枫今日就已是在劫难逃。
“何人挑战老子?”就在这时,又是一声爆喝响起,而后一道身影自人群之中掠出,最后稳稳的落在了比斗台上。
这位身材瘦弱,身高平平,长得那叫一个猥琐,金钩鼻子蛤蟆眼,嘴巴一张牙带色儿,说话臭气能传三十米,他若是当面对谁哈口气,都能让那人跪在地上狂呕不止。
不过别看这位长得不咋地,他在核心地带可是鼎鼎有名,剑道盟创建者,青龙榜第九名,正是巫九是也。
“你就是巫九?”楚枫眼前一亮,第一次发现,世间还有长得如此奇葩的人。
巫九鼻孔一张,大嘴一咧,拍着那干瘪的肚子,得意洋洋的道:“老子就是巫九,你挑战老子?”
“不是挑战你,是要宰了你。”楚枫冷声笑道。
“好,老子满足你这个心愿,长老,拿张生死状来,老子今日在这里了解了这小子。”巫九扯开嗓子,对着下方吼了一声。
如果他对内门长老这么说话,也就算了,但是敢这么跟核心长老说话的,他可真是算作胆肥的,就连许多核心弟子,都是倒吸了一口凉气,心想这楚枫够狂,想不到这巫九更狂。
不过最让人无言的就是,巫九话音落下不久,真有一位核心长老自人群走出,缓缓得来到了比斗台上,左手端着笔墨,右手拿着生死状,来到了巫九的身前。
“我去,这巫九什么身份,竟能这般使唤核心长老?”有人吃惊不已,那长老修为不低,乃是元武七重,比巫九还要高两个层次,但却这般听巫九的话,着实让人吃惊不已。
“这你就不知道了吧,巫九在前些时日,拜了钟离长老为师尊,已正式得到钟离长老的亲传。”有人解释道。
“钟离长老?可是那位性情古怪的钟离一护?”
“没错,就是他。”
“天哪,巫九竟然拜入了他的门下?”
人们再次大吃一惊,那钟离一护在青龙宗可是赫赫有名,若要是论其个人实力,在青龙宗内绝对能够排入前三,连护宗六老单打独斗都不会是他的对手。
只不过那钟离一护,如同诸葛青云一样,也是一位客卿长老,所以在青龙宗内只是挂个头衔,什么事都不管,更别说传授宗内弟子技艺了,如今巫九竟然能拜入他老人家门下,着实让人羡慕不已。
“恩怨生死台,的确可以决斗生死,但必须要有所恩怨,你们两个人,可有恩怨?”那位长老开口询问道。
先不说楚枫的这般年纪,能够踏入元武一重说明了什么,最主要的是,楚枫在刚刚踏入核心地带时,明明只是灵武七重,而在这两个月不到的时间,连跳两重修为,又跳过一个大境界,踏入了元武一重,这般进步速度,简直堪称变态啊。
“这楚枫不简单,原来是一位天才,若是让他继续成长下去,绝对能成为一号人物,不过可惜,他太狂妄了,竟与龚师兄定下生死约战,一年时间,他再厉害也无法战胜龚师兄。”有人认识到楚枫的潜力后,为楚枫感到惋惜。
“哼,何须一年,此子今日就必败,巫九与龚师兄关系甚好,早就看他不顺眼了,如今他竟敢登上生死台挑战巫九,巫九岂会放过他?”但也有人认为,楚枫今日就已是在劫难逃。
“何人挑战老子?”就在这时,又是一声爆喝响起,而后一道身影自人群之中掠出,最后稳稳的落在了比斗台上。
这位身材瘦弱,身高平平,长得那叫一个猥琐,金钩鼻子蛤蟆眼,嘴巴一张牙带色儿,说话臭气能传三十米,他若是当面对谁哈口气,都能让那人跪在地上狂呕不止。
不过别看这位长得不咋地,他在核心地带可是鼎鼎有名,剑道盟创建者,青龙榜第九名,正是巫九是也。
“你就是巫九?”楚枫眼前一亮,第一次发现,世间还有长得如此奇葩的人。
巫九鼻孔一张,大嘴一咧,拍着那干瘪的肚子,得意洋洋的道:“老子就是巫九,你挑战老子?”
“不是挑战你,是要宰了你。”楚枫冷声笑道。
“好,老子满足你这个心愿,长老,拿张生死状来,老子今日在这里了解了这小子。”巫九扯开嗓子,对着下方吼了一声。
如果他对内门长老这么说话,也就算了,但是敢这么跟核心长老说话的,他可真是算作胆肥的,就连许多核心弟子,都是倒吸了一口凉气,心想这楚枫够狂,想不到这巫九更狂。
不过最让人无言的就是,巫九话音落下不久,真有一位核心长老自人群走出,缓缓得来到了比斗台上,左手端着笔墨,右手拿着生死状,来到了巫九的身前。
“我去,这巫九什么身份,竟能这般使唤核心长老?”有人吃惊不已,那长老修为不低,乃是元武七重,比巫九还要高两个层次,但却这般听巫九的话,着实让人吃惊不已。
“这你就不知道了吧,巫九在前些时日,拜了钟离长老为师尊,已正式得到钟离长老的亲传。”有人解释道。
“钟离长老?可是那位性情古怪的钟离一护?”
“没错,就是他。”
“天哪,巫九竟然拜入了他的门下?”
人们再次大吃一惊,那钟离一护在青龙宗可是赫赫有名,若要是论其个人实力,在青龙宗内绝对能够排入前三,连护宗六老单打独斗都不会是他的对手。
只不过那钟离一护,如同诸葛青云一样,也是一位客卿长老,所以在青龙宗内只是挂个头衔,什么事都不管,更别说传授宗内弟子技艺了,如今巫九竟然能拜入他老人家门下,着实让人羡慕不已。
“恩怨生死台,的确可以决斗生死,但必须要有所恩怨,你们两个人,可有恩怨?”那位长老开口询问道。
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful Web接口。
Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
ElasticSearch 为.net提供了两个客户端,分别是 Elasticsearch.Net和NEST
Elasticsearch.net为什么会有两个客户端?
Elasticsearch.Net是一个非常底层且灵活的客户端,它不在意你如何的构建自己的请求和响应。它非常抽象,因此所有的Elasticsearch API被表示为方法,没有太多关于你想如何构建json/request/response对象的东东,并且它还内置了可配置、可重写的集群故障转移机制。
Elasticsearch.Net有非常大的弹性,如果你想更好的提升你的搜索服务,你完全可以使用它来做为你的客户端。
NEST是一个高层的客户端,可以映射所有请求和响应对象,拥有一个强类型查询DSL(领域特定语言),并且可以使用.net的特性比如协变、Auto Mapping Of POCOs,NEST内部使用的依然是Elasticsearch.Net客户端。
具体客户端的用法可参考官方的文档说明,本文主要针对 NEST 的查询做扩展。
起因:之前在学习Dapper的时候看过一个 DapperExtensions 的封装 其实Es的查询基本就是类似Sql的查询 。因此参考DapperExtensions 进行了Es版本的迁移。
通过官网说明可以看到 NEST 的对象初始化的方式进行查询 都是已下面的方式开头:
var searchRequest = new SearchRequest<XXT>(XXIndex)
我们可以通过查看源码
我们可以看到所有的查询基本都是在SearchRequest上面做的扩展 这样我们也可以开始我们的第一步操作:
1、关于分页,我们定义如下分页对象:
/// <summary>
/// 分页类型
/// </summary>
public class PageEntity
{
/// <summary>
/// 每页行数
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 当前页
/// </summary>
public int PageIndex { get; set; }
/// <summary>
/// 总记录数
/// </summary>
public int Records { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int Total
{
get
{
if (Records > 0)
return Records % PageSize == 0 ? Records / PageSize : Records / PageSize + 1;
return 0;
}
}
/// <summary>
/// 排序列
/// </summary>
public string Sidx { get; set; }
/// <summary>
/// 排序类型
/// </summary>
public string Sord { get; set; }
}
2、定义ElasticsearchPage 分页对象
/// <summary>
///ElasticsearchPage
/// </summary>
public class ElasticsearchPage<T> : PageEntity
{
public string Index { get; set; }
public ElasticsearchPage(string index)
{
Index = index;
}
/// <summary>
/// InitSearchRequest
/// </summary>
/// <returns></returns>
public SearchRequest<T> InitSearchRequest()
{
return new SearchRequest<T>(Index)
{
From = (PageIndex - 1) * PageSize,
Size = PageSize
};
}
}
至此我们的SearchRequest的初始化操作已经完成了我们可以通过如下方式进行调用
var elasticsearchPage = new ElasticsearchPage<Content>("content")
{
PageIndex = pageIndex,
PageSize = pageSize
};
var searchRequest = elasticsearchPage.InitSearchRequest();
通过SearchRequest的源码我们可以得知,所有的查询都是基于内部属性进行(扩展的思路来自DapperExtensions):
3、QueryContainer的扩展 ,类似Where 语句:
我们定义一个 比较操作符 类似 Sql中的 like != in 等等
/// <summary>
/// 比较操作符
/// </summary>
public enum ExpressOperator
{
/// <summary>
/// 精准匹配 term(主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型): )
/// </summary>
Eq,
/// <summary>
/// 大于
/// </summary>
Gt,
/// <summary>
/// 大于等于
/// </summary>
Ge,
/// <summary>
/// 小于
/// </summary>
Lt,
/// <summary>
/// 小于等于
/// </summary>
Le,
/// <summary>
/// 模糊查询 (You can use % in the value to do wilcard searching)
/// </summary>
Like,
/// <summary>
/// in 查询
/// </summary>
In
}
接着我们定义一个 如下接口,主要包括:
提供返回一个 QueryContainer GetQuery方法
属性名称 PropertyName
操作符 ExpressOperator
谓词值 Value
/// <summary>
/// 谓词接口
/// </summary>
public interface IPredicate
{
QueryContainer GetQuery(QueryContainer query);
}
/// <summary>
/// 基础谓词接口
/// </summary>
public interface IBasePredicate : IPredicate
{
/// <summary>
/// 属性名称
/// </summary>
string PropertyName { get; set; }
}
public abstract class BasePredicate : IBasePredicate
{
public string PropertyName { get; set; }
public abstract QueryContainer GetQuery(QueryContainer query);
}
/// <summary>
/// 比较谓词
/// </summary>
public interface IComparePredicate : IBasePredicate
{
/// <summary>
/// 操作符
/// </summary>
ExpressOperator ExpressOperator { get; set; }
}
public abstract class ComparePredicate : BasePredicate
{
public ExpressOperator ExpressOperator { get; set; }
}
/// <summary>
/// 字段谓词
/// </summary>
public interface IFieldPredicate : IComparePredicate
{
/// <summary>
/// 谓词的值
/// </summary>
object Value { get; set; }
}
具体实现定义 FieldPredicate 并且继承如上接口,通过操作符映射为 Nest具体查询对象
public class FieldPredicate<T> : ComparePredicate, IFieldPredicate
where T : class
{
public object Value { get; set; }
public override QueryContainer GetQuery(QueryContainer query)
{
switch (ExpressOperator)
{
case ExpressOperator.Eq:
query = new TermQuery
{
Field = PropertyName,
Value = Value
};
break;
case ExpressOperator.Gt:
query = new TermRangeQuery
{
Field = PropertyName,
GreaterThan = Value.ToString()
};
break;
case ExpressOperator.Ge:
query = new TermRangeQuery
{
Field = PropertyName,
GreaterThanOrEqualTo = Value.ToString()
};
break;
case ExpressOperator.Lt:
query = new TermRangeQuery
{
Field = PropertyName,
LessThan = Value.ToString()
};
break;
case ExpressOperator.Le:
query = new TermRangeQuery
{
Field = PropertyName,
LessThanOrEqualTo = Value.ToString()
};
break;
case ExpressOperator.Like:
query = new MatchPhraseQuery
{
Field = PropertyName,
Query = Value.ToString()
};
break;
case ExpressOperator.In:
query = new TermsQuery
{
Field = PropertyName,
Terms=(List<object>)Value
};
break;
default:
throw new ElasticsearchException("构建Elasticsearch查询谓词异常");
}
return query;
}
}
4、定义好这些后我们就可以拼接我们的条件了,我们定义了 PropertyName 但是我们更倾向于一种类似EF的查询方式 可以通过 Expression<Func<T, object>> 的方式所以我们这边提供一个泛型方式,因为在创建 Elasticsearch 文档的时候我们已经建立了Map 文件 我们通过反射读取 PropertySearchName属性 就可以读取到我们的 PropertyName 这边 PropertySearchName 是自己定义的属性
为什么不反解Nest 的属性 针对不同类型需要反解的属性也是不相同的 所以避免麻烦 直接重新定义了新的属性 。代码如下:
public class PropertySearchNameAttribute: Attribute
{
public PropertySearchNameAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
}
然后我们就可以来定义的们初始化IFieldPredicate 的方法了
首先我们解析我们的需求:
1、我们需要一个Expression<Func<T, object>>
2、我们需要一个操作符
3、我们需要比较什么值
针对需求我们可以得到这样一个方法:
注:所依赖的反射方法详解文末
/// <summary>
/// 工厂方法创建一个新的 IFieldPredicate 谓语: [FieldName] [Operator] [Value].
/// </summary>
/// <typeparam name="T">实例类型</typeparam>
/// <param name="expression">返回左操作数的表达式 [FieldName].</param>
/// <param name="op">比较运算符</param>
/// <param name="value">谓语的值.</param>
/// <returns>An instance of IFieldPredicate.</returns>
public static IFieldPredicate Field<T>(Expression<Func<T, object>> expression, ExpressOperator op, object value) where T : class
{
var propertySearchName = (PropertySearchNameAttribute)
LoadAttributeHelper.LoadAttributeByType<T, PropertySearchNameAttribute>(expression);
return new FieldPredicate<T>
{
PropertyName = propertySearchName.Name,
ExpressOperator = op,
Value = value
};
}
然后 我们就可以像之前拼接sql的方式来进行拼接条件了
就以我们项目中的业务需求做个演示
var predicateList = new List<IPredicate>();
//最大价格
if (requestContentDto.MaxPrice != null)
predicateList.Add(Predicates.Field<Content>(x => x.UnitPrice, ExpressOperator.Le,
requestContentDto.MaxPrice));
//最小价格
if (requestContentDto.MinPrice != null)
predicateList.Add(Predicates.Field<Content>(x => x.UnitPrice, ExpressOperator.Ge,
requestContentDto.MinPrice));
然后针对实际业务我们在写sql的时候就回有 (xx1 and xx2) or xx3 这样的业务需求了
针对这种业务需求 我们需要在提供一个 IPredicateGroup 进行分组查询谓词
首先我们定义一个PredicateGroup 加入谓词时使用的操作符 GroupOperator
/// <summary>
/// PredicateGroup 加入谓词时使用的操作符
/// </summary>
public enum GroupOperator
{
And,
Or
}
然后我们定义 IPredicateGroup 及实现
/// <summary>
/// 分组查询谓词
/// </summary>
public interface IPredicateGroup : IPredicate
{
/// <summary>
/// </summary>
GroupOperator Operator { get; set; }
IList<IPredicate> Predicates { get; set; }
}
/// <summary>
/// 分组查询谓词
/// </summary>
public class PredicateGroup : IPredicateGroup
{
public GroupOperator Operator { get; set; }
public IList<IPredicate> Predicates { get; set; }
/// <summary>
/// GetQuery
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public QueryContainer GetQuery(QueryContainer query)
{
switch (Operator)
{
case GroupOperator.And:
return Predicates.Aggregate(query, (q, p) => q && p.GetQuery(query));
case GroupOperator.Or:
return Predicates.Aggregate(query, (q, p) => q || p.GetQuery(query));
default:
throw new ElasticsearchException("构建Elasticsearch查询谓词异常");
}
}
}
现在我们可以用 PredicateGroup来组装我们的 谓词
同样解析我们的需求:
1、我们需要一个GroupOperator
2、我们需要谓词列表 IPredicate[]
针对需求我们可以得到这样一个方法:
/// <summary>
/// 工厂方法创建一个新的 IPredicateGroup 谓语.
/// 谓词组与其他谓词可以连接在一起.
/// </summary>
/// <param name="op">分组操作时使用的连接谓词 (AND / OR).</param>
/// <param name="predicate">一组谓词列表.</param>
/// <returns>An instance of IPredicateGroup.</returns>
public static IPredicateGroup Group(GroupOperator op, params IPredicate[] predicate)
{
return new PredicateGroup
{
Operator = op,
Predicates = predicate
};
}
这样我们就可以进行组装了
用法:
//构建或查询
var predicateList= new List<IPredicate>();
//关键词
if (!string.IsNullOrWhiteSpace(requestContentDto.SearchKey))
predicateList.Add(Predicates.Field<Content>(x => x.Title, ExpressOperator.Like,
requestContentDto.SearchKey));
var predicate = Predicates.Group(GroupOperator.And, predicateList.ToArray());
//构建或查询
var predicateListOr = new List<IPredicate>();
if (!string.IsNullOrWhiteSpace(requestContentDto.Brand))
{
var array = requestContentDto.Brand.Split(',').ToList();
predicateListOr
.AddRange(array.Select
(item => Predicates.Field<Content>(x => x.Brand, ExpressOperator.Like, item)));
}
var predicateOr = Predicates.Group(GroupOperator.Or, predicateListOr.ToArray());
var predicatecCombination = new List<IPredicate> {predicate, predicateOr};
var pgCombination = Predicates.Group(GroupOperator.And, predicatecCombination.ToArray());
然后我们的 IPredicateGroup 优雅的和 ISearchRequest 使用呢 我们提供一个链式的操作方法
/// <summary>
/// 初始化query
/// </summary>
/// <param name="searchRequest"></param>
/// <param name="predicate"></param>
public static ISearchRequest InitQueryContainer(this ISearchRequest searchRequest, IPredicate predicate)
{
if (predicate != null)
{
searchRequest.Query = predicate.GetQuery(searchRequest.Query);
}
return searchRequest;
}
至此我们的基础查询方法已经封装完成
然后通过 Nest 的进行查询即可
var response = ElasticClient.Search<T>(searchRequest);
具体演示代码(以项目的业务)
var elasticsearchPage = new ElasticsearchPage<Content>("content")
{
PageIndex = pageIndex,
PageSize = pageSize
};
#region terms 分组
var terms = new List<IFieldTerms>();
var classificationGroupBy = "searchKey_classification";
var brandGroupBy = "searchKey_brand";
#endregion
var searchRequest = elasticsearchPage.InitSearchRequest();
var predicateList = new List<IPredicate>();
//分类ID
if (requestContentDto.CategoryId != null)
predicateList.Add(Predicates.Field<Content>(x => x.ClassificationCode, ExpressOperator.Like,
requestContentDto.CategoryId));
else
terms.Add(Predicates.FieldTerms<Content>(x => x.ClassificationGroupBy, classificationGroupBy, 200));
//品牌
if (string.IsNullOrWhiteSpace(requestContentDto.Brand))
terms.Add(Predicates.FieldTerms<Content>(x => x.BrandGroupBy, brandGroupBy, 200));
//供应商名称
if (!string.IsNullOrWhiteSpace(requestContentDto.BaseType))
predicateList.Add(Predicates.Field<Content>(x => x.BaseType, ExpressOperator.Like,
requestContentDto.BaseType));
//是否自营
if (requestContentDto.IsSelfSupport == 1)
predicateList.Add(Predicates.Field<Content>(x => x.IsSelfSupport, ExpressOperator.Eq,
requestContentDto.IsSelfSupport));
//最大价格
if (requestContentDto.MaxPrice != null)
predicateList.Add(Predicates.Field<Content>(x => x.UnitPrice, ExpressOperator.Le,
requestContentDto.MaxPrice));
//最小价格
if (requestContentDto.MinPrice != null)
predicateList.Add(Predicates.Field<Content>(x => x.UnitPrice, ExpressOperator.Ge,
requestContentDto.MinPrice));
//关键词
if (!string.IsNullOrWhiteSpace(requestContentDto.SearchKey))
predicateList.Add(Predicates.Field<Content>(x => x.Title, ExpressOperator.Like,
requestContentDto.SearchKey));
//规整排序
var sortConfig = SortOrderRule(requestContentDto.SortKey);
var sorts = new List<ISort>
{
Predicates.Sort<Content>(sortConfig.Key, sortConfig.SortOrder)
};
var predicate = Predicates.Group(GroupOperator.And, predicateList.ToArray());
//构建或查询
var predicateListOr = new List<IPredicate>();
if (!string.IsNullOrWhiteSpace(requestContentDto.Brand))
{
var array = requestContentDto.Brand.Split(',').ToList();
predicateListOr
.AddRange(array.Select
(item => Predicates.Field<Content>(x => x.Brand, ExpressOperator.Like, item)));
}
var predicateOr = Predicates.Group(GroupOperator.Or, predicateListOr.ToArray());
var predicatecCombination = new List<IPredicate> {predicate, predicateOr};
var pgCombination = Predicates.Group(GroupOperator.And, predicatecCombination.ToArray());
searchRequest.InitQueryContainer(pgCombination)
.InitSort(sorts)
.InitHighlight(requestContentDto.HighlightConfigEntity)
.InitGroupBy(terms);
var data = _searchProvider.SearchPage(searchRequest);
#region terms 分组赋值
var classificationResponses = requestContentDto.CategoryId != null
? null
: data.Aggregations.Terms(classificationGroupBy).Buckets
.Select(x => new ClassificationResponse
{
Key = x.Key.ToString(),
DocCount = x.DocCount
}).ToList();
var brandResponses = !string.IsNullOrWhiteSpace(requestContentDto.Brand)
? null
: data.Aggregations.Terms(brandGroupBy).Buckets
.Select(x => new BrandResponse
{
Key = x.Key.ToString(),
DocCount = x.DocCount
}).ToList();
#endregion
//初始化
#region 高亮
var titlePropertySearchName = (PropertySearchNameAttribute)
LoadAttributeHelper.LoadAttributeByType<Content, PropertySearchNameAttribute>(x => x.Title);
var list = data.Hits.Select(c => new Content
{
Key = c.Source.Key,
Title = (string) c.Highlights.Highlight(c.Source.Title, titlePropertySearchName.Name),
ImgUrl = c.Source.ImgUrl,
BaseType = c.Source.BaseType,
BelongMemberName = c.Source.BelongMemberName,
Brand = c.Source.Brand,
Code = c.Source.Code,
BrandFirstLetters = c.Source.BrandFirstLetters,
ClassificationName = c.Source.ClassificationName,
ResourceStatus = c.Source.ResourceStatus,
BrandGroupBy = c.Source.BrandGroupBy,
ClassificationGroupBy = c.Source.ClassificationGroupBy,
ClassificationCode = c.Source.ClassificationCode,
IsSelfSupport = c.Source.IsSelfSupport,
UnitPrice = c.Source.UnitPrice
}).ToList();
#endregion
var contentResponse = new ContentResponse
{
Records = (int) data.Total,
PageIndex = elasticsearchPage.PageIndex,
PageSize = elasticsearchPage.PageSize,
Contents = list,
BrandResponses = brandResponses,
ClassificationResponses = classificationResponses
};
return contentResponse;
关于排序、group by 、 高亮 的具体实现不做说明 思路基本一致 可以参考git上面的代码。
源码
https://github.com/wulaiwei/WorkData.Core/tree/master/WorkData/WorkData.ElasticSearch
为什么要对 Nest 进行封装:
1、项目组不可能每个人都来熟悉一道 Nest的 api ,缩小上手难度
2、规范查询方式
以上是关于搜索引擎 ElasticSearch.NET 客户端封装的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch.Net.UnexpectedElasticsearchClientException:在从 ES 获取数据期间
如何从 elasticsearch.net / NEST 获取 geo_point 字段的距离
NEST/Elasticsearch.Net 发送原始JSON请求(Post数据)