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則可以實現字段權重排序等大部分不同規則的定制排序。