MongoDB嵌套聚合查询:补全用户曲目信息并拉取同分类关联曲目方案咨询
MongoDB 高性能查询实现方案
前置性能优化(必做)
首先建立以下索引保障查询效率:
- tracks集合:
_id默认索引已存在,若按创建时间排序需确保对createdAt(替换为你实际的时间字段名)建立索引 - users集合:
_id默认索引已存在 - categories集合:
_id默认索引已存在
实现逻辑说明
在原有聚合的基础上扩展两个嵌套lookup实现需求,全程走索引避免全表扫描:
- 原有关联creator的lookup内部新增嵌套lookup,将creator的tracks数组ID转成完整曲目对象,截取最近5条并剔除creator字段
- 新增一层关联categories的lookup,拿到分类下所有曲目ID后过滤当前查询曲目,再关联tracks集合取最新5条并剔除creator字段
完整聚合代码
const targetTrackId = ObjectId(trackId); // 提前转好ObjectId避免重复转换 const result = await tracks.aggregate([ // 第一步:匹配目标曲目 { $match: { _id: targetTrackId }, }, // 第二步:关联创作者,同时处理创作者的曲目列表 { $lookup: { let: { userId: { $toObjectId: "$creatorId" } }, from: "users", pipeline: [ { $match: { $expr: { $eq: ["$_id", "$$userId"] } } }, // 截取最新的5条曲目ID { $addFields: { tracks: { $slice: ["$tracks", -5] } } }, // 关联tracks集合拿到完整曲目信息 { $lookup: { let: { trackIds: { $map: { input: "$tracks", in: { $toObjectId: "$$this" } } } }, from: "tracks", pipeline: [ { $match: { $expr: { $in: ["$_id", "$$trackIds"] } } }, // 按创建时间倒序,确保是最新的5条 { $sort: { createdAt: -1 } }, // 剔除creator字段 { $unset: "creator" } ], as: "tracks" } } ], as: "creator", }, }, // 把creator从数组转成对象 { $unwind: "$creator" }, // 第三步:关联分类获取同分类相关曲目 { $lookup: { let: { categoryId: { $toObjectId: "$categoryId" }, currentTrackId: targetTrackId }, from: "categories", pipeline: [ { $match: { $expr: { $eq: ["$_id", "$$categoryId"] } } }, // 过滤掉当前查询的曲目ID { $addFields: { tracks: { $filter: { input: "$tracks", cond: { $ne: [ { $toObjectId: "$$this" }, "$$currentTrackId" ] } } } } }, // 截取最新的最多5条ID { $addFields: { tracks: { $slice: ["$tracks", -5] } } }, // 关联tracks拿完整信息 { $lookup: { let: { trackIds: { $map: { input: "$tracks", in: { $toObjectId: "$$this" } } } }, from: "tracks", pipeline: [ { $match: { $expr: { $in: ["$_id", "$$trackIds"] } } }, { $sort: { createdAt: -1 } }, { $unset: "creator" } ], as: "tracks" } }, // 只返回tracks字段 { $project: { tracks: 1, _id: 0 } } ], as: "relatedTracks" } }, // 把relatedTracks从嵌套结构提出来 { $unwind: "$relatedTracks" }, { $addFields: { relatedTracks: "$relatedTracks.tracks" } }, { $limit: 1 } ]).toArray();
注意事项
- 如果你存储的ID本身就是ObjectId类型,可以去掉所有
$toObjectId转换步骤,性能会更高 - 代码中的
createdAt替换成你实际存储曲目创建时间的字段名,如果你的users.tracks和categories.tracks数组本身就是按最新插入顺序排列的,也可以省略lookup内部的sort步骤,性能更优 - 所有匹配都走单文档命中+索引查询,无全表扫描,性能符合高并发场景要求
内容的提问来源于stack exchange,提问作者daniel93
相关产品推荐
相关产品推荐

