Elasticsearch动态映射下term查询无法匹配createdBy字段特殊字符问题
咱们来拆解下你遇到的这个矛盾情况:明明createdBy字段是未分词的string类型,term精确匹配查不到结果,match查询却能正常返回,我帮你捋捋核心原因和解决思路:
核心原因:动态映射的“隐形”处理
你提到查看ES生成的映射显示createdBy是string类型,但这里大概率是个误解——在Elasticsearch 5.x及以后的版本里,原生的string类型已经被拆分为text(分词)和keyword(未分词)两种类型了。动态映射下,当你第一次插入字符串类型的字段值时,ES默认会把它映射为text类型,同时自动生成一个keyword子字段(比如createdBy.keyword)来支持精确匹配。
可能你看到的映射里只显示了主字段的类型,但实际上它是text而非你以为的未分词string。这就导致:
term查询针对text字段时,会尝试精确匹配分词后的词项,但google-oauth2|1234567890这类带特殊字符的字符串会被分词器拆分成多个词(比如google、oauth2、1234567890),你的term查询用完整字符串自然匹配不到任何词项;match查询针对text字段时,会先把查询字符串分词,再去匹配分词后的索引词,所以能找到对应的文档。
验证与解决步骤
确认真实的字段映射
执行以下命令查看createdBy字段的完整映射,验证我们的猜想:GET named-entities/_mapping/field/createdBy如果结果显示
createdBy是text类型,且包含keyword子字段,那就坐实了问题根源。修改
term查询的目标字段
既然精确匹配需要用未分词的keyword子字段,把查询改成下面这样就能正常返回结果了:GET named-entities/_search?size=1 { "query": { "term": { "createdBy.keyword": "google-oauth2|1234567890" } } }(可选)修改字段的默认映射
如果你希望createdBy字段默认就是未分词的类型,可以创建索引时手动指定映射,避免动态映射的自动处理:PUT named-entities { "mappings": { "properties": { "createdBy": { "type": "keyword" } // 其他字段的映射... } } }
还有一种小概率情况:如果你的createdBy字段确实是keyword类型,但查询字符串和实际存储的值存在细微差异(比如大小写、前后空格、不可见字符),这时候可以用match查询的operator参数做精确匹配测试,或者用_termvectors API查看文档的实际索引词项来排查。
内容的提问来源于stack exchange,提问作者echen

