MongoDB聚合管道:按用户分组并保留计数
按用户分组统计工单信息的MongoDB聚合方案
我来帮你搞定这个需求,根据你给出的User和Ticket Schema,我整理了两种聚合管道方案,分别对应不同的统计深度:
基础版:快速统计已分配/已解决工单数量
如果只需要每个用户的assignedTickets和resolvedTickets的总数,不需要关联工单的详细信息,这个方案性能最优:
db.users.aggregate([ // 先计算每个用户的两类工单数量 { $addFields: { assignedCount: { $size: "$assignedTickets" }, resolvedCount: { $size: "$resolvedTickets" } } }, // 按用户分组(因为每个文档就是一个用户,这里分组是为了保留所有非数组字段) { $group: { _id: "$_id", firstName: { $first: "$firstName" }, lastName: { $first: "$lastName" }, assignedCount: { $first: "$assignedCount" }, resolvedCount: { $first: "$resolvedCount" }, createdAt: { $first: "$createdAt" }, updatedAt: { $first: "$updatedAt" } } }, // 可选:格式化输出,把_id改成更直观的userId { $project: { userId: "$_id", firstName: 1, lastName: 1, assignedCount: 1, resolvedCount: 1, createdAt: 1, updatedAt: 1, _id: 0 } } ])
这里用$size直接读取数组长度,避免了关联集合的开销,适合只需要数量的场景。
进阶版:关联工单详情,统计各类型工单数量
如果需要进一步统计已分配/已解决工单中不同type的数量(比如bug、feature各有多少),就需要先关联Ticket集合再统计:
db.users.aggregate([ // 关联已分配的工单详情 { $lookup: { from: "tickets", localField: "assignedTickets", foreignField: "_id", as: "assignedTicketDetails" } }, // 关联已解决的工单详情 { $lookup: { from: "tickets", localField: "resolvedTickets", foreignField: "_id", as: "resolvedTicketDetails" } }, // 计算总数+各类型统计 { $addFields: { // 已分配工单总数 assignedCount: { $size: "$assignedTicketDetails" }, // 已分配工单按类型统计,转成键值对格式 assignedTypeStats: { $arrayToObject: { $map: { input: { $setUnion: ["$assignedTicketDetails.type"] }, as: "type", in: { k: "$$type", v: { $size: { $filter: { input: "$assignedTicketDetails", cond: { $eq: ["$$this.type", "$$type"] } } } } } } } }, // 已解决工单总数 resolvedCount: { $size: "$resolvedTicketDetails" }, // 已解决工单按类型统计 resolvedTypeStats: { $arrayToObject: { $map: { input: { $setUnion: ["$resolvedTicketDetails.type"] }, as: "type", in: { k: "$$type", v: { $size: { $filter: { input: "$resolvedTicketDetails", cond: { $eq: ["$$this.type", "$$type"] } } } } } } } } } }, // 分组保留用户信息 { $group: { _id: "$_id", firstName: { $first: "$firstName" }, lastName: { $first: "$lastName" }, assignedCount: { $first: "$assignedCount" }, assignedTypeStats: { $first: "$assignedTypeStats" }, resolvedCount: { $first: "$resolvedCount" }, resolvedTypeStats: { $first: "$resolvedTypeStats" }, createdAt: { $first: "$createdAt" }, updatedAt: { $first: "$updatedAt" } } }, // 可选:格式化输出 { $project: { userId: "$_id", firstName: 1, lastName: 1, assignedCount: 1, assignedTypeStats: 1, resolvedCount: 1, resolvedTypeStats: 1, createdAt: 1, updatedAt: 1, _id: 0 } } ])
关键逻辑说明
$lookup:用来关联tickets集合,把用户的工单ID转换成实际的工单文档,方便后续统计类型。$setUnion:先提取所有不重复的工单类型,避免重复统计。$arrayToObject + $map:把类型数组转换成{"bug": 5, "feature": 3}这种直观的键值对格式,便于前端展示或后续处理。
如果需要优化性能,比如只统计特定类型的工单,可以在$lookup里加入pipeline参数提前过滤工单,减少数据传输量。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

