ElasticSearch同义词解析

什么是同义词?

搜索引擎会对文档和查询进行分析并将其拆解为最小的单元(通常称为词元,实际上就是抽象的符号)。搜索时,匹配过程会使用简单字串相似度,所以如果查询中有一些十分微小的拼写错误(例如"hous",只比"house"少一个字母 e)或者使用名词的复数形式("houses"),即使文档中包含名词的单数形式("house"),搜索引擎也不会匹配到这份文档。词干提取器模糊查询等工具虽然可以解决一些最常见的此类问题,但是它们并不能消除相关联的概念或想法之间的差异,也不能将文档或查询中稍有不同的单词用法视为等同。

这时同义词就派上了大用场。同义词的英文 synonym 来自于希腊语,分别是前缀 σύν(syn,表示"一起")和 ὄνομα(ónoma,表示"名称")。从它的词源可以看出,同义词表示的是在同一语言或领域中具有完全或基本相同意思的不同词语。实际上,同义词的范围非常广泛,包括一般同义词("疲劳"和"困倦")、缩写(英镑的两种写法"lb."和"pound")、电商搜索中产品的不同拼写("iPod"和"i-Pod")、细微的语言差异(例如均表示电梯的英式英语"lift"和美式英语"elevator")、专业用词和普通用词(例如"犬"和"狗"),甚至单纯表示同一概念的两种方式("宇宙"和"太空")。通过提供恰当的同义词规则,搜索工程师能够就哪些词在各自领域内具有相似意思并应该采取相似处理方法提供相关信息。

同义词用于提高搜索质量并扩大匹配范围。 例如,搜索oil的用户可能希望找到包含原油或石油的文档,尽管这三个词完全不同。

在我们的很多情况下,我们希望在搜索时,有时能够使用一个词的同义词来进行搜索,这样我们能搜索出来更多相关的内容。我们可以通过 text analysis 来帮助我们形成同义词

ElasticSearch 同义词配置

索引时使用同义词和搜索时使用同义词的对比

同义词在分析器中使用,其既可在索引时使用,也可在搜索时使用。关于在 Elasticsearch 中如何使用同义词筛选器,最常见的问题之一就是:"我应该索引时使用,还是搜索时使用,还是同时都用?" 我们首先看一下在索引时应用同义词筛选。这意味着会在索引后的文档中对字词进行一次性替换或扩展,结果将一直保存在搜索索引中。

索引时使用同义词有几个劣势:

  • 由于必须对所有同义词进行索引,所以索引规模会变大。
  • 搜索得分(依赖于字词统计数据)可能会受影响,因为同义词也会计算在内,所以不常见单词的统计数据会存在偏差。
  • 除非进行重新索引,否则无法针对既有文档更改同义词规则。

最后两条尤其是巨大劣势。索引时应用同义词的唯一潜在好处是性能好,因为您在前期已费心完成了扩展过程,所以无需再在每次查询时完成一遍扩展过程,这有可能致使需要与更多的字词进行匹配。然而这一点在实践中通常并非真正的问题。

相反,在搜索时所用的分析工具中使用同义词则可以避免很多上述问题:

  • 索引规模不受影响。
  • 语料库中的字词统计数据保持不变。
  • 如需变更同义词规则,无需对文档进行重新索引。

这些优势通常要高出唯一的劣势,即每次查询时都必须执行同义词扩展操作,这有可能导致需要匹配更多字词。不仅如此,搜索时扩展同义词还能够允许使用更加复杂的 synonym_graph 词元筛选器,这一工具能够正确处理多单词同义词,并且仅可在搜索分析器中使用。

一般而言,搜索时使用同义词的优势通常要高于索引时使用同义词可能实现的微小性能改进。

然而,如果在搜索时使用同义词,过去还需要注意另外一个问题。尽管更改同义词规则不需要对文档进行重新索引,但是如要更改的话,您必须暂时关闭再重新打开索引。这一点很有必要,因为分析器在下列时候才会创建实例:创建索引时,重启节点时,以及重新打开已关闭的索引时。为了让对同义词规则文件所做的变更对索引可见,用户必须首先在所有节点上更新文件,然后再关闭并重新打开索引。但是这个问题已经得以解决。

在查询时对词进行同义词解析

es操作版本8.8.0

java 复制代码
创建索引
PUT myindex
{
    "settings": {
        "analysis": {
            "filter": {
                "my_synonyms": {
                    "type": "synonym_graph",
                    "synonyms": [
                        "看月亮,吃月饼=>中秋节",
                        "双十一,双11=>购物",
                        "免费,免费版,不要钱的,无偿"
                    ]
                }
            },
            "analyzer": {
                "my_analyzer": {
                    "type": "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "lowercase",
                        "my_synonyms"
                    ]
                }
            }
        }
    },
    "mappings": {
            "properties": {
                "content": {
                    "type": "text",
                    "analyzer": "standard",
                    "search_analyzer": "my_analyzer"
                }
            }
        }
    }

展示数据
"hits": {
    "total": 4,
    "max_score": 1.0,
    "hits": [
        {
            "_index": "myindex",
            "_type": "_doc",
            "_id": "luiwhIsB4MmTaMc6fVt7",
            "_score": 1.0,
            "_source": {
                "content": "无偿"
            }
        },
        {
            "_index": "myindex",
            "_type": "_doc",
            "_id": "lOiwhIsB4MmTaMc6a1uu",
            "_score": 1.0,
            "_source": {
                "content": "中秋节"
            }
        },
        {
            "_index": "myindex",
            "_type": "_doc",
            "_id": "p-iwhIsB4MmTaMc68GuR",
            "_score": 1.0,
            "_source": {
                "content": "不要钱的"
            }
        },
        {
            "_index": "myindex",
            "_type": "_doc",
            "_id": "kuiwhIsB4MmTaMc6NFsw",
            "_score": 1.0,
            "_source": {
                "content": "购物"
            }
        }
    ]
}

测试查询

测试查询分词

使用同义词文件

上面我们一直在创建索引时直接指定同义词列表。 但是,当你有大量同义词时,将它们全部添加到索引中会很麻烦。 更好的方法是将它们存储在一个文件中,然后动态地将它们加载到索引中。 使用同义词文件有很多好处,其中包括:

  • 方便维护大量的同义词。
  • 可以被不同的索引使用。
  • 可以在不关闭索引的情况下动态重新加载

我们将创建的同义词文件称为 synonyms.txt,但可以任意命名,把同义词维护进去,加载到es中 默认去读取config目录下

使用同义词文件创建一个新索引,请注意我们将同义词筛选器标记为了 updateable(可更新)

java 复制代码
PUT myindex
{
    "settings": {
        "analysis": {
            "filter": {
                "my_synonyms": {
                    "type": "synonym_graph",
                    "synonyms_path": "analysis/synonyms.txt",
                    "updateable": true
                }
            },
            "analyzer": {
                "my_analyzer": {
                    "type": "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "lowercase",
                        "my_synonyms"
                    ]
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "content": {
                "type": "text",
                "analyzer": "standard",
                "search_analyzer": "my_analyzer"
            }
        }
    }
}

测试同义词

java 复制代码
GET /myindex/_search
{
    "took": 0,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 3,
            "relation": "eq"
        },
        "max_score": 1,
        "hits": [
            {
                "_index": "myindex",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "content": "齐鲁"
                }
            },
            {
                "_index": "myindex",
                "_id": "2",
                "_score": 1,
                "_source": {
                    "content": "淄博"
                }
            },
            {
                "_index": "myindex",
                "_id": "3",
                "_score": 1,
                "_source": {
                    "content": "戴眼镜"
                }
            }
        ]
    }
}

测试请求: 
GET /myindex/_search
{
    "query": {
        "match": {
            "content": "4眼仔"
        }
    }
}

响应:
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 2.634553,
        "hits": [
            {
                "_index": "myindex",
                "_id": "3",
                "_score": 2.634553,
                "_source": {
                    "content": "戴眼镜"
                }
            }
        ]
    }
}
相关推荐
不是笨小孩i1 小时前
Git常用指令
大数据·git·elasticsearch
+码农快讯+4 小时前
Git入门学习(1)
git·学习·elasticsearch
码爸8 小时前
java 执行es中的sql
java·sql·elasticsearch
学习使我快乐——玉祥11 小时前
es查询语法
大数据·elasticsearch·搜索引擎
徐*红13 小时前
Elasticsearch 8.+ 版本查询方式
大数据·elasticsearch
码爸13 小时前
flink 例子(scala)
大数据·elasticsearch·flink·scala
txtsteve14 小时前
es由一个集群迁移到另外一个集群es的数据迁移
大数据·elasticsearch·搜索引擎
工作中的程序员14 小时前
ES 索引或索引模板
大数据·数据库·elasticsearch
Lill_bin1 天前
深入理解ElasticSearch集群:架构、高可用性与数据一致性
大数据·分布式·elasticsearch·搜索引擎·zookeeper·架构·全文检索
RwTo1 天前
Elasticsearch 聚合搜索
大数据·elasticsearch·搜索引擎·全文检索