MongoDB:无需指定子字段名检查元素及查询失效原因咨询
如何在不指定子字段名称的情况下检查字段下所有子字段的元素?
需求说明
- 遍历目标字段
field下的所有子字段(如示例中的sub1、sub2),检查是否存在元素的set字段值为IC42 - 禁止使用MongoDB Atlas Search
示例文档
{ "field": { "sub1": [{"set": "IC42", "code": 910}, {"set": "IC38", "code": 911}], "sub2": [{"set": "BH90", "code": 912}, {"set": "BH70", "code": 913}] } }
补充问题
此前使用的聚合查询在子字段元素字段较少时有效,但为元素添加更多字段后失效,查询语句如下:
db.collection.aggregate([ { $match: { $expr: { $ne: [ { $filter: { input: { $objectToArray: "$field" }, cond: { $in: [{"set": "IC42"}, "$$this.v"] } } }, [] ] } } } ])
请问失效原因是什么?该如何解决?
一、旧查询失效的原因
旧查询中的$in: [{"set": "IC42"}, "$$this.v"]是检查子字段数组$$this.v中是否存在完全等于{"set": "IC42"}的元素:
- 当子字段的元素只有
set单个字段时,两者完全匹配,查询正常生效; - 当元素新增
code等其他字段后,目标元素变成{"set": "IC42", "code": 910}这类多字段对象,和{"set": "IC42"}不再完全相等,$in无法匹配到目标元素,最终过滤结果为空,查询失效。
二、正确的解决方法
需要遍历子字段的数组元素,单独检查每个元素的set字段值,而非匹配整个对象。以下是两种可行方案:
方案1:嵌套$anyElementTrue+$map实现
db.collection.aggregate([ { $match: { $expr: { $anyElementTrue: [ $map: { input: { $objectToArray: "$field" }, as: "subField", in: { $anyElementTrue: [ $map: { input: "$$subField.v", as: "item", in: { $eq: ["$$item.set", "IC42"] } } ] } } ] } } } ])
逻辑说明
$objectToArray: "$field":将field对象转换为[{k: "sub1", v: [...]}, {k: "sub2", v: [...]}]格式的键值对数组;- 外层
$map:遍历每个子字段的键值对,对每个子字段的数组$$subField.v做处理; - 内层
$map:遍历子字段数组中的每个元素,返回$$item.set == "IC42"的布尔值数组; - 内层
$anyElementTrue:判断当前子字段的数组中是否存在符合条件的元素; - 外层
$anyElementTrue:判断所有子字段中是否有至少一个子字段包含目标元素。
方案2:$reduce+$or实现
db.collection.aggregate([ { $match: { $expr: { $reduce: { input: { $objectToArray: "$field" }, initialValue: false, in: { $or: [ "$$value", { $anyElementTrue: [ $map: { input: "$$this.v", as: "item", in: { $eq: ["$$item.set", "IC42"] } } ] } ] } } } } } ])
逻辑说明
用$reduce遍历所有子字段,初始值设为false;只要遍历到任意一个包含目标元素的子字段,就将结果更新为true,最终返回是否存在符合条件的元素。
内容的提问来源于stack exchange,提问作者Lilac
相关产品推荐
相关产品推荐

