如何跨MongoDB集合查询,按用户+日期返回指定组织的指标文档
解决MongoDB跨集合聚合按「用户+日期」返回文档的问题
问题背景
我们需要跨MongoDB的users、daily_metrics、userspaces(记录用户所属组织)集合进行查询,获取指定组织下用户的指标文档并附带用户详情,要求按「用户+localDate」分别返回文档,而非将同一用户的所有指标合并为单文档。
当前问题
当前基于daily_metrics的聚合管道会将同一用户的所有指标聚合为单文档,仅保留首个localDate,不符合需求。不确定是否使用了正确的聚合阶段/累加器,或是否在错误阶段执行了$unwind操作。
现有聚合管道
[ { $lookup: { from: "userspaces", localField: "user", foreignField: "user", as: "userspaces", }, }, { $lookup: { from: "users", localField: "user", foreignField: "_id", as: "userdetails", }, }, { $unwind: "$userspaces", }, { $match: { createdAt: { $gte: ISODate("2023-03-09T18:30:00.000Z"), $lt: ISODate("2023-03-30T18:30:00.000Z"), }, "userspaces.organization": ObjectId( "63fdb56aac3a6db93dcbbc22" ), }, }, { $group: { _id: "$user", name: { $first: "$userdetails.name", }, dp: { $first: "$userdetails.profile_image", }, handle: { $first: "$userdetails.profile_id", }, date: { $first: "$localDate", }, steps: { $sum: "$metrics.steps", }, distance: { $sum: "$metrics.distance", }, calories: { $sum: "$metrics.calories", }, }, }, { $sort: { name: 1, }, }, ]
样本文档
daily_metrics
{ "_id": { "$oid": "640a7f519b12b8a960362e33" }, "user": { "$oid": "6408109bac3a6db93dcbe03d" }, "metrics": { "steps": 6911, "calories": 463.63, "distance": 3870.62 }, "localDate": "03-10-2023", "createdAt": { "$date": { "$numberLong": "1678409553242" } }, "updatedAt": { "$date": { "$numberLong": "1678512627838" } }, "__v": 0 }
userspaces
{ "_id": { "$oid": "620158cc4b23c40d3457c0c1" }, "organization": { "$oid": "6200f8aa4b23c40d3457c0a9" }, "user": { "$oid": "61f0fd1eb3dea2d99c6f9658" }, "createdAt": { "$date": { "$numberLong": "1644255436192" } }, "updatedAt": { "$date": { "$numberLong": "1644255436192" } }, "__v": 0 }
users
{ "_id": { "$oid": "61ee66c227b40d7b5fc912e4" }, "verified": true, "name": "Amanchira", "email": "amanchirasdf96@gmail.com", "profile_image": "https://d25ji.cloudfront.net/profileimages/asfsefkze.jpg", "createdAt": { "$date": { "$numberLong": "1643013826921" } }, "updatedAt": { "$date": { "$numberLong": "1659381101772" } }, "__v": 0, "profile_id": "amanchira1" }
预期输出
{ "_id": { "$oid": "640835b4ac3a6db93dcbe187" }, "name": [ "Adeanlika" ], "dp": [], "handle": [ "adeanlika" ], "date": "03-10-2023", "steps": 17635.25, "distance": 13819, "calories": 692.4 }
解决方案
问题核心在于$group阶段的_id只按user分组,导致同一用户的所有日期指标被合并。修改思路如下:
- 调整分组键:将
$group的_id改为包含user和localDate的复合键,确保每个「用户+日期」组合单独成文档。 - 优化
$unwind时机:对userdetails执行$unwind,避免返回数组格式的用户信息(最后通过$project转为预期的数组格式)。 - 修正字段提取:使用
$first获取用户详情(同一分组内用户信息一致)。
修改后的聚合管道:
[ // 关联用户所属组织 { $lookup: { from: "userspaces", localField: "user", foreignField: "user", as: "userspaces" } }, // 关联用户详情 { $lookup: { from: "users", localField: "user", foreignField: "_id", as: "userdetails" } }, // 展开组织关联结果,便于后续匹配 { $unwind: "$userspaces" }, // 展开用户详情,避免数组格式 { $unwind: "$userdetails" }, // 筛选指定组织和时间范围的记录 { $match: { createdAt: { $gte: ISODate("2023-03-09T18:30:00.000Z"), $lt: ISODate("2023-03-30T18:30:00.000Z") }, "userspaces.organization": ObjectId("63fdb56aac3a6db93dcbbc22") } }, // 按「用户+日期」分组,聚合当日指标 { $group: { _id: { user: "$user", date: "$localDate" }, name: { $first: "$userdetails.name" }, dp: { $first: "$userdetails.profile_image" }, handle: { $first: "$userdetails.profile_id" }, steps: { $sum: "$metrics.steps" }, distance: { $sum: "$metrics.distance" }, calories: { $sum: "$metrics.calories" } } }, // 重塑文档结构,匹配预期输出格式 { $project: { _id: "$_id.user", date: "$_id.date", name: ["$name"], dp: ["$dp"], handle: ["$handle"], steps: 1, distance: 1, calories: 1 } }, // 按用户名排序 { $sort: { name: 1 } } ]
关键修改说明
- 复合分组键:
_id: { user: "$user", date: "$localDate" }确保每个用户的每日指标单独成文档,不会被合并。 $unwind用户详情:将userdetails数组展开为单个对象,方便分组时提取字段,最后通过$project转为预期的数组格式。$project重塑结构:把日期从分组_id中提取到顶层字段,完全匹配预期输出的文档结构。
内容的提问来源于stack exchange,提问作者kaishuvam7
相关产品推荐
相关产品推荐

