当前位置: 首页 > news >正文

【Elasticsearch入门到落地】15、DSL排序、分页及高亮

接上篇《14、DSL复合查询》
上一篇我们讲解了DSL的复合查询,学习“相关性算分”、“FunctionScoreQuery”和“BooleanQuery”的相关内容。本篇我们来学习ElasticSearch的DSL搜索结果处理(排序、分页及高亮)。

一、引言

Elasticsearch作为一款强大的搜索引擎,其DSL(Domain Specific Language)提供了丰富的查询和结果处理能力。在实际应用中,我们经常需要对搜索结果进行排序、分页和高亮显示,以提升用户体验。本文将详细介绍如何使用Elasticsearch DSL对搜索结果进行处理,并结合酒店搜索的实际案例进行演示。

二、DSL搜索结果排序

1. 排序基础

Elasticsearch默认按照相关性得分(_score)降序排列结果。相关性得分是根据查询条件与文档匹配程度计算得出的(类似加了"sort":["_score"])。

{"query": {"match": {"name": "上海酒店"}}
}

查询结果:

对于不需要计算相关性的场景,可以使用_doc按索引顺序排序:

{"query": {"match": {"name": "上海酒店"}},"sort": ["_doc"]
}

查询结果:

2. 单字段与多字段排序

我们可以按照酒店价格升序排列:

{"query": {"match": {"city": "上海"}},"sort": [{"price": {"order": "asc"}}]
}

查询结果:

也可以按照评分降序、价格升序的多字段排序(按优先级逐级排序):

{"query": {"match": {"city": "上海"}},"sort": [{"score": {"order": "desc"}},{"price": {"order": "asc"}}]
}

查询结果:

这里大家可能会注意到,价格好像未严格按升序排列,第一个“上海虹桥金臣皇冠假日酒店”的价格2488元要比下面“上海南青华美达酒店”的299元高得多,尽管评分确实是升序的,这里需要解释一下:

(1)​​多字段排序的优先级规则​​

咱们的排序条件为score降序优先,price升序次之。这意味着:
​​第一优先级​​:所有文档先按score从高到低排序(48 > 47 > 46)
​​第二优先级​​:仅在score相同的文档组内,price才会按升序排列
从结果可见:
score=48的文档只有1条(价格2488),无需比较价格
score=47的文档组中,价格确实按升序排列:299 < 529 < 809 < 889 < 891 < 922 < 924
score=46的文档组中,价格也是升序:408 < 592

(2)​​跨分数段的价格比较无意义​​

不同score分段的文档之间​​不会​​比较价格。例如:
score=47组的最低价格是299,但score=46组的408比它高,这是允许的,因为排序逻辑是​​先分组再组内排序​​,而非全局统一排序。

​​3.地理坐标排序(_geo_distance)​

​​我们也可以按照地理位置距离进行排序,如按酒店距离用户当前位置(如上海中心大厦:31.2334°N,121.5050°E)由近到远排序,并限制距离在10公里内。DSL查询​​(假设hotel索引的location字段为geo_point类型):

