Elasticsearch缺失字段排序和自定义排序
Elasticsearch 字段缺失排序和自定义排序
例题1
现有需求需按照有vr图
的数据排在最前,再后面是有图片
的数据,最后是既没有vr图也没有图片的数据
先使用ES的字段缺失排序实现
{
"from": 0,
"size": 10,
"query": {
"bool": {
"filter": [
{
"term": {
"city_id": {
"value": 11,
"boost": 1.0
}
}
}
],
"adjust_pure_negative": true,
"boost": 1.0
}
},
"_source": {
"includes": [
"id",
"name",
"area",
"addr",
"useage",
"price",
"thumb_url",
"vr_cover",
"vr_house_id",
"vr_url"
],
"excludes": []
},
"sort": [
{
"vr_url": {
"missing": "_last"
}
},
{
"thumb_url": {
"missing": "_last"
}
}
],
"track_total_hits": true //数据超过1w条时设置为true
}
用以上json查询出的数据就是无论升序还是降序都可将有vr图的放在最前,再是有图片的数据,既没有vr也没有图片的数据则排在最后。
对应的Java代码是
FieldSortBuilder vrSortBuilder = SortBuilders
.fieldSort("vr_url");
FieldSortBuilder imgSortBuilder = SortBuilders
.fieldSort("thumb_url");
vrSortBuilder.missing("_last");
imgSortBuilder.missing("_last");
SearchSourceBuilder searchSourceBuilder.sort(vrSortBuilder)//先按vr排序
.sort(imgSortBuilder)//再按图片排序
同理,既然可以将缺失值排在最后也可以将缺失值排在最前,只需将_last
换成_first
即可
例题2
现有需求:需从一个索引中获取数据,排序时无论升序或降序,都需要将价格为null
和0
的数据排至最后。
此时,例题1中的方法已经无法完成此需求了,因为使用ES提供的字段缺失排序时,字段为0代表该数据是有值的,所以字段值为0的数据也会参与排序。故在此引入一种自定义排序方式。
先解决在升序时的排序方式:
{
"from": 0,
"size": 10,
"query": {
"bool": {
"filter": [
{
"term": {
"city_id": {
"value": 11,
"boost": 1.0
}
}
}
],
"adjust_pure_negative": true,
"boost": 1.0
}
},
"_source": {
"includes": [
"id",
"name",
"area",
"addr",
"useage",
"price",
"thumb_url",
"vr_cover",
"vr_house_id",
"vr_url"
],
"excludes": []
},
"sort": [
{
"_script": {
"script": {
"source": "if(doc['price'].size()<=0){return factor;}else if(doc['price'].size()>0){if(Double.parseDouble(doc['price'].value)==0){return factor}else if(Double.parseDouble(doc['price'].value)>0){return Double.parseDouble(doc['price'].value)}}",
"lang": "painless",
"params": {
"factor": 10000000
}
},
"type": "number",
"order": "asc"
}
}
]
}
此排序方式最重要的点在于自定义,而实现自定义的核心在于下面这一段
{
"source":
"if(doc['price'].size()<=0){ //若price字段值为null
return factor; //使用一个尽可能大的数据代替null来参与排序,此处factor为下面params中的参数,可实现动态变化
}else if(doc['price'].size()>0){ //若price字段值不为null
if(Double.parseDouble(doc['price'].value)==0){ //若price字段的值为0
return factor; //使用一个尽可能大的数据代替0参与排序
}else if(Double.parseDouble(doc['price'].value)>0){ //若price字段的值不为0
return Double.parseDouble(doc['price'].value); //使用price字段原本的值参与排序
}
}",
"lang": "painless",
"params": {
"factor": 10000000
}
}
此时解决了升序时的排序方法开始考虑降序时的排序:
{
"from": 0,
"size": 10,
"query": {
"bool": {
"filter": [
{
"term": {
"city_id": {
"value": 11,
"boost": 1.0
}
}
}
],
"adjust_pure_negative": true,
"boost": 1.0
}
},
"_source": {
"includes": [
"id",
"name",
"area",
"addr",
"useage",
"price",
"thumb_url",
"vr_cover",
"vr_house_id",
"vr_url"
],
"excludes": []
},
"sort": [
{
"_script": {
"script": {
"source": "if(doc['price'].size()<=0){return 0;}else if(doc['price'].size()>0){return Double.parseDouble(doc['price'].value);}",
"lang": "painless"
},
"type": "number",
"order": "desc"
}
}
]
}
此段核心则在于
if(doc['price'].size()<=0){ //若price字段的值为null
return 0; //使用0代替null参与排序
}else if(doc['price'].size()>0){ //若price字段的值不为null
return Double.parseDouble(doc['price'].value); //使用price原本的值排序
}
Java代码实现
Map params = new HashMap<>();
String scriptStr = "";
SearchSourceBuilder searchSourceBuilder = null;
ScriptSortBuilder guideSort = null;
if("asc".equals(OrderDirection)){
scriptStr = "if(doc['guide_price'].size()<=0){return 10000000;}else if(doc['guide_price'].size()>0) {if(Double.parseDouble(doc['guide_price'].value)==0){return 10000000}else if(Double.parseDouble(doc['guide_price'].value)>0){return Double.parseDouble(doc['guide_price'].value)}}";
Script script = new Script(ScriptType.INLINE,"painless",scriptStr , params);
guideSort = new ScriptSortBuilder(script, ScriptSortBuilder.ScriptSortType.NUMBER);
searchSourceBuilder.sort(guideSort);
}else if("desc".equals(OrderDirection)){
scriptStr = "if(doc['guide_price'].size()<=0){return 0;}else if(doc['guide_price'].size()>0){return Double.parseDouble(doc['guide_price'].value);}";
Script script = new Script(ScriptType.INLINE,"painless",scriptStr , params);
guideSort = new ScriptSortBuilder(script, ScriptSortBuilder.ScriptSortType.NUMBER);
searchSourceBuilder.sort(guideSort);
}
有了自定义排序,es则可以实现字段权重排序等大部分不同规则的定制排序。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)