MongoDB聚合中如何按组统计布尔字段isReserved的学生总数?
MongoDB聚合:统计关联组中已预留学生数的实现方案
问题场景
现有两个MongoDB集合:
Groups:结构为{_id : ObjectId, title: String}Students:结构为{mainGroups: String, isReserved: Boolean}
其中Students的mainGroups字段是学生参与的所有组ID拼接成的字符串。当前聚合管道已能关联每个组对应的学生,并统计学生总数studentCount,需要新增totalStudentsReserved字段,统计每个组中isReserved: true的学生数量。
现有聚合代码:
[ { $addFields: { gid: { $toString: "$_id" } } }, { $lookup: { from: 'students', let: { groupId: "$gid" }, pipeline: [ { $match: { $expr: { $regexMatch: { input: "$mainGroups", regex: { $concat: [".*", "$$groupId", ".*"] }, options: "i" } } } } ], as: "students" } }, { $project: { _id: 1, gid: 1, title: 1, studentCount: { $size: "$students" } } } ]
解决方案
方案一:在$project阶段直接筛选统计
不需要额外的$group阶段,直接在$project中通过$filter和$size组合实现需求,修改后的完整管道如下:
[ { $addFields: { gid: { $toString: "$_id" } } }, { $lookup: { from: 'students', let: { groupId: "$gid" }, pipeline: [ { $match: { $expr: { $regexMatch: { input: "$mainGroups", regex: { $concat: [".*", "$$groupId", ".*"] }, options: "i" } } } } ], as: "students" } }, { $project: { _id: 1, gid: 1, title: 1, studentCount: { $size: "$students" }, totalStudentsReserved: { $size: { $filter: { input: "$students", cond: { $eq: ["$$this.isReserved", true] } } } } } } ]
逻辑说明:
$filter遍历students数组,筛选出所有isReserved为true的元素,返回筛选后的新数组$size获取该新数组的长度,即为当前组中已预留的学生总数
方案二:在$lookup内部提前统计(适合大数据量场景)
如果关联的学生数据量较大,可以在$lookup的内部管道中直接完成统计,减少后续数据传输和处理量:
[ { $addFields: { gid: { $toString: "$_id" } } }, { $lookup: { from: 'students', let: { groupId: "$gid" }, pipeline: [ { $match: { $expr: { $regexMatch: { input: "$mainGroups", regex: { $concat: [".*", "$$groupId", ".*"] }, options: "i" } } } }, { $group: { _id: null, total: { $sum: 1 }, reservedTotal: { $sum: { $cond: [{ $eq: ["$isReserved", true] }, 1, 0] } } } } ], as: "studentStats" } }, { $project: { _id: 1, gid: 1, title: 1, studentCount: { $arrayElemAt: ["$studentStats.total", 0] }, totalStudentsReserved: { $arrayElemAt: ["$studentStats.reservedTotal", 0] } } } ]
逻辑说明:
- 内部管道先匹配出当前组对应的所有学生
- 用
$group统计:total:匹配到的学生总数($sum:1累加计数)reservedTotal:用$cond判断,若isReserved为true则加1,否则加0,最终得到预留学生数
- 最后通过
$arrayElemAt从studentStats数组中取出统计值(因为内部$group后只会生成一个统计文档)
内容的提问来源于stack exchange,提问作者plibr
相关产品推荐
相关产品推荐

