如何使用MongoDB聚合对层级地址数组分组统计各层级用户数
MongoDB 层级地址用户数统计实现方案
可以通过MongoDB聚合操作实现该需求,完整聚合管道如下:
db.user.aggregate([ // 阶段1:生成每个用户所有层级的地址节点,记录完整路径、节点名、层级深度 { $addFields: { levels: { $reduce: { input: "$location", initialValue: [], in: { $concatArrays: [ "$$value", [ { path: { $concatArrays: [ "$$value.path", [ "$$this" ] ] }, name: "$$this", depth: { $add: [ { $size: "$$value.path" }, 1 ] } } ] ] } } } } }, // 阶段2:打散所有层级节点为独立文档 { $unwind: "$levels" }, // 阶段3:按地址完整路径分组,统计对应节点的用户数量 { $group: { _id: "$levels.path", name: { $first: "$levels.name" }, depth: { $first: "$levels.depth" }, userCount: { $sum: 1 } } }, // 阶段4:按层级深度升序排序,方便后续构建嵌套树结构 { $sort: { depth: 1 } }, // 阶段5:聚合所有节点,通过JS函数构建嵌套的层级树 { $group: { _id: null, nodes: { $push: "$$ROOT" } } }, { $project: { _id: 0, result: { $function: { body: function(nodes) { const nodeMap = {}; const rootNodes = []; // 初始化所有节点的items字段,建立路径映射 nodes.forEach(node => { nodeMap[node._id.join('|')] = { name: node.name, userCount: node.userCount, items: [] }; }); // 遍历节点挂载到对应父节点下 nodes.forEach(node => { const currentPath = node._id; const currentNode = nodeMap[currentPath.join('|')]; if (currentPath.length === 1) { rootNodes.push(currentNode); return; } const parentPath = currentPath.slice(0, -1); const parentNode = nodeMap[parentPath.join('|')]; parentNode && parentNode.items.push(currentNode); }); return rootNodes; }, args: [ "$nodes" ], lang: "js" } } } }, // 阶段6:格式化输出结构,和预期格式完全对齐 { $unwind: "$result" }, { $replaceRoot: { newRoot: "$result" } } ])
注意事项
- 上述方案依赖MongoDB 4.4及以上版本的
$function操作符支持 - 如果你使用的MongoDB版本不支持
$function,可以只运行前4个聚合阶段拿到扁平的各层级统计数据,把树结构构建逻辑迁移到应用层实现 - 统计逻辑为只要用户地址包含对应层级节点就算入该节点用户数,和需求预期的统计结果完全匹配
内容的提问来源于stack exchange,提问作者Ramesh Kithsiri HettiArachchi
相关产品推荐
相关产品推荐

