Elasticsearch 搜索(上)
Elasticsearch 搜索(上)
搜索简介
Elasticsearch 支持多种搜索:
- 精确搜索(词项搜索):搜索数值、日期、IP 或字符串的精确值或范围。
- 全文搜索:搜索非结构化文本数据并查找与查询项最匹配的文档。
- 向量搜索:存储向量,并使用 ANN 或 KNN 搜索来查找相似的向量,从而支持 语义搜索 等场景。
可以使用 _search API
来搜索和聚合 Elasticsearch 数据流或索引中的数据。API 的 query
请求采用 DSL 语义来进行查询。
Elasticsearch 支持两种搜索方式:URI Query 和 Request Body Query(DSL)
::: details URI Query 示例
1 | GET /kibana_sample_data_ecommerce/_search?q=customer_first_name:Eddie |
:::
::: details Request Body Query(DSL)示例
1 | POST /kibana_sample_data_ecommerce/_search |
:::
当文档存储在 Elasticsearch 中时,它会在 1 秒内近乎实时地被索引和完全搜索。
Elasticsearch 基于 Lucene 开发,并引入了分段搜索的概念。分段类似于倒排索引,但 Lucene 中的单词 index
表示“段的集合加上提交点”。提交后,将向提交点添加新分段并清除缓冲区。
位于 Elasticsearch 和磁盘之间的是文件系统缓存。内存中索引缓冲区的文档会被写入新的分段,然后写入文件系统缓存,然后才刷新到磁盘。
Lucene 允许写入和打开新分段,使其包含的文档对搜索可见,而无需执行完全提交。这是一个比提交到磁盘要轻松得多的过程,并且可以频繁地完成而不会降低性能。
在 Elasticsearch 中,写入和打开新分段的这一过程称为刷新。刷新使自上次刷新以来对索引执行的所有操作都可用于搜索。
默认情况下,Elasticsearch 每秒定期刷新一次索引,但仅限于在过去 30 秒内收到一个或多个搜索请求的索引。这就是我们说 Elasticsearch 具有近实时搜索能力的原因:文档更改不会立即对搜索可见,但会在此时间范围内变得可见。
排序
在 Elasticsearch 中,默认排序是按照相关性的评分(_score
)进行降序排序。_score
是浮点数类型,_score
评分越高,相关性越高。评分模型的选择可以通过 similarity
参数在映射中指定。
在 5.4 版本以前,默认的相关性算法是 TF-IDF。TF 是词频(term frequency),IDF 是逆文档频率(inverse document frequency)。一个简短的解释是,一个词条出现在某个文档中的次数越多,它就越相关;但是,如果该词条出现在不同的文档的次数越多,它就越不相关。5.4 版本以后,默认的相关性算法 BM25。
此外,也可以通过 sort
自定排序规则,如:按照字段的值排序、多级排序、多值字段排序、基于 geo(地理位置)排序以及自定义脚本排序。
::: details 排序示例
单字段排序
1 | POST /kibana_sample_data_ecommerce/_search |
多字段排序
1 | POST /kibana_sample_data_ecommerce/_search |
:::
详情参考:Sort search results
分页
默认情况下,Elasticsearch 搜索会返回前 10 个匹配的匹配项。
Elasticsearch 支持三种分页查询方式。
- from + size
- search after
- scroll
from + size
可以使用 from
和 size
参数分别指定起始页和每页记录数。
当一个查询:from = 990, size = 10,会在每个分片上先获取 1000 个文档。然后,通过协调节点聚合所有结果。最后,再通过排序选取前 1000 个文档。
页数越深,占用内存越多。为了避免深分页问题,ES 默认限定最多搜索 10000 个文档,可以通过 index.max_result_window
进行设置。
::: details from + size 分页查询示例
1 | POST /kibana_sample_data_ecommerce/_search |
:::
scroll
scroll 搜索方式类似于 RDBMS 中的游标,只允许向下翻页。每次下一页查询后,使用返回结果的 scroll id 来作为下一次翻页的标记。
scroll 在搜索初始化阶段会生成快照,后续数据的变化无法及时体现在查询结果,因此更加适合一次性批量查询或非实时数据的分页查询。
启用游标查询时,需要注意设定期望的过期时间(scroll = 1m),以降低维持游标查询窗口所需消耗的资源。
注意:Elasticsearch 官方不再建议使用 scroll 查询方式进行深分页,而是推荐使用
search_after
和时间点(PIT)一起使用。
::: details scroll 分页查询示例
1 | POST /kibana_sample_data_ecommerce/_search?scroll=1m |
响应结果
1 | { |
:::
search after
search after 搜索方式不支持指定页数,只能向下翻页;并且需要指定 sort,并保证值是唯一的。然后,可以反复使用上次结果中最后一个文档的 sort 值进行查询。
search after 实现的思路同 scroll 方式基本一致,通过记录上一次分页的位置标识,来进行下一次分页查询。相比于 scroll 方式,它的优点是可以实时获取数据的变化,解决了查询快照导致的查询结果延迟问题。
::: details search after 分页查询示例
第一次查询
1 |
|
响应结果
1 | { |
从上次查询的响应中获取 sort
值,然后将 sort 值插入 search after 数组:
1 | POST /kibana_sample_data_ecommerce/_search |
:::
限定字段
默认情况下,搜索响应中的每个点击都包含 _source
,该字段保存了原始文本的 JSON 对象。有两种推荐的方法可以从搜索查询中检索所选字段:
折叠搜索结果
Elasticsearch 中,可以通过 collapse 对搜索结果进行分组,且每个分组只显示该分组的一个代表文档。
::: details collapse 查询示例
1 | POST /kibana_sample_data_ecommerce/_search |
响应结果:
1 | { |
:::
过滤搜索结果
使用带有 filter
子句的布尔查询,可以过滤搜索和聚合的结果。
使用 post_filter
可以过滤搜索的结果,但不能过滤聚合结果。
:::details filter 示例
1 | POST /kibana_sample_data_ecommerce/_search |
:::
高亮
Elasticsearch 的高亮(highlight)可以从搜索结果中的一个或多个字段中获取突出显示的摘要,以便向用户显示查询匹配的位置。当请求突出显示(即高亮)时,响应结果的 highlight
字段中包括高亮的字段和高亮的片段。Elasticsearch 默认会用 <em></em>
标签标记关键字。
Elasticsearch 提供了三种高亮器,分别是默认的 highlighter 高亮器、postings-highlighter 高亮器 和 fast-vector-highlighter 高亮器。
::: details 高亮结果示例
1 | POST /kibana_sample_data_ecommerce/_search |
:::
详情参考:Highlighting
分片路由搜索
Elasticsearch 可以在多个节点上的多个分片中存储索引数据的副本。在运行搜索请求时,Elasticsearch 会选择包含索引数据副本的节点,并将搜索请求转发到该节点的分片。此过程称为路由。
默认情况下,Elasticsearch 使用自适应副本选择来路由搜索请求。默认情况下,自适应副本选择从所有符合条件的节点和分片中进行选择。如果要限制符合搜索请求条件的节点和分片集,可以使用 preference
查询参数。
详情参考:Search shard routing
查询规则
Elasticsearch 允许自定义查询规则来进行搜索。
搜索模板
搜索模板是可以使用不同变量运行的存储搜索。
详情参考:Search templates