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