当前位置: 首页 > 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.lryc.cn/news/581510.html

相关文章:

  • 【HarmonyOS】鸿蒙应用开发Text控件常见错误
  • 深入解析Vue中v-model的双向绑定实现原理
  • D3 面试题100道之(61-80)
  • Qt实现外网双向音视频通话/支持嵌入式板子/实时性好延迟低/可以加水印
  • C++基础复习笔记
  • 【网络系列】HTTP 429 状态码
  • Debezium日常分享系列之:认识Debezium Operator
  • Go语言实现双Token登录的思路与实现
  • UNIX程序设计基本概念和术语
  • 玄机——第一章日志分析-mysql应急响应
  • docker 无法拉取镜像解决方法
  • 系统架构设计师论文分享-论软件体系结构的演化
  • Apache Iceberg数据湖基础
  • 极简的神经网络反向传播例子
  • 探寻《答案之书》:在随机中寻找生活的指引
  • 5种高效解决Maven依赖冲突的方法
  • Golang读取ZIP压缩包并显示Gin静态html网站
  • c++对象池
  • 数据库|达梦DM数据库安装步骤
  • [论文阅读] 人工智能 + 软件工程 | 自然语言驱动结构代码搜索:突破DSL学习壁垒的创新方法
  • 分布式压测
  • python高级变量XIII
  • jenkins安装
  • 分布式事务解决方案(二)
  • 探索实现C++ STL容器适配器:优先队列priority_queue
  • react当中的this指向
  • (C++)学生管理系统(正式版)(map数组的应用)(string应用)(引用)(文件储存的应用)(C++教学)(C++项目)
  • .NET9 实现字符串拼接(StringConcatenation)性能测试
  • 深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
  • jmm,`as - if - serial` 与 `happens - before` 原则