阿里云官方镜像站:
https://developer.aliyun.com/mirror/?utm_content=g_1000304570
在我们的很多情况下,我们希望在搜索时,有时能够使用一个词的同义词来进行搜索,这样我们能搜索出来更多相关的内容。我们可以通过 text analysis 来帮助我们形成同义词。文本分析通常应用于你建立索引时的所有文档以及发送给 Elasticsearch 的所有查询。在进行同义词搜索时,我们有如下的几种方案:
在今天的文章中,我们将分别论述。
首先,我们来创建一个具有如下 anaylzer 及 mapping 的一个索引:
PUT myindex{"settings": {"analysis": {"filter": {"my_synonyms": {"type": "synonym_graph","synonyms": ["China, chn, PRC, People's Republic of China"]}},"analyzer": {"my_analyzer": {"type": "custom","tokenizer": "standard","filter":["lowercase","my_synonyms"]}}}},"mappings": {"properties": {"content": {"type": "text","analyzer": "standard","search_analyzer": "my_analyzer"}}}}
在上面,我们使用 synonym_graph 过滤器对 quey 时的词进行过滤。在这个过滤器中,我们把如下的一个词都视为同义词:
China, chn, PRC, People's Republic of China
在mapping 中,我们定义了 search_analyzer 为 my_analyzer,也就是说在 query 时,它会对所有的词进行分词。但凡有任何一个词是 China, chn, PRC, People’s Republic of China 其中的一个,它都将被视为同义词。
我们首先来创建一个文档:
PUT myindex/_doc/1{"content": "I like People's Republic of China"}
运行上面的指令,我们将创建一个 content 为 I like People’s Republic of China 的文档。
接下来,我们做如下的查询:
GET myindex/_search{"query": {"match": {"content": "China"}}}
那么显示的结果是:
{"took" : 256,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.4384104,"hits" : [{"_index" : "myindex","_type" : "_doc","_id" : "1","_score" : 1.4384104,"_source" : {"content" : "I like People's Republic of China"}}]}}
可能有人说了,这是因为上面的 content 里本身就含有 China, 所以上面的结果证明不了什么。接下来,我们进行如下的搜索:
GET myindex/_search{"query": {"match": {"content": "prc"}}}
结果,我们可以发现,我们同样显示上面的搜索的结果。这个说明了这个同义词的搜索是成功的。
接下来,我们想搜索 silk road 也能搜索出中国来,那么我怎么做呢?
我们来执行如下的命令:
POST myindex/_closePUT myindex/_settings{"analysis": {"filter": {"my_synonyms": {"type": "synonym_graph","synonyms": ["china, silk road, chn, PRC, People's Republic of China"]}},"analyzer": {"my_analyzer": {"type": "custom","tokenizer": "standard","filter": ["lowercase","my_synonyms"]}}}}POST myindex/_open
我们可以通过更新 setting 来实现这个。在上面请注意:当我们更新一个索引的 index 时,我们必须先把它关掉,等设置好后,在重新打开。否则会有错误。那么经过上面的修改后,我们重新运行如下的搜索:
GET myindex/_search{"query": {"match": {"content": "silk road"}}}
那么上面的搜索结果将会显示我们之前显示的结果。在这里 silk road 也就是和之前的其它词都是同义词。
有人可能觉得上面在 settings 里配置太多的同义词很麻烦(如果同义词很多的话)。按照 Elastic 的官方文档,我们可以把所有的同义词放到一个文档中。首先,我们在 Elasticsearch 的 config 目录中,创建一个叫做 analysis 的子目录,然后创建一个叫做 synonyms.txt 的文档,而它的内容如下:
$ pwd/Users/liuxg/elastic/elasticsearch-7.8.0/config/analysisliuxg:analysis liuxg$ cat synonyms.txt"china, silk road, chn, PRC, People's Republic of China","elk, elastic stack"
在这里,我们多添加了一个 elk, elastic stack 的同义词。我们来创建一个新的索引:
PUT myindex1{"settings": {"analysis": {"filter": {"my_synonyms": {"type": "synonym_graph","synonyms_path": "analysis/synonyms.txt"}},"analyzer": {"my_analyzer": {"type": "custom","tokenizer": "standard","filter":["lowercase","my_synonyms"]}}}},"mappings": {"properties": {"content": {"type": "text","analyzer": "standard","search_analyzer": "my_analyzer"}}}}
运行完上的指令后,我们来创建一个文档:
PUT myindex1/_doc/1{"content": "I love elastic stack"}
然后我们做如下的搜索:
GET myindex1/_search{"query": {"match": {"content": "elk"}}}
上面的搜索结果显示:
{"took" : 451,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.5753642,"hits" : [{"_index" : "myindex1","_type" : "_doc","_id" : "1","_score" : 0.5753642,"_source" : {"content" : "I love elastic stack"}}]}}
显然,我可以看到搜索 elk,我们就可以搜索到含有 elastic stack 的文档。
在实际的使用中,如果我们更新 synonyms.txt 文件,那么,我们可以使用如下的 API 来进行更新:
POST myindex1/_reload_search_analyzers
针对这种情况,我们可以在建立索引的时候,就把同义词建立好。这样,我们可以在 query 时,不使用同义词解析。在这种情况下,我们可以使用 synonym 过滤器,而不是 synonym_graph 过滤器。
我们接下来使用如下的命令来创建一个新的索引:
PUT myindex2{"settings": {"analysis": {"filter": {"my_synonyms": {"type": "synonym","synonyms": ["china, silk road, chn, PRC, People's Republic of China","elk, elastic stack"]}},"analyzer": {"my_analyzer": {"tokenizer": "standard","filter": ["lowercase","my_synonyms"]}}},"number_of_shards": 1},"mappings": {"properties": {"content": {"type": "text","analyzer": "my_analyzer"}}}}
在上面,我们使用了 my_analyzer 作为 myindex2 在索引时使用的分词器。它将使用 synonym 过滤器,并把如下的词视为同义词:
"china, silk road, chn, PRC, People's Republic of China","elk, elastic stack"
我们可以使用如下的方法来测试这个 analyzer:
POST myindex2/_analyze{"text": "I like elk a lot","analyzer": "my_analyzer"}
上面的命令显示的结果是:
{"tokens" : [{"token" : "i","start_offset" : 0,"end_offset" : 1,"type" : "<ALPHANUM>","position" : 0},{"token" : "like","start_offset" : 2,"end_offset" : 6,"type" : "<ALPHANUM>","position" : 1},{"token" : "elk","start_offset" : 7,"end_offset" : 10,"type" : "<ALPHANUM>","position" : 2},{"token" : "elastic","start_offset" : 7,"end_offset" : 10,"type" : "SYNONYM","position" : 2},{"token" : "a","start_offset" : 11,"end_offset" : 12,"type" : "<ALPHANUM>","position" : 3},{"token" : "stack","start_offset" : 11,"end_offset" : 12,"type" : "SYNONYM","position" : 3},{"token" : "lot","start_offset" : 13,"end_offset" : 16,"type" : "<ALPHANUM>","position" : 4}]}
你可以看到,尽管在测试的 text 没有 elastic stack,只有 elk,但是显示的结果了含有 elastic 及 stack 这两个 token。
我们接下来使用如下的命令来创建一个文档:
PUT myindex2/_doc/1{"content": "I like elk a lot"}
我们使用如下的查询:
GET myindex2/_validate/query?rewrite=true{"query": {"match": {"content": "elastic stack"}}}
上面显示的结果是:
{"_shards" : {"total" : 1,"successful" : 1,"failed" : 0},"valid" : true,"explanations" : [{"index" : "myindex2","valid" : true,"explanation" : """content:"elastic stack" content:elk"""}]}
从上面的显示的结果来看,当我们搜索 elastic stack 时,它同时匹配 content: “elastic stack” 以及 content: elk。也就是说,如果文档里含有 elk,那么这个文档也将被搜索到。我们做如下的搜索:
GET myindex2/_search{"query": {"match": {"content": "elastic stack"}}}
那么上面的命令显示的结果是:
{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.977273,"hits" : [{"_index" : "myindex2","_type" : "_doc","_id" : "1","_score" : 0.977273,"_source" : {"content" : "I like elk a lot"}}]}}
显然它已经把我们的想要的结果搜索出来了。
在上面,我们展示了两种方法进行同义词的查询。在实际的使用中,你可以根据自己的情况适当进行选择。当然,我们有可以把上面的两种方法进行同时并用。通过这两种方法,也有可能会造成搜索的精确度的问题。这个是你必须要想清楚的。这个就像我们撒网打鱼一样,把网撒大了,捞上来的也有可能不是我们想要的。
原文链接:https://blog.csdn.net/UbuntuTouch/article/details/108003222
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号