Elasticsearch中flattened字段数组对象的must查询匹配问题
问题:Flattened字段上对象数组的关联查询失效
在包含对象数组的flattened字段上执行must查询时,期望匹配数组中单个元素同时满足所有条件,但实际匹配了数组中任意元素满足各自条件的文档。
文档示例
[ { "_source": { "metadata": { "name": "banana", "seasonalities": [ { "date": "2023-01-01", "country": "AA" }, { "date": "2023-01-22", "country": "BB" } ] } }, { "_source": { "metadata": { "name": "potato", "seasonalities": [ { "date": "2023-01-01", "country": "AA" }, { "date": "2023-01-01", "country": "BB" }, { "date": "2023-01-01", "country": "CC" }, { "date": "2023-01-22", "country": "DD" }, { "date": "2023-01-23", "country": "EE" } ] } } ]
有效查询示例
以下两个查询均可正确返回单个文档:
查询1
{ "query": { "bool": { "filter": [ { "bool": { "must": [ { "term": { "metadata.seasonalities.date": {"value":"2023-01-22"} } }, { "term": { "metadata.name": {"value":"banana"} } } ] } } ] } } }
查询2(简化版)
{ "query": { "bool": { "must": [ { "term": { "metadata.seasonalities.date": "2023-01-22" } }, { "term": { "metadata.name": "banana" } } ] } } }
失效查询示例
执行以下查询时,返回了两个文档而非预期的第一个:
{ "query": { "bool": { "must": [ { "term": { "metadata.seasonalities.date": "2023-01-22" } }, { "term": { "metadata.seasonalities.country": "BB" } } ] } } }
索引映射
当前索引映射如下:
"mappings": { "properties": { "metadata": { "type": "flattened" } } },
问题补充
在实际索引中执行带explain的查询后,得到943条结果,但根据explain信息,包含2023-01-22的文档有177条,包含BB的文档有584条,预期仅返回两者交集的少量文档。尝试将term改为match无效,使用的是ES7.13.3版本。
解决方案
原因分析
flattened类型会将整个嵌套结构扁平化为键值对集合,丢失了对象数组中元素的字段关联性——也就是说,Elasticsearch无法判断metadata.seasonalities.date:2023-01-22和metadata.seasonalities.country:BB是否属于数组中的同一个对象,只要文档中存在任意一个对象满足date条件、任意一个对象满足country条件,就会被匹配。
方法1:修改映射为Nested类型(推荐)
Nested类型会将数组中的每个对象作为独立的子文档存储,能完整保留对象内部字段的关联性。
更新后的映射
"mappings": { "properties": { "metadata": { "properties": { "name": {"type": "keyword"}, "seasonalities": { "type": "nested", "properties": { "date": {"type": "date"}, "country": {"type": "keyword"} } } } } } }
对应的查询
使用nested查询包裹关联条件,指定路径为对象数组的字段名:
{ "query": { "bool": { "must": [ { "nested": { "path": "metadata.seasonalities", "query": { "bool": { "must": [ {"term": {"metadata.seasonalities.date": "2023-01-22"}}, {"term": {"metadata.seasonalities.country": "BB"}} ] } } } } ] } } }
方法2:添加复合字段(无需修改映射的折中方案)
如果无法修改现有索引映射(比如已有大量数据无法重新索引),可以在写入文档时,给每个数组对象添加一个复合字段(如date_country),将需要关联的字段值拼接成一个字符串,查询时直接匹配这个复合字段。
写入时的文档示例
{ "_source": { "metadata": { "name": "banana", "seasonalities": [ { "date": "2023-01-01", "country": "AA", "date_country": "2023-01-01_AA" }, { "date": "2023-01-22", "country": "BB", "date_country": "2023-01-22_BB" } ] } } }
对应的查询
{ "query": { "term": {"metadata.seasonalities.date_country": "2023-01-22_BB"} } }
内容的提问来源于stack exchange,提问作者Josep Valls
相关产品推荐
相关产品推荐

