真实配置索引时词干还原、查询时不还原的不对称analyzer,验证这种不一致导致的隐蔽查询失败,并用index-time同义词展开的真实反例说明不一致的后果取决于具体差异点而非一概而论。
代码PUT /mismatch2 {"settings":{"analysis":{ "filter": {"english_stemmer": {"type":"stemmer","language":"english"}}, "analyzer": { "stem_analyzer": {"type":"custom","tokenizer":"standard","filter":["lowercase","english_stemmer"]}, "plain_analyzer": {"type":"custom","tokenizer":"standard","filter":["lowercase"]} } }},"mappings":{"properties":{ "body": {"type":"text","analyzer":"stem_analyzer","search_analyzer":"plain_analyzer"} }}}} 索引文档: {"body": "the dog runs fast"}
索引时stem_analyzer对"runs running"分词: → ['run', 'run'] ← 两个不同的原始形式真实都被还原成了同一个词干run 查询时plain_analyzer(跟索引用的不是同一个analyzer)对"running"分词: → ['running'] ← 真实原样保留,没有做任何词干还原
这组真实的分词结果对比,从根源上展示了analyzer不一致会造成什么样的"错位":文档写入时,body字段真实用stem_analyzer把"runs"还原成词干"run"存进了倒排索引,索引里压根没有"running"这个原始形式;而search_analyzer被真实配置成了不做词干还原的plain_analyzer,用户搜"running"的时候,这个词真实原样保留,根本不会被还原成"run"去匹配索引里真实存在的词项。
代码POST /mismatch2/_search {"query":{"match":{"body":"running"}}} POST /mismatch2/_search {"query":{"match":{"body":"run"}}} POST /mismatch2/_search {"query":{"match":{"body":{"query":"running","analyzer":"stem_analyzer"}}}}
搜"running"(用户真实会输入的自然说法): hits = 0 ← 真实查不到! 搜"run"(词干形式,用户通常不会这么输入): hits = 1 ← 真实反而能命中 查询时显式指定analyzer=stem_analyzer(修复了不一致): 搜"running": hits = 1 ← 真实命中了
代码index_time_analyzer真实配了同义词过滤器["laptop,notebook"],search_analyzer故意配成不认识这组同义词的standard 索引文档: {"body": "I bought a new laptop"} POST /mismatch_test/_search {"query":{"match":{"body":"notebook"}}}
search_analyzer=standard(真实不包含同义词过滤器): 搜"notebook": hits = 1 ← 真实依然命中,尽管查询端压根不知道laptop和notebook是同义词
这个真实反例补全了对analyzer不一致后果的完整理解:问题的根源真实不在于"两边analyzer是不是同一个",而在于"两边analyzer对这个具体查询词的处理结果是否恰好一致"。本次实测里,laptop在索引时真实已经被同义词过滤器同时展开成了laptop和notebook两个并列token存进了倒排索引(本系列第39篇验证过的机制),查询端哪怕完全不认识这组同义词,只要原样把"notebook"当成一个词去查,也真实能在索引里找到这个词项——因为索引端早就真实"提前"把同义关系兑现成了具体的token。这跟前面stemmer的例子刚好相反:stemmer的后果真实只存在于索引端产出的"run"和查询端产出的"running"字面不一致,而同义词展开如果真实发生在索引端,查询端反而可以更"无知",不需要重复认识同一套规则。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
stemmer token filter 与 search_analyzer 映射