{"query": {"bool": {"filter": {"geo_distance": {"distance": "10km","location": {  "lat": 31.2334,"lon": 121.5050}}}}},"sort": [{"_geo_distance": {"location": {  // 目标坐标"lat": 31.2334,"lon": 121.5050},"order": "asc",  // 升序(由近到远)"unit": "km",    // 距离单位"mode": "min"    // 若location为多值,取最近距离排序}}]
}

​​响应关键字段​​:
hits.hits.sort:包含文档到目标点的实际距离(单位:km)。
若距离超出10km的文档会被过滤掉(因geo_distance过滤条件)。
查询结果:

4. 高级排序:脚本排序(script)​

对于更复杂的排序需求,可以使用脚本排序。例如,按照价格与评分的乘积排序:

{"query": {"match": {"city": "上海"}},"sort": {"_script": {"type": "number","script": {"lang": "painless","source": "doc['price'].value * doc['score'].value"},"order": "desc"}}
}

5. 应用场景

在酒店搜索中,常见的排序场景包括:
按价格排序(升序或降序)
按评分排序(高评分优先)
按距离排序(结合地理位置)
综合排序(如评分+价格的加权排序)

6.注意事项

​​(1)字段类型限制​​

排序字段必须为keyword、数值(numeric)或日期(date)类型,避免直接对text类型字段排序(因其分词后无法直接排序)若需对文本内容排序,应使用.keyword子字段。
​​示例错误场景​​:
"sort": [{ "name": "asc" }]  // 错误:name是text类型
修正为:
"sort": [{ "name.keyword": "asc" }]  // 使用keyword子字段

​​(2)性能优化​​

Elasticsearch默认对非文本字段启用doc_values(列式存储结构),加速排序和聚合操作。但需注意:避免对高基数(唯一值多)字段排序,可能增加内存和磁盘压力。可通过"docvalue_fields": ["price"]显式指定从列存读取字段值,减少序列化开销。

三、DSL搜索结果分页

1. 基础分页

Elasticsearch使用from和size参数实现分页:

{"query": {"match": {"city": "上海"}},"from": 0,"size": 10,"sort": [{"price": {"order": "asc"}}]
}

from: 起始位置(从0开始)
size: 每页大小

2. 深度分页问题与解决方案

当from值很大时(如超过10000),会出现性能问题。这是因为Elasticsearch需要将所有匹配的文档排序后才能确定哪些文档应该返回。
解决方案:

(1)使用search_after​​:

{"query": {"match": {"city": "上海"}},"size": 10,"sort": [{"price": {"order": "asc"}}],"search_after": [336]
}

查询结果:

search_after参数指定上一页最后一条记录的排序字段值。

​​(2)使用Scroll API​​:

对于大量数据导出场景,可以使用Scroll API:
初始化 Scroll 请求

GET /hotel/_search?scroll=5m
{"query": {"match": {"city": "上海"}},"size": 100,"sort": ["_doc"]
}

scroll=5m意思是初始Scroll请求设置的有效期5分钟。
查询结果:

然后通过scroll_id获取后续批次:

POST /_search/scroll
{"scroll": "5m",  // 每次请求需刷新超时时间"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABCzA..."
}

查询结果:

3. 实践建议

避免深度分页,尽量限制用户翻页深度
对于后台导出等场景,使用Scroll API
前端实现"加载更多"功能时,使用search_after

四、DSL搜索结果高亮

1. 高亮原理

高亮功能可以将匹配的搜索词用HTML标签包裹,便于前端展示。

2. 实现方法

基本高亮查询:

{"query": {"match": {"name": "万豪"}},"highlight": {"fields": {"name": {}}}
}

查询结果:

HTML显示效果:

自定义高亮标签:

{"query": {"match": {"name": "万豪"}},"highlight": {"pre_tags": ["<strong>"],"post_tags": ["</strong>"],"fields": {"name": {}}}
}

查询结果:

HTML显示效果:

3. 优化技巧

确保高亮字段与搜索字段一致
对于大文本字段,可以使用fragment_size控制高亮片段长度
可以使用number_of_fragments限制返回的高亮片段数量

五、综合案例演示

1. 酒店搜索场景

假设我们需要实现以下搜索需求:
●搜索上海的酒店
●按价格升序排列
●分页显示,每页10条
●高亮显示酒店名称中的搜索词

2. 代码示例

{"query": {"bool": {"must": [{"match": {"city": "上海"}}],"filter": [{"range": {"price": {"gte": 300,"lte": 1000}}}]}},"from": 0,"size": 10,"sort": [{"price": {"order": "asc"}}],"highlight": {"fields": {"name": {}}}
}

注:Elasticsearch高亮默认仅对​​搜索字段​​(即query中指定的city字段)生效。虽然我们在highlight.fields中指定了name,但未在查询条件中匹配该字段,因此不会触发高亮。所以这里我们添加require_field_match: false参数,允许高亮非搜索字段。
查询结果:

以上为DSL查询结果的排序、分页及高亮内容的总结。下一篇我们来学习使用Java编程语言操作RestClient进行文档的查询。

转载请注明出处:https://blog.csdn.net/acmman/article/details/149156295

http://www.dtcms.com/a/268357.html

相关文章:

  • golang 协程 如何中断和恢复
  • WHAT - 依赖管理工具 CocoaPods
  • 从小白到进阶:解锁linux与c语言高级编程知识点嵌入式开发的任督二脉(1)
  • 如何确保Kafka集群的高可用?
  • 【MySQL】DTS机制对触发器时间的影响
  • Python-可视化学习笔记
  • 【机器学习笔记Ⅰ】3 代价函数
  • 空调和烘干机的使用
  • pyhton基础【23】面向对象进阶四
  • 爬虫的笔记整理
  • 在Ubuntu 24.04上部署Zabbix 7.0对服务器进行监控
  • Grok 4 最新技术评测与发布指南
  • 位置编码和RoPE
  • 光纤的最小弯曲半径是多少?
  • 商业秘密攻防战:技术信息与经营信息的界定之道
  • 基于Flask和机器学习开发的米其林餐厅数据可视化平台
  • 爬虫-request模块使用
  • CSS05:结构伪类选择器和属性选择器
  • 反向遍历--当你修改一个元素的outerHTML时,该元素会被从 DOM 中移除
  • 大模型RLHF中PPO强化学习代码学习笔记(二)
  • 回环检测 Scan Contex
  • DolphinScheduler 3.2.0 后端开发环境搭建指南
  • XML 笔记
  • 极简的神经网络反向传播例子
  • 用户中心Vue3项目开发2.0
  • Docker 容器编排原理与使用详解
  • 125.【C语言】数据结构之归并排序递归解法
  • FileZilla二次开发实战指南:C++架构解析与界面功能扩展
  • 操作系统王道考研习题
  • 76、覆盖最小子串