如何在MongoDB聚合中实现任意深度嵌套父子数组树的扁平化
任意深度嵌套树形结构通用扁平化方案
目前MongoDB原生暂不支持你提到的$*.items这类递归通配符语法,但是有两种通用方案可以实现任意嵌套深度的树形结构扁平化,无需提前预知层级数量:
方案1:使用$function自定义递归逻辑(适配MongoDB 5.0+)
该方案通过服务端JS执行递归遍历,写法简单灵活,适配所有嵌套深度:
db.collection.aggregate([ { $addFields: { flatNodes: { $function: { body: function(root) { const res = [] const traverse = (node) => { // 提取节点信息,可自行决定是否保留items字段 const { items, ...nodeInfo } = node res.push(nodeInfo) Array.isArray(node.items) && node.items.forEach(traverse) } traverse(root) return res }, args: ["$$ROOT"], lang: "js" } } } }, // 若需要将每个节点拆分为独立文档,新增以下两个阶段即可 { $unwind: "$flatNodes" }, { $replaceRoot: { newRoot: "$flatNodes" } } ])
方案2:纯聚合运算符实现(适配MongoDB 4.4+,无需JS权限)
如果服务端未开启JS执行权限,可以通过迭代队列的方式实现,调整$range的最大值即可适配不同深度的嵌套:
db.collection.aggregate([ { $project: { flatResult: { $reduce: { // 100为支持的最大嵌套深度,可根据实际需求调整 input: { $range: [0, 100] }, initialValue: { processQueue: ["$$ROOT"], result: [] }, in: { $cond: [ { $eq: [{ $size: "$$value.processQueue" }, 0] }, "$$value", { processQueue: { $concatArrays: [ { $slice: ["$$value.processQueue", 1, { $size: "$$value.processQueue" }] }, { $ifNull: [{ $first: "$$value.processQueue.items" }, []] } ] }, result: { $concatArrays: [ "$$value.result", [{ id: { $first: "$$value.processQueue.id" }, type: { $first: "$$value.processQueue.type" } // 可在此处补充其他需要提取的节点字段 }] ] } } ] } } } } }, // 拆分节点为独立文档 { $unwind: "$flatResult.result" }, { $replaceRoot: { newRoot: "$flatResult.result" } } ])
内容的提问来源于stack exchange,提问作者Andrej K
相关产品推荐
相关产品推荐

