如何在ElasticSearch中对对象数组执行精确匹配查询
问题描述
现有Elasticsearch索引定义如下:
PUT /orchards { "mappings": { "properties": { "fruits": { "type": "nested", "properties": { "farms": { "type": "long" }, "visits": { "type": "long" }, "counts": { "type": "long" } } } } } }
已写入以下两个文档:
文档1:
POST orchards/_doc { "fruits" : [ { "farms" : 1, "visits" : 1, "counts" : [1] } ] }
文档2:
POST orchards/_doc { "fruits" : [ { "farms" : 1, "visits" : 1, "counts" : [1] }, { "farms" : 2, "visits" : 2, "counts" : [1,2] } ] }
需求是编写查询仅返回精确匹配fruits数组为以下内容的文档:
"fruits" : [ { "farms" : 1, "visits" : 1, "counts" : [1] } ]
尝试过普通的nested查询,但会同时返回两个文档,无法实现精确匹配。因脚本操作性能开销大,希望避免使用脚本,求可行的查询方案。
解决方案
要实现无脚本的精确匹配,需要从两个维度限制:
- 确保所有
fruits嵌套对象都符合目标条件(排除包含其他不符合条件的嵌套对象的文档) - 限制
fruits数组的长度为1(排除包含多个嵌套对象的文档)
步骤1:添加数组长度字段(可选,若已有则跳过)
由于Elasticsearch默认不存储数组长度,且无脚本无法直接查询数组长度,需给索引添加一个存储fruits数组长度的字段:
PUT /orchards/_mapping { "properties": { "fruits_length": { "type": "integer" } } }
然后重新索引所有文档,给每个文档补充fruits_length字段(值为fruits数组的元素个数):
- 文档1的
fruits_length设为1 - 文档2的
fruits_length设为2
步骤2:编写精确匹配查询
结合bool查询和nested查询,同时满足三个条件:
- 过滤
fruits_length为1,确保数组只有一个元素 - 匹配符合目标条件的嵌套对象
- 排除存在任何不符合目标条件的嵌套对象
查询语句如下:
GET /orchards/_search { "query": { "bool": { "filter": [ {"term": {"fruits_length": 1}} ], "must": [ { "nested": { "path": "fruits", "query": { "bool": { "must": [ {"term": {"fruits.farms": 1}}, {"term": {"fruits.visits": 1}}, {"term": {"fruits.counts": 1}} ] } } } } ], "must_not": [ { "nested": { "path": "fruits", "query": { "bool": { "should": [ {"bool": {"must_not": {"term": {"fruits.farms": 1}}}}, {"bool": {"must_not": {"term": {"fruits.visits": 1}}}}, {"bool": {"must_not": {"term": {"fruits.counts": 1}}}} ], "minimum_should_match": 1 } } } } ] } } }
说明
filter部分限制数组长度,确保只返回单元素的fruits数组文档must部分匹配目标嵌套对象,确保文档包含符合要求的元素must_not部分排除任何包含不符合条件的嵌套对象的文档,避免误匹配
内容的提问来源于stack exchange,提问作者shady2k
相关产品推荐
相关产品推荐

