Elasticsearch 7.11同义词分词器分词位置结果生成原理咨询
整体分词执行链路
你配置的my_synonyms分词器完全遵循ES固定的分词执行顺序,按先后完成三个环节:
- 使用配置的
standard分词器对原始文本切分,输出带有基础位置标记的初始token流 - 按配置顺序依次执行两个token过滤器:
lowercase、my_synonym_filter - 输出最终带位置标记的分词结果
第一步:初始分词与小写转换的输出结果
输入文本为The United States is wealthy,经过standard分词器切分、lowercase过滤器转小写处理后,会得到连续的基础token流,每个token占一个独立的位置(position从0开始递增):
| 位置 | 小写化后的原始token |
|---|---|
| 0 | the |
| 1 | united |
| 2 | states |
| 3 | is |
| 4 | wealthy |
这一步没有任何同义词替换逻辑,输出结果和普通标准分词器的结果完全一致。
第二步:同义词过滤器生成异常结果的核心逻辑
你配置的my_synonym_filter使用的是ES 7.11内置的老版synonym类型过滤器,这个过滤器处理多词同义词时的逻辑和大多数人的直觉不符,也是你看到错位同位置token的根本原因。
同义词规则的解析方式
你在配置里写的规则"usa,united states,u s a,united states of america",会被过滤器自动解析为4个互相等价的token序列,每个序列的组成、长度分别为:
- 长度1的序列:
usa - 长度2的序列:
united+states - 长度3的序列:
u+s+a - 长度4的序列:
united+states+of+america
老版synonym的匹配缺陷
老版synonym过滤器不会严格校验连续token是否真的能匹配上述任意一个序列的完整前缀,只要在token流的某个位置,匹配到上述等价序列里任意一个序列的首个token(本例中位置1的原token是united,正好是长度2、长度4两个序列的首个token),它就会把这4个等价序列,从匹配起始的位置(也就是位置1)开始,把每个序列的token按自身的偏移量直接铺到对应的流位置上,完全不校验后续位置的原token和序列要求的内容是否一致。
按照这个逻辑铺开后,每个位置最终会合并的token和你实际查询到的结果完全一致:
| 位置 | 保留的原token | 从等价序列铺入的token | 该位置最终的全部token |
|---|---|---|---|
| 0 | the | 无(所有等价序列从位置1开始铺入) | the |
| 1 | united | 长度1序列的usa、长度2序列的united、长度3序列的u、长度4序列的united | united、usa、u、united(也就是你看到的第一组同位置token) |
| 2 | states | 长度2序列的states、长度3序列的s、长度4序列的states | states、s、states(也就是你看到的第二组同位置token) |
| 3 | is | 长度3序列的a、长度4序列的of | is、a、of(也就是你看到的第三组同位置token) |
| 4 | wealthy | 长度4序列的america | wealthy、america(也就是你看到的第四组同位置token) |
因为同位置的所有token不会改变自己的position值,所以查询结果里这些同组的token就会显示为相同位置。
补充说明
这种非预期的token错位,本质是老版synonym过滤器的设计局限:它从早期Lucene版本继承的匹配逻辑,本身不支持对多词同义词做严格的连续序列匹配,也不会生成支持短语查询的正确位置增量。如果需要正确的多词同义词匹配效果,应该替换为synonym_graph类型的过滤器,它会严格按连续序列做匹配,生成带正确位置增量的token,不会出现把同义词token铺到无关文本位置上的问题。
内容的提问来源于stack exchange,提问作者Tom

