MongoDB聚合Lookup填充嵌套数组added_by字段的问题
问题
需要将claim文档中actions数组里的added_by字段从ObjectId替换为对应的用户文档,同时保留原action的其他字段(比如message)。
示例结构
原claim文档结构:
{ _id: ObjectId('.....'), actions: [ { added_by: ObjectId('64d4f8a3fd3df..'), message: 'test' }, { added_by: ObjectId('64d4f8a3fd3....'), message: 'test2' }, // 其余记录 ] }
期望效果:
{ _id: ObjectId('.....'), actions: [ { added_by: { "email": "test@gmail.com", // 其余属性 }, message: 'test' }, { added_by: { "email": "testf@gmail.com", // 其余属性 }, message: 'test2' }, // 其余记录 ] }
尝试的代码及问题
尝试了以下聚合Lookup代码:
lookup.push({ $lookup: { from: 'users', // 关联的users集合 let: { actions: '$actions' }, // 定义匹配变量 as: 'actions', // 存储结果的字段 pipeline: [ { $match: { $expr: { $in: ['$_id', '$$actions.added_by'], // 匹配_id在added_by数组中的用户 }, }, } ], }, });
但遇到两个问题:
- 操作会覆盖整个
actions数组,导致原有的message等字段丢失; - 若多个action由同一用户创建,输出中仅显示一次该用户,而非对应每个action都显示。
完整示例文档
claim文档:
{ _id: ObjectId('64d79a9b5239b08bb7c00bb0'), claim_message: 'hola', files: [], added_by: ObjectId('64d4f8a3fd3dfa4fd2a37607'), company: ObjectId('64d4b30dfd3dfa4fd2a37283'), services: [ ObjectId('64d4b0f4fd3dfa4fd2a3720f') ], status: 'open', createdAt: ISODate('2023-08-12T14:43:39.343Z'), updatedAt: ISODate('2023-08-12T18:57:40.162Z'), actions: [ { files: null, added_by: ObjectId('64d4f8a3fd3dfa4fd2a37607'), message: 'testing' }, { files: null, added_by: ObjectId('64d4f8a3fd3dfa4fd2a37607'), message: 'new reply' }, { files: null, added_by: ObjectId('64d4f8a3fd3dfa4fd2a37879'), message: 'another reply' } ] }
user集合文档:
[ { _id: ObjectId('64d4f8a3fd3dfa4fd2a37607'), account_type: 'CLIENT', email: 'linked@gmail.com' }, { _id: ObjectId('64d4f8a3fd3dfa4fd2a37879'), account_type: 'CLIENT', email: 'visitor@gmail.com' } ]
解决方案
要解决这两个问题,需要先拆分actions数组,关联用户后再重组结构,同时保留所有原字段。具体聚合管道如下:
db.claims.aggregate([ // 1. 将actions数组拆分为独立文档,保留原claim的其他字段 { $unwind: "$actions" }, // 2. 关联users集合,匹配每个action的added_by对应的用户 { $lookup: { from: "users", localField: "actions.added_by", foreignField: "_id", as: "actions.added_by" } }, // 3. 将关联得到的用户数组转为单个对象(每个action仅对应一个用户) { $set: { "actions.added_by": { $first: "$actions.added_by" } } }, // 4. 按claim的_id重新分组,重组actions数组并保留其他字段 { $group: { _id: "$_id", claim_message: { $first: "$claim_message" }, files: { $first: "$files" }, added_by: { $first: "$added_by" }, company: { $first: "$company" }, services: { $first: "$services" }, status: { $first: "$status" }, createdAt: { $first: "$createdAt" }, updatedAt: { $first: "$updatedAt" }, actions: { $push: "$actions" } } } ])
代码说明
$unwind阶段:拆分actions数组为单个文档,确保每个action都能单独关联用户,避免同一用户只显示一次的问题。$lookup阶段:用每个action的added_by字段匹配users集合的_id,将匹配到的用户存入actions.added_by(此时为数组)。$set阶段:利用$first将用户数组转为单个对象,替换原有的ObjectId。$group阶段:按claim的_id重新分组,将拆分的action重新组合为数组,同时完整保留原claim的所有其他字段。
处理后,既能保留原action的所有字段(如message、files),又能让每个action正确关联到对应用户文档,同一用户创建的多个action也会一一对应。
内容的提问来源于stack exchange,提问作者Anouer Kassaa
相关产品推荐
相关产品推荐

