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();
	}
	.....
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章