Elastic Search學習-Sort篇

ES SORT篇

最近接手的項目,搜素有用到ES,經常也需要寫一些ES相關的搜索。但是一般,都是比較簡單的,搜索某幾個字段,過濾一下,然後按照某個字段排個序。

{
    "from":0,
    "size":20,
    "query":{
        "bool":{
            "filter":[
                {
                    "term":{
                        "userId":{
                            "value":96315,
                            "boost":1
                        }
                    }
                },
                {
                    "term":{
                        "isExcellent":{
                            "value":1,
                            "boost":1
                        }
                    }
                }
            ],
            "adjust_pure_negative":true,
            "boost":1
        }
    },
    "sort":{
        "area":{
            "order":"desc"
        },
        "roomNumber":{
            "order":"asc"
        }
    }
}

上面是一個非常簡單的例子,翻譯成SQL就是

SELECT * 
FROM XXX 
WHERE user_id = 96315 
    AND is_excellent = 1 
LIMIT 0, 20 
ORDER BY area DESC, room_number ASC

但是這時候產品說:我們做“簡單一點”,我只需要按照面積排序就行了,但是排序規則要改一下。我希望面積越接近100平方越好。這個時候有點犯難了,大概應該就是需要ORDER BY Math.abs(area - 100) ASC

然後,我翻了好久的官方文檔,終於讓我找到了script這玩意。看起來好像可以實現我想要的能力。折騰了很久,終於寫出了對應的查詢語句:

{
    "from":0,
    "size":20,
    "query":{
        "bool":{
            "filter":[
                {
                    "term":{
                        "userId":{
                            "value":96315,
                            "boost":1
                        }
                    }
                },
                {
                    "term":{
                        "isExcellent":{
                            "value":1,
                            "boost":1
                        }
                    }
                }
            ],
            "adjust_pure_negative":true,
            "boost":1
        }
    },
    "sort":[
        {
            "_script":{
                "script":{
                    "source":"return Math.abs(doc['area'].value - params.areaParam)",
                    "lang":"painless",
                    "params":{
                        "areaParam":100
                    }
                },
                "type":"number",
                "order":"asc"
            }
        }
    ]
}

這裏,我將參數放在了params裏面,而不是寫死,就可以很方便地替換成其他值傳進去。而這也是官方推薦的做法,官方對script的params有一段說明:
ES官網script params說明
在這裏插入圖片描述
大概意思是說,如果你的script裏面包含一個可變參數的話,請一定要把它放到params裏面去。不然,你每次改變這個參數,ES服務器都需要重新編譯一遍。而編譯的代價是很大的,甚至如果你的請求過於頻繁ES會拒絕執行這個script。
最後上一段Java的代碼實現:

private static final String SORT_SCRIPT = "return Math.abs(doc['area'].value - params.areaParam)";

private static final String SCRIPT_PARAM_FIELD = "areaParam";

private static final String[] BASE_FIELDS = new String[]{EsConstants.HOURSE_ID, EsConstants.HOURSE_NAME};


public List<HourseData> queryHourseList(long userId, int start, int size) {
	final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
	final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
	                .filter(QueryBuilders.termQuery(EsConstants.USER_ID, userId))
	                .filter(QueryBuilders.termQuery(EsConstants.IS_EXCELLENT, 1))
	searchSourceBuilder.query(queryBuilder);
	Map<String, Object> params = Maps.newHashMap();
	params.put(SCRIPT_PARAM_FIELD, area);
	Script script = new Script(Script.DEFAULT_SCRIPT_TYPE, 
			Script.DEFAULT_SCRIPT_LANG, 
			SORT_SCRIPT,
	        Collections.emptyMap(), 
	        params);

	ScriptSortBuilder scriptSortBuilder = SortBuilders.scriptSort(script,
	        ScriptSortBuilder.ScriptSortType.NUMBER);
	searchSourceBuilder.sort(scriptSortBuilder);
	searchSourceBuilder.from(start);
	searchSourceBuilder.size(size);
	searchSourceBuilder.fetchSource(BASE_FIELDSS, null);
	final SearchRequest searchRequest = new SearchRequest(EsConstants.INDEX_NAME);
	searchRequest.source(searchSourceBuilder);

	final SearchResponse response;
	try {
	    response = upEsClient.search(searchRequest);
	} catch (final IOException e) {
	    return ListResult.empty();
	}
	final SearchHits hits = response.getHits();
	final int totalHits = (int) hits.getTotalHits();
	if (totalHits == 0) {
	    return Collections.emptyList();
	}
	.....
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章