如何基于字段值在MongoDB中关联不同集合执行Lookup查询
动态关联不同集合填充数组元素数据
问题场景
现有MongoDB文档结构如下:
{ name: 'my list', items: [ { type: 'book', id: 5364 }, { type: 'car', id: 354 }, { type: 'laptop', id: 228 } ] }
对应的Mongoose Schema定义:
{ name: String, items: { type: Array, default: [] } }
需要根据items数组中每个元素的type字段,从对应的集合(如books、cars、laptops)中获取对应id的文档,最终输出结构如下:
{ name: 'my list', items: [ { type: 'book', id: 5364, data: [{...}] }, { type: 'car', id: 354, data: [{...}] }, { type: 'laptop', id: 228, data: [{...}] } ] }
解决方案
方法一:聚合管道实现(高性能推荐)
利用MongoDB聚合管道的$map、$switch和$lookup组合,实现动态关联不同集合:
const result = await YourListModel.aggregate([ // 可选:匹配目标文档,根据业务需求添加过滤条件 { $match: { name: 'my list' } }, // 遍历items数组,根据type关联对应集合 { $addFields: { items: { $map: { input: '$items', as: 'item', in: { type: '$$item.type', id: '$$item.id', data: { $switch: { branches: [ { case: { $eq: ['$$item.type', 'book'] }, then: { $lookup: { from: 'books', localField: '$$item.id', foreignField: '_id', as: 'bookData' } } }, { case: { $eq: ['$$item.type', 'car'] }, then: { $lookup: { from: 'cars', localField: '$$item.id', foreignField: '_id', as: 'carData' } } }, { case: { $eq: ['$$item.type', 'laptop'] }, then: { $lookup: { from: 'laptops', localField: '$$item.id', foreignField: '_id', as: 'laptopData' } } } ], default: [] } } } } } } }, // 整理data字段,提取对应集合的查询结果 { $addFields: { items: { $map: { input: '$items', as: 'item', in: { type: '$$item.type', id: '$$item.id', data: { $arrayElemAt: [ { $switch: { branches: [ { case: { $eq: ['$$item.type', 'book'] }, then: '$$item.data.bookData' }, { case: { $eq: ['$$item.type', 'car'] }, then: '$$item.data.carData' }, { case: { $eq: ['$$item.type', 'laptop'] }, then: '$$item.data.laptopData' } ], default: [] } }, 0 // 若查询单条文档,取出数组第一个元素;多条则直接保留数组 ] } } } } } } ]);
方法二:手动遍历查询(逻辑直观)
先获取主文档,再遍历items数组逐个查询对应集合数据:
const listDoc = await YourListModel.findOne({ name: 'my list' }); if (!listDoc) return; // 并行查询所有关联数据 listDoc.items = await Promise.all( listDoc.items.map(async (item) => { let data = []; // 根据type匹配对应模型查询 switch (item.type) { case 'book': data = await BookModel.find({ _id: item.id }); break; case 'car': data = await CarModel.find({ _id: item.id }); break; case 'laptop': data = await LaptopModel.find({ _id: item.id }); break; default: data = []; } return { ...item, data }; }) ); // 此时listDoc即为期望结构 console.log(listDoc);
注意事项
- 确保
item.id与对应集合_id的类型一致(如均为ObjectId或数字),类型不匹配时需转换(例如用mongoose.Types.ObjectId(item.id))。 - 聚合管道方法适合大数据量场景,性能更优;手动查询逻辑简单,适合小数据量或快速开发场景。
内容的提问来源于stack exchange,提问作者Soroush Bgm
相关产品推荐
相关产品推荐

