一、排序
elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
1.1.语法说明:
对结果的排序语法如下:
GET /indexname/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": {
"FIELD": "desc" //排序字段和排序方式ASC、DESC
}
}
]
}
1.2.案例一:
对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序,评价是score字段,价格是price字段,按照顺序添加两个排序规则即可。
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": "desc"
},
{
"price": "asc"
}
]
}
执行后结果如下:按照评分的高低排序,如果评分相同,在按照价格升序排序。
1.3.案例二:
实现对酒店数据按照到你的位置坐标的距离升序排序,获取经纬度的方式: https://jingweidu.bmcx.com/,这里以虹桥机场的经纬度为坐标,
代码如下:
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 31.196955,
"lon": 121.339752
},
"order": "asc",
"unit": "km"
}
}
]
}
执行后结果如下:
二、分页
ES中分页跟MySQL很像,但是它的底层原理,有很大的差别,ES底层采用倒排索引,它的结构是不利于做分页的,实际上采用的是逻辑上的分页,比如查990到1000的数据,对ES来讲它或查询0到1000的所有数据,然后去截取990-1000的部分
这种截取在单点查询的时候没有问题,但是ES为了存储更多的数据,ES做了集群。
2.1.语法说明
elasticsearch默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。
elasticsearch中通过修改from、size参数来控制要返回的分页结果:
GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 990, //分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
ES做了集群,ES把数据进行了拆分放到不同的机器上,拆分出来的每一分叫做一个分片,每一个分片上的数据是不一样的,要查询990-1000的数据,就不知道是哪一个分片的前1000条数据了
2.2.深度分页问题
ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from=990,size=10的数据:
- 首先在每个数据分片上都排序并查询前1000条文档。
- 然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档
- 最后从这1000条中,选取从990开始的10条文档
如果搜索页数过深,或者结果集(from+size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000
2.3.深度分页解决方案
针对深度分页,ES提供了两种解决方案,宜方文档:
- search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
- scroll:原理将排序数据形成快照,保存在内存。官方已经不推荐使用。
注意:
- search after:搜索是必须先排序,比如说按照价格排序,排序后就有了顺序,比如说先查询0-10条数据,第二次查询接下来10条的时候,会记录第十条排序的价格值,再次查询的时候可以从这个价格值往后去查询 有一个缺点,知道了上一次分页查询的最后一条数据值,往后查,不能够往前查询,数据只能往后分页,不能往前分页
- scoll:查询方式是一个快照,第一次查询的时候,我把所有的数据都给你缓存起来,将来你要查询的时候,我再给你截取你需要的部分,这种方式对内存的消耗方式是非常大的不推荐使用 它形成了快照,当你更新了一条文档数据,你这条快照会更新吗,查询到的数据是旧的数据,没有办法查询到实时的数据
2.4.案例说明:
按照价格price字段,安装asc生效排序分页,显示第一页10条数据如下:
2.5.总结
1)from+size:
- 优点:支持随机翻页
- 缺点:深度分页问题,默认查询上限(from+size)是10000
- 场景:百度、京东、谷歌、淘宝这样的随机翻页搜索
2)after search:
- 优点:没有查询上限(单次查询的size不超过10000)
- 缺点:只能向后逐页查询,不支持随机翻页
- 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页
3)scroll:
- 优点:没有查询上限(单次查询的size不超过10000)
- 缺点:会有额外内存消耗,并且搜索结果是非实时的
- 场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用after search方案。
三、高亮
3.1.高亮原理说明
高亮,就是在搜索结果中把搜索关键字突出显示。原理如下:
- 将搜索结果中的关键字用标签标记出来
- 在页面中给标签添加css样式
以百度搜索如下:
3.2.语法说明
GET /hotel/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
},
"highlight": {
"fields": { //指定要高亮的字段
"FIELD":{
"pre_tags": "<em>",//用来标记高亮字段的前置标签
"post_tags": "</em>"//用来标记高亮字段的后置标签
}
}
}
}
注意:
- pre_tag、post_tags如果不写默认是添加em标签
- ES搜索的字段必须与高亮的字段一致,如果不一致,需要添加require_field_match,默认是高亮字段和搜索字段不匹配
3.3.案例
根据我们创建all字段搜索,而高亮字段是name字段,这时候搜索的字段和高亮的字段不一致,所以需要设置参数"require_field_match"为 "false",默认就会给添加标签<em>,就没在写pre_tag、post_tags
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
},
"highlight": {
"fields": {
"name": {"require_field_match": "false"}
}
}
}
执行后如下:
四、搜索结果整体处理
结合排序、分页和高亮对于结果进行处理,后续可以按照这个案例使用:
GET /hotel/_search
{
"query": {
"match": {
"name": "如家"
}
},
"from": 0,//分页开始的位置
"size": 20,//期望获取的文档总数
"sort": [
{"price": "desc"},//普通排序,升序
{"_geo_distance": {//按照距离排序
"location": {
"lat": 31.196955,
"lon": 121.339752
},
"order": "asc",
"unit": "km"
}}
],
"highlight": {
"fields": {//高亮字段
"name": {
"pre_tags": "<em>",//用来标记高亮字段的前置标签
"post_tags": "</em>"//用来标记高亮字段的后置标签
}
}
}
}
执行后结果如下: