MongoDB递归查询嵌套字段:筛选含指定type的文档返回name
实现方法
由于你的文档嵌套层级不固定,需要递归遍历data字段的所有层级来检查是否存在type="20"的节点,以下是两种可行的方案:
方案1:用$function自定义递归检查函数(MongoDB 4.4+)
这个聚合查询会递归遍历每个文档的data结构,筛选出符合条件的文档并仅返回name字段:
db.collection.aggregate([ { $match: { $expr: { $function: { body: function checkHasType20(obj) { // 检查当前节点的type是否为"20" if (obj.type === "20") return true; // 遍历items数组里的子节点,递归检查 if (obj.items && Array.isArray(obj.items)) { for (const item of obj.items) { if (checkHasType20(item)) return true; } } return false; }, args: ["$data"], lang: "js" } } } }, { $project: { _id: 0, name: 1 } } ])
工作逻辑:
$function定义了一个递归函数,从data对象开始遍历:- 先判断当前节点的
type是否等于"20",是则返回true - 如果当前节点有
items数组,就逐个递归检查数组里的子节点 - 只要任意层级存在符合条件的节点,函数返回
true,文档会被$match保留
- 先判断当前节点的
$project阶段只保留name字段,去掉其他无关内容
方案2:迭代式检查(避免递归深度问题)
如果担心递归深度限制(虽然20层完全没问题),可以用栈实现迭代遍历,逻辑和递归一致但更稳妥:
db.collection.aggregate([ { $addFields: { hasType20: { $function: { body: function(obj) { const stack = [obj]; while (stack.length > 0) { const current = stack.pop(); if (current.type === "20") return true; if (current.items && Array.isArray(current.items)) { stack.push(...current.items); } } return false; }, args: ["$data"], lang: "js" } } } }, { $match: { hasType20: true } }, { $project: { _id: 0, name: 1 } } ])
工作逻辑:
- 用栈存储待检查的节点,初始时把
data对象压入栈 - 每次弹出一个节点检查
type,符合条件就返回true - 如果节点有
items数组,就把所有子节点压入栈继续检查 - 最后通过临时字段
hasType20筛选符合条件的文档,返回name
注意事项
$function需要MongoDB 4.4及以上版本,如果你用的是更低版本,只能用Map-Reduce(效率较低)或者提前扁平化数据结构- 数据量很大时,JavaScript函数的性能会比原生操作符差,这种情况下建议调整数据存储结构,比如扁平化嵌套的items数组
内容的提问来源于stack exchange,提问作者Yusuf Kayikci
相关产品推荐
相关产品推荐

