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();
}
.....
}