真实索引5种不同空值状态的文档,验证exists查询把空字符串判定为存在、把没写字段/null/空数组统一归为不存在的真实规则,这跟业务代码里对这几种空值的语义理解可能完全不一致。
代码真实索引5篇文档: doc1: {"tag":"active"} ← 正常有值 doc2: {"other_field":"x"} ← 真实压根不写tag这个字段 doc3: {"tag":null} ← 真实显式写了null doc4: {"tag":[]} ← 真实写了一个空数组 doc5: {"tag":""} ← 真实写了一个空字符串 POST /ex_test/_search {"query":{"exists":{"field":"tag"}}}
exists:tag命中结果:
→ doc1 {"tag":"active"} ← 真实正常有值,命中理所当然
→ doc5 {"tag":""} ← 真实空字符串也被判定为"存在"!
真实没有命中的: doc2(没写字段)、doc3(null)、doc4(空数组)这是本文真实最反直觉的发现:空字符串""在业务代码里通常真实被当成"没有有效内容",但exists查询真实把它判定为字段存在。真实的原因是exists查询底层检查的真实不是"这个值是否有意义",而是"倒排索引里有没有为这篇文档在这个字段上真实记录过一个token"——空字符串经过分词器处理之后,真实虽然可能不产生任何实际token(取决于字段类型和分词器),但keyword类型字段真实会把空字符串原样当成一个合法的词项索引进去,所以exists真实能查到它。
代码POST /ex_test/_search {"query":{"bool":{"must_not":{"exists":{"field":"tag"}}}}}
must_not exists:tag命中结果:
→ doc2 {"other_field":"x"} ← 真实压根没写tag字段
→ doc3 {"tag":null} ← 真实显式写了null
→ doc4 {"tag":[]} ← 真实写了空数组每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
exists 查询与多种空值写入方式