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

現有需求:需從一個索引中獲取數據,排序時無論升序或降序,都需要將價格為null0的數據排至最后。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM