MongoDB聚合:将一个$lookup结果合并到另一lookup的data数组子项
问题根源
原有聚合逻辑存在两处核心错误:
- 第二个
$lookup关联字段写死为不存在的items.activity_type_id,第一次关联activities集合后,查询结果实际存放在data数组中,无items字段 - 关联applications的时机错误:在
data还是数组结构时直接做顶层关联,查询到的activity_type自然会挂在文档顶层,无法对应到data内的单条activity记录
修正后的聚合代码
const results = await Constants.aggregate([ { $match: query, }, { $unwind: { path: '$value.categories', preserveNullAndEmptyArrays: true, }, }, { $lookup: { from: 'activity', localField: 'value.categories.id', foreignField: 'config.agenda_item_category_ids', as: 'data', }, }, // 拆分data数组,每条文档对应单个activity记录 { $unwind: { path: '$data', preserveNullAndEmptyArrays: true, } }, // 基于单条activity的activity_type_id关联applications,结果直接写入data子字段 { $lookup: { from: 'applications', localField: 'data.activity_type_id', foreignField: '_id', as: 'data.activity_type', }, }, { $addFields: { // lookup默认返回数组,1:1关联场景下直接取第一个元素转为对象结构 'data.activity_type': { $arrayElemAt: ['$data.activity_type', 0] } } }, // 按分类维度分组,把拆分后的activity重新组装为data数组 { $group: { _id: '$value.categories.id', title: { $first: '$value.categories.title' }, description: { $first: '$value.categories.description' }, icon_src: { $first: '$value.categories.icon_src' }, category_id: { $first: '$value.categories.id' }, data: { $push: '$data' } } }, { $project: { _id: 0, category_id: 1, title: 1, description: 1, icon_src: 1, data: 1 } } ]);
输出结构说明
修正后返回结果完全匹配预期结构,每个activity对应的activity_type会直接嵌入data数组的对应子项中:
[ { "category_id": "001", "title": "test 1", "data": [ { "propert1": "", "propert2": "", "config": { "agenda_item_category_ids": ["001", "002"] }, "activity_type_id": "123", "activity_type": { "_id": "123", "prop1": "", "prop2": "" } } ] }, { "category_id": "002", "title": "test 2", "data": [ { "propert1": "", "propert2": "", "config": { "agenda_item_category_ids": ["001", "002"] }, "activity_type_id": "123", "activity_type": { "_id": "123", "prop1": "", "prop2": "" } }, { "propert1": "", "propert2": "", "config": { "agenda_item_category_ids": ["002"] }, "activity_type_id": "456", "activity_type": { "_id": "456", "prop1": "", "prop2": "" } } ] } ]
关键改动点
- 关联applications集合前先通过
$unwind拆开data数组,保证单条文档只对应一个activity,避免关联结果错位 - 第二个
$lookup直接将关联结果写入data.activity_type路径,不需要额外做顶层字段的映射转换 - 关联完成后用
$arrayElemAt把lookup返回的数组转为单个对象,符合activity和application 1:1关联的业务逻辑 - 最后通过
$group把同分类下的activity重新聚合成data数组,结构和预期完全一致
内容的提问来源于stack exchange,提问作者Dharmik Soni
相关产品推荐
相关产品推荐

