如何用MongoDB聚合将用户programs模块与module_attempts关联?
解决MongoDB用户模块数据聚合关联问题
现有集合结构
// collection: users { "_id" : ObjectId("63ab0d0ed5e1b9566e0a2632"), "created_at" : ISODate("2022-12-27T20:49:42.350+05:30"), "programs" : [ { "program_id" : "5c1a01c6079288177e793c82", "modules" : [ { "module_id" : "5d5e9d990792882b250ecbf2", "module_instance_id" : "63ab0dedd5e1b9566e0a2638" }, { "module_id" : "5d5671e9079288617b1eaf72", "module_instance_id" : "63abf24b8c5554a06f0dcf52" } ] }, { "program_id" : "5c401f7307928872bf7ed303", "modules" : [ { "module_id" : "5d4bffd70792883e8b16b066", "module_instance_id" : "63ac4ccd9273aa285f011772" }, { "module_id" : "5d5684ff0792886b371d9dc2", "module_instance_id" : "63ad866b32b504b0340449d2" } ] } ], "module_attempts" : { "5d5e9d990792882b250ecbf2" : { //module_id "63ab0dedd5e1b9566e0a2638" : { //module_instance_id "start_date" : "2022-12-27 15:33:53", "module_instance_id" : "63ab0dedd5e1b9566e0a2638" } }, "5d5671e9079288617b1eaf72" : { "63abf24b8c5554a06f0dcf52" : { "start_date" : "2023-01-02 11:11:17", "module_instance_id" : "63abf24b8c5554a06f0dcf52" } }, "5d5684ff0792886b371d9dc2" : { "63ad866b32b504b0340449d2" : { "start_date" : "2023-01-02 13:57:27", "module_instance_id" : "63ad866b32b504b0340449d2" } } } }
需求说明
- 将
users.programs.modules层级的所有模块合并为一个平级数组 - 关联每个模块的
module_instance_id与文档内的module_attempts数据,输出包含以下字段的文档数组:_id(用户ID)module_instance_idmodule_idstart_date
错误查询问题分析
你之前用$lookup跨users集合查询是错误的——所有需要的module_attempts数据都在当前文档内,不需要跨集合关联,这也是查询返回空的核心原因。
正确聚合实现
db.users.aggregate([ // 过滤目标用户 { "$match": { "_id": ObjectId("63ab0d0ed5e1b9566e0a2632") }}, // 展开programs数组,得到单个program文档 { "$unwind": "$programs" }, // 展开每个program下的modules数组,得到单个module文档 { "$unwind": "$programs.modules" }, // 从module_attempts中匹配对应module_id和module_instance_id的start_date { "$addFields": { "start_date": { "$getField": { "field": "$programs.modules.module_instance_id", "input": { "$getField": { "field": "$programs.modules.module_id", "input": "$module_attempts" } } } } } }, // 映射输出需要的字段 { "$project": { "_id": 1, "module_instance_id": "$programs.modules.module_instance_id", "module_id": "$programs.modules.module_id", "start_date": "$start_date.start_date" } }, // 过滤掉没有匹配到尝试记录的模块(可选) { "$match": { "start_date": { "$exists": true } }} ])
步骤解释
- $match:精准定位目标用户,减少后续计算量
- 两次$unwind:逐层展开
programs和programs.modules数组,把嵌套的模块数据拆成单个文档 - $addFields:通过
$getField嵌套访问,从module_attempts中根据当前模块的module_id和module_instance_id取出对应的start_date数据 - $project:提取需要的字段,把嵌套的
start_date值拉平 - 可选$match:过滤掉没有匹配到尝试记录的模块(比如示例中
module_id:5d4bffd70792883e8b16b066的模块,因为module_attempts里没有对应数据)
执行后会输出符合需求的文档数组:
{ "_id" : ObjectId("63ab0d0ed5e1b9566e0a2632"), "module_instance_id" : "63ab0dedd5e1b9566e0a2638", "module_id" : "5d5e9d990792882b250ecbf2", "start_date" : "2022-12-27 15:33:53" }, { "_id" : ObjectId("63ab0d0ed5e1b9566e0a2632"), "module_instance_id" : "63abf24b8c5554a06f0dcf52", "module_id" : "5d5671e9079288617b1eaf72", "start_date" : "2023-01-02 11:11:17" }, { "_id" : ObjectId("63ab0d0ed5e1b9566e0a2632"), "module_instance_id" : "63ad866b32b504b0340449d2", "module_id" : "5d5684ff0792886b371d9dc2", "start_date" : "2023-01-02 13:57:27" }
内容的提问来源于stack exchange,提问作者Miuranga
相关产品推荐
相关产品推荐

