Elasticsearch之排序解析(十二)
在默认情况下,ES对搜索结果是按照相关性降序排序的。有时需要按照某些字段的值进行升序或者降序排序。ES提供了sort子句可以对数据进行排序。使用sort子句一般是按照字段信息进行排序,不受相关性影响,而且打分步骤需要耗费一定的硬件资源和时间,因此默认情况下,不对文档进行打分。使用sort排序分为两种类别,一种是按照字段值的大小进行排序,另一种是按照给定地理坐标的距离远近进行排序。...
在默认情况下,ES对搜索结果是按照相关性降序排序的。有时需要按照某些字段的值进行升序或者降序排序。
ES提供了sort子句可以对数据进行排序。使用sort子句一般是按照字段信息进行排序,不受相关性影响,而且打分步骤需要耗费一定的硬件资源和时间,因此默认情况下,不对文档进行打分。使用sort排序分为两种类别,一种是按照字段值的大小进行排序,另一种是按照给定地理坐标的距离远近进行排序。
按普通字段值排序
使用sort子句对字段值进行排序时需要指定排序的字段。ES默认是按照字段值进行升序排序,可以设置order参数为asc或desc,指定按照字段值进行升序或者降序排序。以下示例为搜索名称包含“北京”的旅馆,并对旅馆按照价格进行降序排列:
GET /hotel/_search
{
"query": {
"match": {
"title": "北京"
}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
使用sort对搜索结果排序后,在每个文档的_source信息下面多出了一个sort信息,该信息中显示了当前文档排序字段的值。另外,文档的_score值和max_score都为null,这说明在默认情况下ES查询时使用sort对结果排序是不计算分数的。也可以使用sort对搜索结果按照多个字段进行排序。例如,用户可以按照价格进行降序排列,然后再按照口碑值进行降序排列,对应的DSL如下:
GET /hotel/_search
{
"query": {
"match": {
"title": "北京"
}
},
"sort": [
{
"price": {
"order": "desc"
},
"praise": {
"order": "desc"
}
}
]
}
在Java客户端中对搜索结果进行排序时,可以一次或者多次调用searchSourceBuilder.sort()方法添加一个或多个排序条件。对应上面的排序DSL,Java代码如下:
@Test
public void testNormalFieldSort() throws IOException {
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(RestClient.builder(Arrays.stream("127.0.0.1:9200".split(","))
.map(host->{
String[] split = host.split(":");
String hostName = split[0];
int port = Integer.parseInt(split[1]);
return new HttpHost(hostName,port,HttpHost.DEFAULT_SCHEME_NAME);
}).filter(Objects::nonNull).toArray(HttpHost[]::new)));
SearchRequest request = new SearchRequest("hotel");
request.source(new SearchSourceBuilder().query(QueryBuilders.matchQuery("title","北京")).sort("price", SortOrder.DESC)
.sort("praise",SortOrder.DESC));
SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit:searchResponse.getHits()) {
System.out.println(hit.getSourceAsString()+" "+hit.getScore());
}
}
按地理距离排序
使用geo_distance查询,配合sort可以指定另一种排序规则,即按照文档坐标与指定坐标的距离对结果进行排序。使用时,需要在sort内部指定排序名称为geo_distanc,并指定目的地坐标。除了可以指定升序或者降序排列外,还可以指定排序结果中sort子句中的距离的计量单位,默认值为km即千米。在进行距离计算时,系统默认使用的算法为arc,该算法的特点是计算精准但是耗费时间较长,用户可以使用distance_type参数选择另一种计算速度快但经度略差的算法,名称为plane。如下示例使用geo_distance查询天安门5km范围内的旅馆,并按照距离由近及远进行排序:
GET /hotel/_search
{
"query": {
"geo_distance":{
"distance":"5km",
"location":{
"lat":39.915143,
"lon":116.4039
}
}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat":39.915143,
"lon":116.4039
},
"order": "asc",
"unit": "km",
"distance_type": "plane"
}
}
]
}
在Java客户端中对geo_distance的搜索结果进行排序时,可以调用SortBuilders.geo DistanceSort()方法新建geo_distance查询对象的实例,然后将该实例传给searchSource Builder.sort()方法即可完成按照距离排序的要求。对应上面的排序DSL,Java代码如下:
@Test
public void testGeoSort() throws IOException {
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(RestClient.builder(Arrays.stream("127.0.0.1:9200".split(","))
.map(host->{
String[] split = host.split(":");
String hostName = split[0];
int port = Integer.parseInt(split[1]);
return new HttpHost(hostName,port,HttpHost.DEFAULT_SCHEME_NAME);
}).filter(Objects::nonNull).toArray(HttpHost[]::new)));
SearchRequest request = new SearchRequest("hotel");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(QueryBuilders.geoDistanceQuery("location").distance(5, DistanceUnit.KILOMETERS)
.point(39.915143, 116.4039));
GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort("location", 39.915143, 116.4039).point(39.915143, 116.4039).unit(DistanceUnit.KILOMETERS);
searchSourceBuilder.sort(geoDistanceSortBuilder);
SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
for (SearchHit hit:searchResponse.getHits()) {
System.out.println(hit.getSourceAsString()+" "+hit.getScore());
}
}
在实际开发过程中,往往需要先写出符合需求的查询的DSL,然后对照DSL进行Java编码,最后再通过对比两方的结果是否一致来判定程序正确与否。
更多推荐
所有评论(0)