You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose查询含重复ID的数组时返回对应重复文档的方法

解决Mongoose按重复ID数组返回对应重复文档的问题

这个问题确实很常见——$in查询会自动去重返回唯一文档,但我们需要保留原数组的重复顺序和次数。这里有两种实用的解决方案,根据你的场景选就行:

方法一:后端映射(简单易实现,推荐中小数据量)

核心思路是先获取所有唯一的歌曲文档,再把它们转成一个以_id为键的映射对象,最后遍历原ID数组,逐个取出对应的歌曲文档。这样就能完美还原原数组的重复次数和顺序了。

代码示例:

// 假设用户的收听记录ID数组是 user.listenHistoryIds = ['a', 'a', 'a', 'b', 'c']
const uniqueSongIds = [...new Set(user.listenHistoryIds)];

// 先查询所有唯一的歌曲
const uniqueSongs = await Song.find({ _id: { $in: uniqueSongIds } }).exec();

// 将歌曲转成键值对映射,方便快速查找(注意把ObjectId转成字符串匹配)
const songMap = uniqueSongs.reduce((map, song) => {
  map[song._id.toString()] = song;
  return map;
}, {});

// 按照原ID数组的顺序和重复次数生成最终列表
const userPlayHistory = user.listenHistoryIds.map(id => songMap[id.toString()]);

额外优化:处理无效ID

如果原数组里有不存在的歌曲ID,上面的map会返回undefined,可以加个过滤步骤清理掉:

const userPlayHistory = user.listenHistoryIds
  .map(id => songMap[id.toString()])
  .filter(song => song); // 移除不存在的歌曲项

这种方法的优点是代码简洁易懂,性能也不错——因为只查一次数据库,剩下的都是内存操作,适合大多数用户收听记录的场景。

方法二:MongoDB聚合管道(数据库层处理,适合大数据量)

如果你的用户收听记录特别长,或者希望所有逻辑都在数据库层面完成,可以用MongoDB的聚合框架来实现。核心是通过$map和$arrayToObject来构建映射,再还原原数组的结构:

代码示例:

const userPlayHistory = await Song.aggregate([
  // 第一步:获取所有唯一的歌曲文档
  { $match: { _id: { $in: [...new Set(user.listenHistoryIds)] } } },
  // 第二步:将歌曲数组转成键值对映射(ObjectId转字符串作为键)
  { $group: {
    _id: null,
    songMap: { $push: { k: { $toString: "$_id" }, v: "$$ROOT" } }
  } },
  // 第三步:根据原ID数组,映射出包含重复项的歌曲列表
  { $project: {
    playHistory: {
      $map: {
        input: user.listenHistoryIds,
        as: "id",
        in: { $arrayElemAt: [
          "$songMap.v",
          { $indexOfArray: ["$songMap.k", "$$id"] }
        ] }
      }
    }
  } },
  // 第四步:展开数组并返回最终文档
  { $unwind: "$playHistory" },
  { $replaceRoot: { newRoot: "$playHistory" } }
]).exec();

这个方法把所有逻辑都交给数据库处理,后端只需要调用聚合查询即可,适合数据量较大、不想占用后端内存的场景,但代码相对复杂一些。

关键注意点

  • 一定要统一_id的类型:MongoDB的_id是ObjectId类型,而用户数组里的ID通常是字符串,所以必须转成字符串后再做映射匹配,否则会出现找不到的情况。
  • 两种方法都能保留原数组的顺序——因为map操作是严格按照原数组的顺序执行的。

内容的提问来源于stack exchange,提问作者Yooooomi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:53:59