MongoDB聚合需求:同时汇总根节点数据与子数组数据
如何在Mongo聚合中同时汇总根层级与子数组数据?
输入数据
[ { id: ID, points: 10, sources: [{ sourceId: 's1', sourcePoints: 6, }, { sourceId: 's2', sourcePoints: 4, }] }, { id: ID, points: 2, sources: [{ sourceId: 's1', sourcePoints: 2, }] } ];
期望汇总结果
{ id: ID, points: 12, // 所有匹配id的根层级points总和 sources: { s1: { sourcePoints: 8 }, // 所有匹配id的s1来源sourcePoints总和 s2: { sourcePoints: 4 } // 所有匹配id的s2来源sourcePoints总和 } }
已实现部分
已能通过以下管道完成根层级points的求和:
[ { $match: { id: ID } }, { $group: { _id: '$id', sum: { $sum: '$points' }} } ]
完整聚合方案
要同时完成子数组sources的分组求和并整理成目标格式,可使用以下扩展聚合管道:
[ { $match: { id: ID } }, // 拆分sources数组,每个元素转为独立文档 { $unwind: '$sources' }, // 第一层分组:按id+sourceId分别计算总和 { $group: { _id: { id: '$id', sourceId: '$sources.sourceId' }, totalPoints: { $sum: '$points' }, totalSourcePoints: { $sum: '$sources.sourcePoints' } } }, // 第二层分组:按id汇总所有数据 { $group: { _id: '$_id.id', totalPoints: { $first: '$totalPoints' }, sources: { $push: { k: '$_id.sourceId', v: { sourcePoints: '$totalSourcePoints' } } } } }, // 将sources数组转换为键值对对象 { $addFields: { sources: { $arrayToObject: '$sources' } } }, // 调整字段名匹配期望格式 { $project: { _id: 0, id: '$_id', points: '$totalPoints', sources: 1 } } ]
关键步骤说明
$unwind:拆分sources数组,让每个来源项单独参与后续分组计算,避免数组嵌套导致的求和困难- 第一层
$group:以id+sourceId为分组键,同时计算根层级points总和(同一id的拆分文档points值一致,求和结果等价于总合)和对应sourceId的sourcePoints总和 - 第二层
$group:以id为分组键,汇总根层级总点数,并将每个来源的汇总结果收集为{k: sourceId, v: {sourcePoints: 总和}}格式的数组 $arrayToObject:将上述数组转换为键值对对象,直接得到期望的sources结构$project:重命名字段,移除_id,将totalPoints改为points,_id改为id,完全匹配目标输出格式
内容的提问来源于stack exchange,提问作者memyrok99
相关产品推荐
相关产品推荐

