Elasticsearch:使用 pipelines 路由文档到想要的 Elasticsearch 索引中去
liuxg 发表了文章 • 0 个评论 • 3888 次浏览 • 2023-02-23 10:33
原文地址 [elasticstack.blog.csdn.net](https://elasticstack.blog.csdn ... 174215)
![](https://img-blog.csdnimg.cn/2d ... c.jpeg)
路由文件
====
当应用程序需要向 Elasticsearch 添加文档时,它们首先要知道目标索引是什么。在很多的应用案例中,特别是针对时序数据,我们想把每个月的数据写入到一个特定的索引中。一方面便于管理索引,另外一方面在将来搜索的时候可以按照每个月的索引来进行搜索,这样速度更快,更便捷。
当你处于某种类型的文档总是转到特定索引的琐碎情况时,这似乎很明显,但当你的索引名称可能根据杂项参数(无论它们是否在你的系统外部 - 当前例如日期 - 或者你尝试存储的文档的固有属性 - 大多数时候是文档字段之一的值)。
当发生最后一种情况时(我们指的是索引名称可以变化的情况),在向 Elasticsearch 发出索引命令之前,你的应用程序需要计算目标索引的名称。
此外 —— 即使一开始这看起来像是一种反模式 —— 你可以有多个应用程序需要在索引中索引相同类型的文档,这些文档的名称可能会发生变化。 现在你必须维护跨多个组件重复的索引名称计算逻辑:就可维护性和敏捷性而言,这不是好消息。
[Logstash](https://www.elastic.co/products/logstash "Logstash") —— Elastic Stack 的一个知名成员 —— 可以帮助集中这样的逻辑,但代价是维护另一个正在运行的软件,这需要配置、知识等。
我们想要在本文中展示的是通过将索引名称计算委托给 Elasticsearch 而不是我们的应用程序来解决此问题的解决方案。
Date index name processor 介绍
============================
该[处理器](https://www.elastic.co/guide/e ... .html "处理器")的目的是通过使用[日期数学索引名称支持](https://www.elastic.co/guide/e ... .html "日期数学索引名称支持"),根据文档中的日期或时间戳字段将文档指向基于正确时间的索引。
处理器根据提供的索引名称前缀、正在处理的文档中的日期或时间戳字段以及提供的日期舍入,使用日期数学索引名称表达式设置 _index 元数据字段。
首先,此处理器从正在处理的文档中的字段中获取日期或时间戳。 或者,可以根据字段值应如何解析为日期来配置日期格式。 然后这个日期、提供的索引名称前缀和提供的日期舍入被格式化为日期数学索引名称表达式。 此处还可以选择日期格式指定日期应如何格式化为日期数学索引名称表达式。
将文档指向每月索引的示例管道,该索引以基于 date1 字段中的日期的 my-index-prefix 开头:
```
- PUT _ingest/pipeline/monthlyindex
- {
- "description": "monthly date-time index naming",
- "processors" : [
- {
- "date_index_name" : {
- "field" : "date1",
- "index_name_prefix" : "my-index-",
- "date_rounding" : "M"
- }
- }
- ]
- }
<br /> <br /> 使用该管道进行索引请求:<br /> <br />
- PUT /my-index/_doc/1?pipeline=monthlyindex
- {
- "date1" : "2016-04-25T12:02:01.789Z"
- }
<br /> <br /> 上面命令运行的结果是:<br /> <br />
- {
- "_index": "my-index-2016-04-01",
- "_id": "1",
- "_version": 1,
- "result": "created",
- "_shards": {
- "total": 2,
- "successful": 1,
- "failed": 0
- },
- "_seq_no": 0,
- "_primary_term": 1
- }
<br /> <br /> 上面的请求不会将这个文档索引到 my-index 索引中,而是索引到 my-index-2016-04-01 索引中,因为它是按月取整的。 这是因为 date-index-name-processor 覆盖了文档的 _index 属性。<br /> <br /> 要查看导致上述文档被索引到 my-index-2016-04-01 中的实际索引请求中提供的索引的日期数学(date-math)值,我们可以使用模拟请求检查处理器的效果。<br /> <br />
- POST _ingest/pipeline/_simulate
- {
- "pipeline" :
- {
- "description": "monthly date-time index naming",
- "processors" : [
- {
- "date_index_name" : {
- "field" : "date1",
- "index_name_prefix" : "my-index-",
- "date_rounding" : "M"
- }
- }
- ]
- },
- "docs": [
- {
- "_source": {
- "date1": "2016-04-25T12:02:01.789Z"
- }
- }
- ]
- }
<br /> <br /> 上面命令返回结果:<br /> <br />
`
- {
- "docs": [
- {
- "doc": {
- "_index": "<my-index-{2016-04-25||/M{yyyy-MM-dd|UTC}}>",
- "_id": "_id",
- "_version": "-3",
- "_source": {
- "date1": "2016-04-25T12:02:01.789Z"
- },
- "_ingest": {
- "timestamp": "2023-02-23T01:15:52.214364Z"
- }
- }
- }
- ]
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
以上示例显示 _index 设置为 <my-index-{2016-04-25||/M{yyyy-MM-dd|UTC}}>。 Elasticsearch 将其理解为 2016-04-01,如[日期数学索引名称文档](https://www.elastic.co/guide/e ... .html "日期数学索引名称文档")中所述。
日期索引名称选项
========
以下是使用 date index name 的一些选项
no - 处理器的描述。 用于描述处理器的用途或其配置。 if no - 有条件地执行处理器。 请参阅https://www.elastic.co/guide/e ... ot%3B title="有条件地运行处理器">有条件地运行处理器。 ignore_failure no false 忽略处理器的故障。 请参阅https://www.elastic.co/guide/e ... ot%3B title="处理管道故障">处理管道故障。 on_failure no - 处理处理器的故障。 请参阅https://www.elastic.co/guide/e ... ot%3B title="处理管道故障">处理管道故障。 tag no - 处理器的标识符。 用于调试和指标
使用案例 - 基于时间的时序索引
================
这是一个众所周知的用例,通常在您要处理日志时发现。这个想法是索引文档,索引的名称由根名称和根据日志事件的日期计算的值组成。 该日期实际上是你要索引的文档的一个字段。
这不是本文的重点,但以这种方式索引文档有几个优点,包括更容易的数据管理、启用冷/暖架构等。让我们举个例子。
假设我们必须处理来自多个来源的数据 —— 例如物联网。 我们的每个对象每分钟都会向不同的后端发送一些数据(是的,这真的很可悲,但我们的对象不通过相同的网络进行通信,因此选择通过多个系统来处理这个问题)。
对象发送的数据被转换成如下所示的 JSON 文档:
```
- POST data/_doc?pipeline=compute-index-name
- {
- "objectId": 1234,
- "manufacturer": "SHIELD",
- "payload": "some_data",
- "date": "2019-04-01T12:10:30.000Z"
- }
<br /> <br /> 我们有一个用于传输数据的对象的 UID、一个制造商 ID、一个有效负载部分和一个日期字段。<br /> <br /> 索引名称计算<br /> ------<br /> <br /> 假设我们要将对象的数据存储在名为 data-{YYYYMMDD} 的索引中,其中根名称是数据后跟日期模式。基于上面的例子,后端收到这个文档应该怎么办呢?<br /> <br /> 首先它必须解析它以提取日期字段的值,然后它必须根据它在文档中找到的日期计算目标索引名称。 最后,它向 Elasticsearch 向刚刚计算出名称的索引发出索引请求。<br /> <br />
- document.date = "2019-04-01T12:10:30Z"
- index.name = "data-" + "20190401"
<br /> <br /> 在我们的例子中,我们有几个后端必须知道如何计算索引名称,因此必须知道索引的命名逻辑。<br /> <br /> 如果索引名的计算直接由 Elasticsearch 进行,岂不是更聪明?<br /> <br /> Ingest pipeline 的力量<br /> -------------------<br /> <br /> ![](<a href="https://img-blog.csdnimg.cn/cf3bc66c45114abb9d06e83595974259.pn" rel="nofollow" target="_blank">https://img-blog.csdnimg.cn/cf ... 59.pn</a>g)<br /> <br /> 从 Elasticsearch 的第 5 版开始,我们现在有了一种称为摄取的节点。默认情况下,集群的所有节点都具有 [ingest](<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest.html" rel="nofollow" target="_blank">https://www.elastic.co/guide/e ... .html</a> "ingest") 类型。这些节点有权在索引文档之前执行所谓的管道。 管道是一组处理器,每个处理器都可以以某种特定方式转换输入文档。当一个文档被摄入到 Elasticsearch 集群时,它的工作流是这样的。<br /> <br /> ![](<a href="https://img-blog.csdnimg.cn/bdc0aab439ed48d8b7eb8aed30ba6e5e.pn" rel="nofollow" target="_blank">https://img-blog.csdnimg.cn/bd ... 5e.pn</a>g)<br /> <br /> 从上面,我们可以看出来,在文档被写入之前,它必须经过 ingest node 进行处理。我们可以通过 date index name processor 来获得我们想要的 index 名称,进而写入到我们想要的索引中去。 这里有用的是,管道不仅可以转换文档的固有数据,还可以修改文档元数据,特别是它的 _index 属性。<br /> <br /> 现在让我们回到我们的例子。我们建议定义一个管道来完成这项工作,而不是将索引名称计算委托给应用程序。根据文档,此处理器允许你定义包含日期的字段名称、索引的根名称(前缀)以及计算附加到此前缀的日期的舍入方法。<br /> <br /> 如果我们想将 IoT 数据添加到模式为 data-{YYYYMMDD} 的索引中,我们只需创建如下所示的管道:<br /> <br />
- PUT _ingest/pipeline/compute-index-name
- {
- "description": "Set the document destination index by appending a prefix and its 'date' field",
- "processors": [
- {
- "date_index_name": {
- "field": "date",
- "index_name_prefix": "data-",
- "date_rounding": "d",
- "index_name_format": "yyyyMMdd"
- }
- }
- ]
- }
<br /> <br /> 一个索引 = 一个管道?<br /> ============<br /> <br /> 好的,现在我们知道如何定义一个管道来为特定的目标索引建立一个名称。 但是我们可以通过操纵文档元数据来做更多的事情!<br /> <br /> 假设我们有不同类型的文档,每个文档都有一个日期字段,但需要在不同的索引中进行索引。计算目标索引名称的逻辑对于每种文档类型都是相同的,但使用上述策略将导致创建多个管道。<br /> <br /> 让我们试着做一些更简单和可重用的东西。<br /> <br /> 回到我们的示例,我们现在有两种文档类型:一种需要在 adata-{YYYYMMDD} 索引(和以前一样)中建立索引,另一种其目的地是名为 new_data-{YYYYMMDD} 的索引。<br /> <br /> 目标为 new_data 的文档具有以下结构:<br /> <br />
- {
- "newObjectId": 1234,
- "source": "HYDRA",
- "payload": "some_data",
- "date": "2019-04-02T13:10:30.000Z"
- }
<br /> <br /> 该结构与标准 IoT 文档略有不同,但重要的是日期字段存在于两个映射中。<br /> <br /> 现在我们要定义一个管道来计算我们两种文档类型的目标索引。 我们所要做的就是通过分析通过索引 API 发出的请求目的地来构建目的地索引名称。<br /> <br />
- PUT _ingest/pipeline/compute-index-name
- {
- "description": "Set the document destination index by appending the requested index and its 'date' field",
- "processors": [
- {
- "date_index_name": {
- "field": "date",
- "index_name_prefix": "{{ _index }}-",
- "date_rounding": "d",
- "index_name_format": "yyyyMMdd"
- }
- }
- ]
- }
```
请注意,索引名称前缀现在位于名为_index 的索引元数据字段中。 通过使用这个字段,我们的管道现在是通用的并且可以与任何索引一起使用 —— 假设目标索引是根据相同的规则计算的。
使用我们的 “路由” 管道
-------------
现在我们有了一个能够根据文档的日期字段计算目标索引名称的通用管道,让我们看看如何让 Elasticsearch 使用它。
我们可以通过两种方式告诉 Elasticsearch 使用管道,让我们评估一下。
Index API 调用
第一个 —— 也是直接的解决方案——是使用 Index API 的管道参数。
换句话说:每次你想索引一个文档,你必须告诉 Elasticsearch 要使用的管道。
```
- POST data/_doc?pipeline=compute-index-name
- {
- "objectId": 1234,
- "manufacturer": "SHIELD",
- "payload": "some_data",
- "date": "2019-04-01T12:10:30.000Z"
- }
```
现在,每次我们通过指示 compute-index-name 管道将文档添加到索引中时,该文档将被添加到正确的基于时间的索引中。 在此示例中,目标索引将为 data-20190401 。
我们提供给 Index API 的 data 索引名称呢? 它可以被看作是一个假索引:它只是用来执行 API 调用并且是真正目标索引的根,它不一定存在!
默认管道:引入 “虚拟索引”
索引默认管道(default pipeline)是使用管道的另一种有用方式:当你创建索引时,有一个名为 index.default_pipeline 的设置可以设置为管道的名称,只要你将文档添加到相应的索引就会执行该管道并且没有管道被添加到 API 调用中。 你还可以在索引文档时使用特殊管道名称 _none 来绕过此默认索引。 通过使用此功能,你可以定义我称之为 “虚拟索引” 的内容,并将其与默认管道相关联,该默认管道将充当我们上面看到的路由管道。
让我们将其应用到我们的示例中。
我们假设我们的通用路由管道 compute-index-name 已经创建。 我们现在可以创建一个名为 data 的索引,它将使用此管道作为其默认管道。
```
- PUT data
- {
- "settings" : {
- "index" : {
- "number_of_shards" : 3,
- "number_of_replicas" : 1,
- "default_pipeline" : "compute-index-name"
- }
- }
- }
<br /> <br /> 现在,每次我们要求 Elasticsearch 为数据索引中的文档编制索引时,计算索引名称管道将负责该文档的实际路由。 因此,数据索引中永远不会有单个文档被索引,但我们将调用管道的责任完全委托给 Elasticsearch。<br /> <br /> 运行完上面的命令后,我们来尝试写入一个文档:<br /> <br />
- POST data/_doc
- {
- "objectId": 1234,
- "manufacturer": "SHIELD",
- "payload": "some_data",
- "date": "2019-04-01T12:10:30.000Z"
- }
<br /> <br /> 上面的命令返回的结果是:<br /> <br />
- {
- "_index": "data-20190401",
- "_id": "2DMGfIYBaog4blQ55Qr7",
- "_version": 1,
- "result": "created",
- "_shards": {
- "total": 2,
- "successful": 1,
- "failed": 0
- },
- "_seq_no": 1,
- "_primary_term": 1
- }
```
结论
==
我们刚刚在这里看到了如何利用 Elasticsearch 中的管道功能根据文档的固有属性来路由文档。Ingest pipeline 不仅仅可以替代 Logstash 过滤器:你可以定义复杂的管道,使用多个处理器(一个特定的处理器甚至允许你调用另一个管道)、条件等。有关 ingest pipeline 的更多文章,请参阅 “[Elastic:开发者上手指南](https://elasticstack.blog.csdn ... 28604 "Elastic:开发者上手指南")” 文章中的 “Ingest pipeline” 章节。
在我看来,本文末尾看到的 “虚拟索引” 非常有趣。 包含创建这样一个并非真正的索引的索引只是为了创建路由管道的入口点的功能甚至可以成为 Elasticsearch 的一个新的和有用的功能,为什么不呢?
使用ngram分词的问题?
charlesfang 回复了问题 • 2 人关注 • 1 个回复 • 4936 次浏览 • 2023-02-09 17:17
ES 使用Join父子文档的方式能否一次查询,同时返回父文档和子文档的结果
laoyang360 回复了问题 • 2 人关注 • 1 个回复 • 6026 次浏览 • 2023-02-08 10:31
es局部更新文档字段
duanxiaobiao 回复了问题 • 2 人关注 • 1 个回复 • 5519 次浏览 • 2023-04-02 15:39
es7.6中,如何实现两个索引关联查询
discolt 回复了问题 • 2 人关注 • 1 个回复 • 5653 次浏览 • 2023-02-10 19:36
在 bulk批量新增数据后,立刻_update操作部分字段,会把新增的字段覆盖(全为null)
God_lockin 回复了问题 • 2 人关注 • 1 个回复 • 3359 次浏览 • 2023-02-07 16:30
配置track_total_hits为true没有生效
warriors 回复了问题 • 3 人关注 • 1 个回复 • 4607 次浏览 • 2023-02-10 16:11
Elasticsearch大版本升级之后geo_shape的polygon查询没有结果
回复zerolh 回复了问题 • 1 人关注 • 1 个回复 • 4177 次浏览 • 2023-02-08 09:06
为什么ES的副本分片进行恢复的时候,仅仅向主分片提供_seq_no就可以了,拿到主分片_seq_no后的操作就算恢复完成,而不是提供_seq_no+_primary_term
emmning 回复了问题 • 2 人关注 • 1 个回复 • 4566 次浏览 • 2023-02-08 16:38
求助:nestde嵌套实现类似not exist的效果
FFFrp 回复了问题 • 2 人关注 • 1 个回复 • 5258 次浏览 • 2023-02-02 10:42
Elasticsearch:如何在 Elasticsearch 中正确使用同义词功能
liuxg 发表了文章 • 0 个评论 • 8350 次浏览 • 2023-01-13 09:20
原文地址 [elasticstack.blog.csdn.net](https://elasticstack.blog.csdn ... 655866)
![](https://img-blog.csdnimg.cn/im ... c6.png)
同义词用于提高搜索质量并扩大匹配范围。 例如,搜索 England 的用户可能希望找到包含 British 或 UK 的文档,尽管这三个词完全不同。
[Elasticsearch](https://so.csdn.net/so/search% ... 1.7020) 中的同义词功能非常强大,如果实施得当,可以使你的搜索引擎更加健壮和强大。 在本文中,我们将通过简单的代码片段介绍在实践中实现同义词功能的要点。 特别是,我们将介绍如何更新现有索引的同义词,这是一个相对高级的话题。
在今天的展示中,我将使用最新的 Elastic Stack 8.6.0,尽管版本不对我们的展示有任何的影响。
准备
==
我们将使用 Docker 在本地启动一个 Elasticsearch 服务器,并使用 [Kibana](https://so.csdn.net/so/search% ... 1.7020) 来管理索引和运行命令。 如果你以前从未使用过 Elasticsearch 或想快速复习一下,[这篇文章](https://elasticstack.blog.csdn ... 36411 "这篇文章")可能会对你有所帮助。 如果你在 Docker 中运行 Elasticsearch 时遇到问题,[这篇文章](https://elasticstack.blog.csdn ... 58356 "这篇文章")很可能会帮助你解决问题。在今天的文章中,我们尝试使用 docker 来部署一个没有安全功能的 Elasticsearch 集群。
准备就绪后,让我们开始探索 Elasticsearch 中的同义词功能的旅程。
我们将在本文中使用的 docker-compose.yaml 文件包含以下内容,稍后我们将向其添加更多功能:
docker-compose.yml
``<br />
- version: "3.9"
- services:
- elasticsearch:
- image: elasticsearch:8.6.0
- environment:
-
- discovery.type=single-node
- discovery.type=single-node
-
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- ES_JAVA_OPTS=-Xms1g -Xmx1g
-
- xpack.security.enabled=false
- xpack.security.enabled=false
- volumes:
-
- type: volume
- type: volume
- source: es_data
- target: /usr/share/elasticsearch/data
- ports:
-
- target: 9200
- target: 9200
- published: 9200
- networks:
-
- elastic
- elastic
- kibana:
- image: kibana:8.6.0
- ports:
-
- target: 5601
- target: 5601
- published: 5601
- depends_on:
-
- elasticsearch
- elasticsearch
- networks:
-
- elastic
- elastic
- volumes:
- es_data:
- driver: local
- networks:
- elastic:
- name: elastic
- driver: bridge
![](<a href="https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.pn" rel="nofollow" target="_blank">https://csdnimg.cn/release/blo ... te.pn</a>g)<br /> ```<br /> <br /> 你可以使用以下命令之一启动 Elasticsearch 和 Kibana:<br /> <br /> ```<br /> docker-compose up<br /> ```<br /> <br /> 或者<br /> <br /> ```<br /> docker-compose up -d<br /> ```<br /> <br /> 如果加上 -d 选项的话,Elasticsearch 会以 daemon 的形式来运行。上面是一种最为简单的方式来启动 Elasticsearch 集群及 Kibana。由于它没有设置安全,我们无需输入任何凭证就可以直接进入到 Kibana 了。<br /> <br /> ![](<a href="https://img-blog.csdnimg.cn/5322f700631f42a5b15acb95ea91e574.pn" rel="nofollow" target="_blank">https://img-blog.csdnimg.cn/53 ... 74.pn</a>g)<br /> <br /> 使用带有同义词列表的标准同义词 token 过滤器<br /> =========================<br /> <br /> 让我们首先使用带有同义词列表的标准同义词标记过滤器创建一个索引。 在 Kibana 中运行以下命令,我们将在稍后解释详细信息: <br /> <br /> ```<br />
- PUT synonyms
- {
- "settings": {
- "index": {
- "analysis": {
- "analyzer": {
- "index_analyzer": {
- "tokenizer": "standard",
- "filter": [
- "lowercase",
- "synonym_filter"
- ]
- }
- },
- "filter": {
- "synonym_filter": {
- "type": "synonym",
- "synonyms": [
- "elk => Elastic Stack",
- "elkb => Elastic Stack"
- ]
- }
- }
- }
- }
- },
- "mappings": {
- "properties": {
- "name": {
- "type": "text",
- "analyzer": "index_analyzer"
- }
- }
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
这里的要点:
- 请注意设置键的嵌套级别。 settings => index => analysis => analyzer/filter 都是内置关键字。 但是,index_analyzer 和 synonym_filter 分别是自定义分析器和过滤器的自定义名称。
- 我们需要创建一个 type 为 synonym 的自定义过滤器。 synonym 选项明确提供了同义词列表。 这通常应该只用于测试,因为更新同义词列表不方便,我们稍后会看到。
- 本文中使用了 [Solr 同义词](https://www.elastic.co/guide/e ... onyms "Solr 同义词")。 对于此示例,使用了显式映射,这意味着 => 左侧的标记将替换为右侧的标记。 稍后我们将使用等同的同义词,这意味着提供的 token 被等同对待。
- synonym_filter 添加到名为 index_analyzer 的新自定义分析器的过滤器列表中。 通常过滤器的顺序很重要。 然而,对于同义词过滤器来说,它有点特殊,可能会让我们中的许多人感到惊讶。 在此示例中,即使 synonym_filter 过滤器放在小写过滤器之后,此过滤器返回的标记也会传递给小写过滤器,因此也会变成小写。 因此,你不需要在同义词列表或同义词文件中提供小写 token。
- 最后,在文档的映射中,为名称字段指定了自定义分析器。
我们知道在早期的 Elastic 产品中 elk 就是 Elastic Stack 的代名词。之后随着 Beats 的加入,很多开发者也把 elkb 当做 Elastic Stack 的代名词。要测试在索引中创建的分析器,我们可以调用 _analyze 端点:
```
- GET /synonyms/_analyze
- {
- "analyzer": "index_analyzer",
- "text": "elk is powerful"
- }
<br /> <br /> 上面命令的输出为:<br /> <br />
`
- {
- "tokens": [
- {
- "token": "elastic",
- "start_offset": 0,
- "end_offset": 3,
- "type": "SYNONYM",
- "position": 0
- },
- {
- "token": "is",
- "start_offset": 4,
- "end_offset": 6,
- "type": "
", - "position": 1
- },
- {
- "token": "stack",
- "start_offset": 4,
- "end_offset": 6,
- "type": "SYNONYM",
- "position": 1
- },
- {
- "token": "powerful",
- "start_offset": 7,
- "end_offset": 15,
- "type": "
", - "position": 2
- }
- ]
- }
`![](https://csdnimg.cn/release/blo ... te.png)
<br /> <br /> 从上面的输出中,我们可以看到 type 为 SNONYM 的 token 为 elastic 及 stack。让我们向索引中添加一些文档并测试它在搜索中是否正常工作:<br /> <br />
- PUT /synonyms/_doc/1
- {
- "name": "elk is very powerful"
- }
- PUT /synonyms/_doc/2
- {
- "name": "elkb is useful"
- }
- PUT /synonyms/_doc/3
- {
- "name": "Elastic Stack is so widely used"
- }
<br /> <br /> 我们可以使用 match 关键字进行简单的搜索:<br /> <br />
- GET /synonyms/_search?filter_path=**.hits
- {
- "query": {
- "match": {
- "name": "elk"
- }
- }
- }
<br /> <br /> 如果没有问题,所有三个文件都应该被搜索到:<br /> <br />
`
- {
- "hits": {
- "hits": [
- {
- "_index": "synonyms",
- "_id": "2",
- "_score": 0.31931418,
- "_source": {
- "name": "elkb is useful"
- }
- },
- {
- "_index": "synonyms",
- "_id": "1",
- "_score": 0.29086044,
- "_source": {
- "name": "elk is very powerful"
- }
- },
- {
- "_index": "synonyms",
- "_id": "3",
- "_score": 0.24686477,
- "_source": {
- "name": "Elastic Stack is so widely used"
- }
- }
- ]
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
索引时间 vs 搜索时间进行同义词操作
===================
如你所见,在上面的示例中,只创建了一个分析器,它用于索引和搜索。
不鼓励在索引(indexing)步骤中对所有文档应用同义词,因为它有一些主要缺点:
- 如果不重新索引所有内容,就无法更新同义词列表,这在实践中是非常低效的。
- 搜索分数会受到影响,因为同义词 token 也会被计算在内。
- 索引过程变得更加耗时并且索引将变得更大。 对于小数据集来说可以忽略不计,但对于大数据集来说非常重要。
因此,最好在搜索步骤中只应用同义词,这样可以克服所有三个缺点。 为此,我们需要创建一个用于搜索的新分析器。
使用 search_analyzer 并应用搜索时间同义词
=============================
在 Kibana 中运行以下命令以创建具有搜索时同义词的新索引:
``<br />
- 如果不重新索引所有内容,就无法更新同义词列表,这在实践中是非常低效的。
- PUT synonym_graph
- {
- "settings": {
- "index": {
- "analysis": {
- "analyzer": {
- "index_analyzer": {
- "tokenizer": "standard",
- "filter": [
- "lowercase"
- ]
- },
- "search_analyzer": {
- "tokenizer": "standard",
- "filter": [
- "lowercase",
- "synonym_filter"
- ]
- }
- },
- "filter": {
- "synonym_filter": {
- "type": "synonym_graph",
- "synonyms": [
- "elk => Elastic Stack",
- "elkb => Elastic Stack"
- ]
- }
- }
- }
- }
- },
- "mappings": {
- "properties": {
- "name": {
- "type": "text",
- "analyzer": "index_analyzer",
- "search_analyzer": "search_analyzer"
- }
- }
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
关键点:
- 该类型现在更改为 [synonym_graph](https://www.elastic.co/guide/e ... .html "synonym_graph"),这是一个更复杂的同义词过滤器,旨在仅用作搜索分析器的一部分。 它可以更恰当地处理多词同义词,推荐用于搜索时分析。 但是,你可以继续使用原来的 synonym 类型,它在这篇文章中的表现是一样的。
- 同义词过滤器从索引时间分析器中删除并添加到搜索时间分析器中。
- search_analyzer 是为 name 字段明确指定的。 如果未指定,则相同的分析器 (index_analyzer) 将用于索引和搜索。
分析器应该返回与以前相同的 token。 然而,当你用这些命令为三个文档建立索引并再次执行相同的搜索后,结果会有所不同:
```
- 该类型现在更改为 [synonym_graph](https://www.elastic.co/guide/e ... .html "synonym_graph"),这是一个更复杂的同义词过滤器,旨在仅用作搜索分析器的一部分。 它可以更恰当地处理多词同义词,推荐用于搜索时分析。 但是,你可以继续使用原来的 synonym 类型,它在这篇文章中的表现是一样的。
- PUT /synonym_graph/_doc/1
- {
- "name": "elk is very powerful"
- }
- PUT /synonym_graph/_doc/2
- {
- "name": "elkb is useful"
- }
- PUT /synonym_graph/_doc/3
- {
- "name": "Elastic Stack is so widely used"
- }
<br /> <br /> 我们使用如下的命令来进行搜索:<br /> <br />
- GET /synonym_graph/_search?filter_path=**.hits
- {
- "query": {
- "match": {
- "name": "elk"
- }
- }
- }
<br /> <br /> 这一次,只有如下的结果返回。甚至 “elk is very powerful” 这个文档也没有被返回:<br /> <br />
- {
- "hits": {
- "hits": [
- {
- "_index": "synonym_graph",
- "_id": "3",
- "_score": 2.3589978,
- "_source": {
- "name": "Elastic Stack is so widely used"
- }
- }
- ]
- }
- }
<br /> <br /> 原因是同义词过滤器仅在搜索时应用。 搜索查询 elk 被替换为同义词标记 “Elastic Stack”。 然而,索引中的文档没有被同义词过滤器(synonym_filter)过滤,因此 “elk” 只是被标记为 elk 而没有被 Elastic Stack 替换。 类似于 elkb。 结果,只能匹配 “Elastic Stack is so widely used”。<br /> <br /> 为了使其像前面的示例一样正常工作,我们需要将同义词规则从显式映射更改为等效同义词。 让我们按如下方式更新同义词过滤器:<br /> <br />
- ......
- "filter": {
- "synonym_filter": {
- "type": "synonym_graph",
- "synonyms": [
- "elk, elkb, Elastic Stack"
- ]
- }
- }
- ......
<br /> <br /> 要更改现有索引的同义词,我们可以重新创建索引并重新索引所有文档,这是愚蠢且低效的。<br /> <br /> 更好的方法是更新索引的设置。 但是,我们需要在更新设置之前关闭索引,然后重新打开它才能访问它:<br /> <br />
- POST /synonym_graph/_close
- PUT /synonym_graph/_settings
- {
- "settings": {
- "index.analysis.filter.synonym_filter.synonyms": [
- "elk, elkb, Elastic Stack"
- ]
- }
- }
- POST /synonym_graph/_open
<br /> <br /> 请注意更新索引设置的特殊语法。<br /> <br /> 运行上述命令后,我们可以通过如下命令的返回值来进行验证:<br /> <br />
GET synonym_graph
<br /> <br /> 上面的命令返回:<br /> <br />
`
- {
- "synonym_graph": {
- "aliases": {},
- "mappings": {
- "properties": {
- "name": {
- "type": "text",
- "analyzer": "index_analyzer",
- "search_analyzer": "search_analyzer"
- }
- }
- },
- "settings": {
- "index": {
- "routing": {
- "allocation": {
- "include": {
- "_tier_preference": "data_content"
- }
- }
- },
- "number_of_shards": "1",
- "provided_name": "synonym_graph",
- "creation_date": "1673501061514",
- "analysis": {
- "filter": {
- "synonym_filter": {
- "type": "synonym_graph",
- "synonyms": [
- "elk, elkb, Elastic Stack"
- ]
- }
- },
- "analyzer": {
- "index_analyzer": {
- "filter": [
- "lowercase"
- ],
- "tokenizer": "standard"
- },
- "search_analyzer": {
- "filter": [
- "lowercase",
- "synonym_filter"
- ],
- "tokenizer": "standard"
- }
- }
- },
- "number_of_replicas": "1",
- "uuid": "UCIWtpQMTsCc1TwnvsywHA",
- "version": {
- "created": "8060099"
- }
- }
- }
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
<br /> <br /> 让我们使用 _analyzer 端点测试 search_analyzer 并查看生成的 token:<br /> <br />
- GET /synonym_graph/_analyze
- {
- "analyzer": "search_analyzer",
- "text": "elk"
- }
<br /> <br /> 上述命令返回:<br /> <br /> 它表明 elk 搜索查询被三个同义词的 token 替换和扩展(由 [expand](<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-graph-tokenfilter.html" rel="nofollow" target="_blank">https://www.elastic.co/guide/e ... .html</a> "expand") 选项控制)。 它还证明,如果在索引时应用等效同义词,则结果索引的大小可以显着增加。<br /> <br /> 然后当我们再次执行相同的搜索时:<br /> <br />
- GET /synonym_graph/_search?filter_path=**.hits
- {
- "query": {
- "match": {
- "name": "elk"
- }
- }
- }
<br /> <br /> 这次搜索的结果是:<br /> <br />
`
- {
- "hits": {
- "hits": [
- {
- "_index": "synonym_graph",
- "_id": "3",
- "_score": 1.6949677,
- "_source": {
- "name": "Elastic Stack is so widely used"
- }
- },
- {
- "_index": "synonym_graph",
- "_id": "2",
- "_score": 1.1220688,
- "_source": {
- "name": "elkb is useful"
- }
- },
- {
- "_index": "synonym_graph",
- "_id": "1",
- "_score": 1.0126972,
- "_source": {
- "name": "elk is very powerful"
- }
- }
- ]
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
我们可以看到三个文档都被搜索出来了。
使用同义词文件
=======
上面我们一直在创建索引时直接指定同义词列表。 但是,当你有大量同义词时,将它们全部添加到索引中会很麻烦。 更好的方法是将它们存储在一个文件中,然后动态地将它们加载到索引中。 使用同义词文件有很多好处,其中包括:
- 方便维护大量的同义词。
- 可以被不同的索引使用。
- 可以在不关闭索引的情况下动态重新加载。
首先,我们需要先将同义词放入一个文件中。 每行都是一个同义词规则,与上面演示的相同。 更多[细节](https://www.elastic.co/guide/e ... yms_2 "细节")可以在官方文档中找到。
我们将创建的同义词文件称为 synonyms.txt,但可以任意命名。 它具有以下内容:
```
- 方便维护大量的同义词。
- $ pwd
- /Users/liuxg/data/docker8
- $ ls
- docker-compose.yml synonyms.txt
- $ cat synonyms.txt
-
This is a comment! The file is named synonyms.txt.
- elk,elkb,Elastic Stack
<br /> <br /> 然后我们需要将同义词文件绑定到 Docker 容器中。 更新 docker-compose.yaml 如下:<br /> <br /> **docker-compose.yml**<br /> <br />
`
- version: "3.9"
- services:
- elasticsearch:
- image: elasticsearch:8.6.0
- environment:
-
- discovery.type=single-node
- discovery.type=single-node
-
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- ES_JAVA_OPTS=-Xms1g -Xmx1g
-
- xpack.security.enabled=false
- xpack.security.enabled=false
- volumes:
-
- type: volume
- type: volume
- source: es_data
- target: /usr/share/elasticsearch/data
-
- type: bind
- type: bind
- source: ./synonyms.txt
- target: /usr/share/elasticsearch/config/synonyms.txt
- ports:
-
- target: 9200
- target: 9200
- published: 9200
- networks:
-
- elastic
- elastic
- kibana:
- image: kibana:8.6.0
- ports:
-
- target: 5601
- target: 5601
- published: 5601
- depends_on:
-
- elasticsearch
- elasticsearch
- networks:
-
- elastic
- elastic
- volumes:
- es_data:
- driver: local
- networks:
- elastic:
- name: elastic
- driver: bridge
`![](https://csdnimg.cn/release/blo ... te.png)
<br /> <br /> 我们可以使用 CTRL+C 来终止之前运行的 docker,然后再次使用如下命令来启动:<br /> <br />
docker-compose up
<br /> <br /> 请注意,同义词文件已加载到容器中的 config 文件夹中。你可以进入容器并使用以下两个命令之一检查它:<br /> <br />
-
User docker
- docker exec -it elasticsearch-1 bash
-
User docker-compose
- docker-compose exec elasticsearch bash
<br /> <br /> 现在我们需要停止并重新启动服务以使更改生效。 请注意,仅重新启动服务将不起作用。<br /> <br />
- docker-compose stop elasticsearch
- docker-compose up -d elasticsearch
<br /> <br />
- $ docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 3ae4b728dd44 kibana:8.6.0 "/bin/tini -- /usr/l…" 23 seconds ago Up 21 seconds 0.0.0.0:5601->5601/tcp docker8-kibana-1
- 878c82384761 elasticsearch:8.6.0 "/bin/tini -- /usr/l…" 23 seconds ago Up 22 seconds 0.0.0.0:9200->9200/tcp, 9300/tcp docker8-elasticsearch-1
- $ docker exec -it docker8-elasticsearch-1 bash
- elasticsearch@878c82384761:~$ pwd
- /usr/share/elasticsearch
- elasticsearch@878c82384761:~$ ls
- LICENSE.txt NOTICE.txt README.asciidoc bin config data jdk lib logs modules plugins
- elasticsearch@878c82384761:~$ cd config/
- elasticsearch@878c82384761:~/config$ ls
- elasticsearch-plugins.example.yml jvm.options log4j2.properties synonyms.txt
- elasticsearch.keystore jvm.options.d role_mapping.yml users
- elasticsearch.yml log4j2.file.properties roles.yml users_roles
<br /> <br /> 从上面的输出中,我们可以看到 synonyms.txt 已经被成功地加载到容器里了。<br /> <br /> 然后我们可以使用同义词文件创建一个新索引:<br /> <br />
`
- PUT /synonym_graph_file
- {
- "settings": {
- "index": {
- "analysis": {
- "analyzer": {
- "index_analyzer": {
- "tokenizer": "standard",
- "filter": [
- "lowercase"
- ]
- },
- "search_analyzer": {
- "tokenizer": "standard",
- "filter": [
- "lowercase",
- "synonym_filter"
- ]
- }
- },
- "filter": {
- "synonym_filter": {
- "type": "synonym_graph",
- "synonyms_path": "synonyms.txt",
- "updateable": true
- }
- }
- }
- }
- },
- "mappings": {
- "properties": {
- "name": {
- "type": "text",
- "analyzer": "index_analyzer",
- "search_analyzer": "search_analyzer"
- }
- }
- }
- }
`![](https://csdnimg.cn/release/blo ... te.png)
```
关键点:
- 对于 synonyms_path,它是同义词文件相对于 Elasticsearch 服务器中 config 文件夹的路径。
- 添加了一个新的 updateable 字段,它指定相应的过滤器是否可更新。 我们很快就会看到如何在不关闭和打开索引的情况下重新加载搜索分析器。
这个新索引 synonym_graph_file 的行为应该与前一个 synonym_graph 的行为相同。
现在让我们在同义词文件中添加更多的同义词,其内容如下:
```
- 对于 synonyms_path,它是同义词文件相对于 Elasticsearch 服务器中 config 文件夹的路径。
- $ pwd
- /Users/liuxg/data/docker8
- $ ls
- docker-compose.yml synonyms.txt
- $ cat synonyms.txt
-
This is a comment! The file is named synonyms.txt.
- elk,elkb,Elastic Stack
- JS => JavaScript
- TS => TypeScript
- Py => Python
<br /> <br /> 添加同义词后,我们可以关闭并打开索引使其生效。 然而,由于我们将同义词过滤器标记为可更新,我们可以重新加载搜索分析器以使更改立即生效,而无需关闭索引,因此无需停机。<br /> <br /> 要重新加载索引的搜索分析器,我们需要调用 _reload_search_analyzers 端点:<br /> <br />
POST /synonym_graph_file/_reload_search_analyzers
<br /> <br /> 上面的命令输出为:<br /> <br />
`
- {
- "_shards": {
- "total": 2,
- "successful": 1,
- "failed": 0
- },
- "reload_details": [
- {
- "index": "synonym_graph_file",
- "reloaded_analyzers": [
- "search_analyzer"
- ],
- "reloaded_node_ids": [
- "tZLy82KRTaiCdpsbkEYnuA"
- ]
- }
- ]
- }
`![](https://csdnimg.cn/release/blo ... te.png)
<br /> <br /> 现在,当我们分析 JS 字符串时,我们将看到返回的 javascript token。<br /> <br />
- GET /synonym_graph_file/_analyze
- {
- "analyzer": "search_analyzer",
- "text": "JS"
- }
<br /> <br /> 上面的命令返回:<br /> <br />
- {
- "tokens": [
- {
- "token": "javascript",
- "start_offset": 0,
- "end_offset": 2,
- "type": "SYNONYM",
- "position": 0
- }
- ]
- }
```
这里应该注意两件重要的事情:
- 如果同义词过滤器的 updateable 设置为true,那么对应的分析器只能作为 search_analyzer 使用,不能用于索引,即使类型是同义词。
- updateable 选项只能在同义词文件与 synonyms_path 选项一起使用时使用,而不是在同义词直接通过 synonyms 选项提供时使用。
恭喜你到达这里! 我们已经涵盖了在 Elasticsearch 中使用同义词功能的所有要点。
我们已经分别介绍了如何在索引时间和搜索时间分析步骤中使用同义词。 此外,还介绍了如何直接提供同义词列表,以及如何通过文件提供。 最后但同样重要的是,介绍了关于如何更新现有索引的同义词列表的不同方法。 建议重新加载索引的搜索分析器,因为它不会给服务带来停机时间。
- 如果同义词过滤器的 updateable 设置为true,那么对应的分析器只能作为 search_analyzer 使用,不能用于索引,即使类型是同义词。