同义词分析器是一款十分灵活的工具,但可能会导致人们在特定情形中过度使用。例如,有时人们会强行用其来替代词干提取器,这样便会导致同义词文件很大,因为其中包含动词和名词的各种语法变形(针对英语)。尽管这种方法可能奏效,但相较于使用真正的词干提取器或词形还原工具,通常性能较差而且维护起来也较为困难。用其来纠正拼写错误的话,后果也是这样。如果仅有几个特别常见的拼写错误,例如针对电商平台,尝试通过使用同义词来纠正有时还算可取。但如果问题更加广泛,那么使用模糊查询或字符级别的ngram方法可能更具有持续性。在分析链中还可考虑使用同义词扩展法的替代方案。有时,相对于在限制性更强的分析过程中使用同义词,在采集管道或某些其他客户端过程中改善文档反倒更加灵活和易于管理。例如,您可以使用命名实体识别(NER)框架对您文档中的命名实体进行检测,然后在您的预处理管道中或者在采集时以您特有的标识符对其进行编码。如果您随后对用户的查询应用同样的过程,然后再将它们发送到Elasticsearch,您可以实现同样的效果,但通常还能拥有更多控制权。
此外,您可能还会倾向于使用同义词来处理其他“相同”概念,例如将特定动物物种分组到一个常用字词下,甚至针对您的领域构建分类学支持内容。这时事情就会变得特别有趣,也有很多问题需要探索,但要记住同义词有时并非最佳选择,如使用不慎可能会导致您的系统出现异常行为。
同义词在分析器中使用,其既可在索引时使用,也可在搜索时使用。关于在Elasticsearch中如何使用同义词筛选器,最常见的问题之一就是:“我应该索引时使用,还是搜索时使用,还是同时都用?”我们首先看一下在_索引时_应用同义词筛选。这意味着会在索引后的文档中对字词进行一次性替换或扩展,结果将一直保存在搜索索引中。
索引时使用同义词有几个劣势:
最后两条尤其是巨大劣势。索引时应用同义词的唯一潜在好处是性能好,因为您在前期已费心完成了扩展过程,所以无需再在每次查询时完成一遍扩展过程,这有可能致使需要与更多的字词进行匹配。然而这一点在实践中通常并非真正的问题。
相反,在搜索时所用的分析工具中使用同义词则可以避免很多上述问题:
这些优势通常要高出唯一的劣势,即每次查询时都必须执行同义词扩展操作,这有可能导致需要匹配更多字词。不仅如此,搜索时扩展同义词还能够允许使用更加复杂的synonym_graph词元筛选器,这一工具能够正确处理多单词同义词,并且仅可在搜索分析器中使用。
一般而言,搜索时使用同义词的优势通常要高于索引时使用同义词可能实现的微小性能改进。
然而,如果在搜索时使用同义词,过去还需要注意另外一个问题。尽管更改同义词规则不需要对文档进行重新索引,但是如要更改的话,您必须暂时关闭再重新打开索引。这一点很有必要,因为分析器在下列时候才会创建实例:创建索引时,重启节点时,以及重新打开已关闭的索引时。为了让对同义词规则文件所做的变更对索引可见,用户必须首先在所有节点上更新文件,然后再关闭并重新打开索引。但是这个问题已经得以解决。
从Elasticsearch7.3开始,无需重新打开索引便能看到同义词文件中的变更。我们新增了一个端点,让用户能够按需触发分析器资源的重新加载操作。调用这个新端点将会重新加载索引中的所有分析器,前提是这些索引中的组件已被标记为可更新。相应地,这会使这些组件只能在搜索时使用。
对同义词筛选器而言,将其标记为可更新并调用“重新加载API”会使每个节点上的同义词配置文件对分析过程可见。虽然仍不能(通过同义词参数)更新筛选器定义中的同义词规则,但这些同义词规则应该主要用于偶尔的测试目的。无论何种情况,用配置文件来配置同义词有几个优势:
为演示起见,我们假设您将包含下列单条规则的初始my_synonyms.txt文件添加到Elasticsearch节点的config目录中。我们假设此文件最初仅包含下列一条规则:
universe,cosmos接下来,我们需要定义一个分析器,并让其在同义词筛选器中引用此文件:
PUT/synonym_test{"settings":{"index":{"analysis":{"analyzer":{"synonym_analyzer":{"tokenizer":"whitespace","filter":["my_synonyms"]}},"filter":{"my_synonyms":{"type":"synonym","synonyms_path":"my_synonyms.txt","updateable":true}}}}}}请注意我们将同义词筛选器标记为了updateable(可更新)。这一点很重要,因为当我们调用新的重新加载端点时,只会重新加载可更新的筛选器;但这样做也有缺点,因为在索引时不允许使用包含可更新筛选器的分析器。但是我们首先来检查一下同义词是否已正确应用,可以通过_analyze端点运行一个简短的测试:
GET/synonym_test/_analyze{"analyzer":"synonym_analyzer","text":"cosmos"}此操作应该会返回两个词元,正如我们所料,其中一个是“universe”。我们接下来通过添加第二行向synonyms.txt文件添加另一条规则:
lift,elevator如果使用之前版本,您此时必须关闭并重新打开索引,以便显示这些变更。现在您可以简单地调用新端点:
POST/synonym_test/_reload_search_analyzers虽然此请求并不要求有正文,但可能会限制为一个或多个使用典型索引通配符模式的索引。响应中包括的信息有已重新加载了哪些分析器,以及哪些节点受到了影响:
{[...],"reload_details":[{"index":"synonym_test","reloaded_analyzers":["synonym_analyzer"],"reloaded_node_ids":["FXbmbgG_SsOrNRssrYcPow"]}]}现在对字词“lift”运行上面的_analyze请求,也会返回“elevator”(作为第二个同义词词元)。
然而,还有一些注意事项。如上面提到的,搜索时应该使用已标记为updateable(可更新)的筛选器,所以在字段层面使用上面所定义的同义词分析器的正确方式如下:
另外一个有关同义词筛选器的常见问题是其在更复杂分析链中的行为。在大多数情况下,您会在同义词筛选器之前加入一些常见的字符或词元筛选器,例如lowercase(小写)筛选器。这意味着流经分析链的所有词元都会变为小写,然后才会应用同义词筛选器。这是否意味着同义词规则中的输入同义词也需要变为小写才能匹配呢?我们通过这个简单的示例看一下:
PUT/test_index{"settings":{"index":{"analysis":{"analyzer":{"synonym_analyzer":{"tokenizer":"whitespace","filter":["lowercase","my_synonyms"]}},"filter":{"my_synonyms":{"type":"synonym","synonyms":["Eins,Uno,One","Cosmos=>Universe"]}}}}}}GET/test_index/_analyze{"analyzer":"synonym_analyzer","text":"one"}在上面的示例中,您可以验证小写的输入文本扩展成了三个词元,这表示小写操作也会应用到同义词筛选器的规则中。同样,右边的替换规则(例如“Cosmos=>Universe”规则)也会重新编写,正如您看到的下边示例的小写输出:
GET/test_index/_analyze{"analyzer":"synonym_analyzer","text":"cosmos"}一般而言,对于提供给前面分析链中所用分词器和筛选器的输入,同义词筛选器会对其进行重新编写。然后,这一规则有一些值得注意的例外情况:多个会输出堆叠式词元的筛选器(例如common_grams或phonetic筛选器)均不允许在同义词筛选器之前使用,如果您尝试这样做,系统会报错。对于其他筛选器,例如复合词筛选器或同义词筛选器自身,如果它们在分析链中位于另一个同义词筛选器之前,则会跳过他们。后面一条规则对于实现同义词筛选器链路十分重要。我们可以在下面的示例中看一下实现过程。
如果您连着使用两个或多个同义词筛选器,会怎样呢?前项的输出会成为后项的输入吗,也就是将同义词筛选器的链接操作转变为部分意义上的传递操作?我们用下面的示例来试一下:
PUT/synonym_chaining{"settings":{"index":{"analysis":{"filter":{"first_synonyms":{"type":"synonym","synonyms":["a=>b","e=>f"]},"second_synonyms":{"type":"synonym","synonyms":["b=>c","d=>e"]}},"analyzer":{"synonym_analyzer":{"filter":["first_synonyms","second_synonyms"],"tokenizer":"whitespace"}}}}}}GET/synonym_chaining/_analyze{"analyzer":"synonym_analyzer","text":"a"}输出词元为“c”,这表示两个筛选器已按顺序得以应用,即第一个筛选器将“a”替换为“b”,第二个筛选器继而将这一输入替换为“c”。如果您尝试将输入改为“d”,会将其替换为“e”(并未应用第一条规则);但如果您将输入改为“e”,会根据第一条规则将这个词元替换为“f”,导致第二个筛选器根本没有可以匹配的内容。
还记得吗?我们刚才讲到了基于前面的词元筛选器进行重新编写时有一些例外情况。如果上面的second_synonyms(第二组同义词)筛选器对其规则集应用了第一个筛选器的规则,那么它会将自身的规则d=>e更改为d=>f(因为会应用前面筛选器的规则e=>f)。在Elasticsearch的早期版本中,这一行为过去常常给人们造成困扰,因此现在处理后面筛选器中的同义词规则时,会跳过同义词筛选器。在6.6及之后的版本中,它将会按照我们所描述的那样运行。
Elastic是一家专注搜索的企业。作为ElasticStack(Elasticsearch、Kibana、Beats和Logstash)的开发者,Elastic构建了自管理型和SaaS型产品。