如何让Mongoose<Model>.find返回含重复ID的对应数据?
解决方案:按含重复ID的数组顺序返回对应文档
你遇到的问题是MongoDB的$in查询会自动去重返回唯一文档,没法保留原数组里的重复项和顺序,以下两种方法可以解决:
方案1:JS端处理(简单高效,推荐)
先查询所有唯一ID对应的文档,再通过映射表按原数组顺序重构结果,自动保留重复项:
// 提取所有唯一的ObjectId,避免重复查询 const uniqueIds = [...new Set(ids.map(id => new mongoose.Types.ObjectId(id.id)))]; // 查询唯一ID对应的文档 const uniqueItems = await this.Products.find( { '_id': { $in: uniqueIds } }, { orders: 0 } ).exec(); // 把文档转成以_id字符串为键的映射表,方便快速查找 const itemMap = new Map(uniqueItems.map(item => [item._id.toString(), item])); // 遍历原ids数组,按顺序取出对应文档 const items = ids.map(id => { const objIdStr = new mongoose.Types.ObjectId(id.id).toString(); return itemMap.get(objIdStr); });
这种方法逻辑简单,性能开销小,适合大多数场景——先查唯一数据再映射,比多次重复查询效率高得多。
方案2:MongoDB聚合管道处理(数据库端完成)
如果希望完全在数据库层处理,可以用聚合管道实现,通过索引标记原顺序:
const items = await this.Products.aggregate([ // 构造带顺序索引的ID数组 { $addFields: { idList: ids.map((id, idx) => ({ _id: new mongoose.Types.ObjectId(id.id), sortIdx: idx })) } }, // 关联Products集合匹配文档 { $lookup: { from: 'products', // 替换为你的实际集合名(通常是模型名复数) localField: 'idList._id', foreignField: '_id', as: 'matchedItems' } }, // 展开匹配结果 { $unwind: '$matchedItems' }, // 给匹配到的文档加上原数组的顺序索引 { $addFields: { 'matchedItems.sortIdx': { $arrayElemAt: [ '$idList.sortIdx', { $indexOfArray: ['$idList._id', '$matchedItems._id'] } ] } } }, // 按原顺序排序 { $sort: { 'matchedItems.sortIdx': 1 } }, // 把匹配文档设为根文档 { $replaceRoot: { newRoot: '$matchedItems' } }, // 排除orders字段 { $project: { orders: 0 } } ]).exec();
注意:from字段要填你的Products集合在MongoDB里的实际名称,比如Mongoose模型是Product的话,集合名一般是products。
另外你提到用$or没用,是因为$or同样只会返回唯一的匹配文档,不会重复返回同一个ID的结果,所以解决不了重复项的问题。
内容的提问来源于stack exchange,提问作者user22152065
相关产品推荐
相关产品推荐

