MongoDB聚合查询实现课程与学生多对多关联数据拼接
优化MongoDB多集合关联查询:替换课程分组中的学生ID为详情
现有Courses和Students两个集合,Courses集合的studentGroups数组中,每个分组存储的是学生ID列表。当前通过循环查询的方式可以得到正确结果,但多次查询导致效率低下,需要用单条MongoDB聚合查询实现将学生ID替换为Students集合中的完整学生详情。
示例数据
Courses集合数据
[ { _id: 1, title: "Maths", studentGroups: [ { group: 'A', students: [1, 5] }, { group: 'B', students: [3] } ] }, { _id: 2, title: "Chemistry", studentGroups: [ { group: 'C', students: [2] }, { group: 'B', students: [4] } ] } ]
Students集合数据
[ { _id: 1, name: 'Henry', age: 15 }, { _id: 2, name: 'Kim', age: 20 }, { _id: 3, name: 'Michel', age: 14 }, { _id: 4, name: 'Max', age: 16 }, { _id: 5, name: 'Nathan', age: 19 } ]
期望结果
[ { _id: 1, title: "Maths", studentGroups: [ { group: 'A', students: [ { _id: 1, name: 'Henry', age: 15 }, { _id: 5, name: 'Nathan', age: 19 } ] }, { group: 'B', students: [ { _id: 3, name: 'Michel', age: 14 } ] } ] }, { _id: 2, title: "Chemistry", studentGroups: [ { group: 'C', students: [ { _id: 2, name: 'Kim', age: 20 } ] }, { group: 'B', students: [ { _id: 4, name: 'Max', age: 16 } ] } ] } ]
当前低效实现
var courses = Courses.find({}).fetch(); courses.forEach(c => { if (c.studentGroups && c.studentGroups.length) { c.studentGroups.forEach(s => { s.students = Students.find({_id: {$in: s.students}}).fetch() }) } })
聚合查询解决方案
使用MongoDB聚合管道,通过$lookup、$unwind、$map、$filter、$group等操作实现单条查询完成关联替换:
db.Courses.aggregate([ // 关联Students集合,一次性获取所有相关学生详情 { $lookup: { from: "Students", localField: "studentGroups.students", foreignField: "_id", as: "allStudents" } }, // 拆分studentGroups数组,单独处理每个分组 { $unwind: "$studentGroups" }, // 将分组内的学生ID替换为对应详情 { $addFields: { "studentGroups.students": { $map: { input: "$studentGroups.students", as: "studentId", in: { $first: { $filter: { input: "$allStudents", cond: {$eq: ["$$this._id", "$$studentId"]} } } } } } } }, // 按课程ID重新分组,合并studentGroups数组 { $group: { _id: "$_id", title: {$first: "$title"}, studentGroups: {$push: "$studentGroups"} } }, // 调整输出字段顺序,匹配原结构 { $project: { _id: 1, title: 1, studentGroups: 1 } } ])
各阶段说明
- $lookup:一次性关联
Students集合,把当前课程所有相关学生的详情存入allStudents数组,避免多次查询开销。 - $unwind:拆分
studentGroups数组为单个文档,方便逐个处理分组内的学生ID替换。 - $addFields + $map + $filter:遍历分组的学生ID数组,用
$filter从allStudents中匹配对应ID的学生详情,$first取唯一匹配结果(学生ID唯一),替换原ID值。 - $group:按课程
_id重新聚合,将拆分后的分组元素合并回数组,恢复原始文档结构。 - $project:调整输出字段顺序,确保和原数据结构一致。
该方案仅需一次聚合查询即可完成所有关联替换,相比循环查询效率大幅提升,数据量越大优势越明显。
内容的提问来源于stack exchange,提问作者StormTrooper
相关产品推荐
相关产品推荐

