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

MongoDB双层嵌套数组字段的lookup联表查询实现方法

MongoDB嵌套数组关联Lookup实现方案

问题背景

现有两个集合:

  • department:存储院系、下属讲师及讲师授课分组ID信息,示例结构:
{
  "_id":99,
  "name":"Erick Kalewe",
  "faculty":"Zazio",
  "lecturers":[
    {
      "lecturerID":31,
      "name":"Granny Kinton",
      "email":"gkintonu@answers.com",
      "imparts":[
        {
          "groupID":70,
          "codCourse":99
        }
      ]
    },
    {
      "lecturerID":36,
      "name":"Michale Dahmel",
      "email":"mdahmelz@artisteer.com",
      "imparts":[
        {
          "groupID":100,
          "codCourse":60
        }
      ]
    }
  ]
}
  • group:存储授课分组、选课学生、上课安排信息,示例结构:
{
  "_id":100,
  "codCourse":11,
  "language":"Romanian",
  "max_students":196,
  "students":[
    {
      "studentID":1
    }
  ],
  "classes":[
    {
      "date":datetime.datetime(2022, 5, 10, 4, 24, 19),
      "cod_classroom":100
    }
  ]
}

需要将lecturers.imparts中的groupID与group集合的_id关联,替换为完整的分组文档,最终统计各院系下属教授授课的学生总数。


实现代码

由于关联字段位于两层嵌套数组中,无法直接使用基础$lookup完成嵌套替换,需要先打平数组、关联后再重组结构,完整聚合语句如下(兼容MongoDB 3.6+版本):

db.department.aggregate([
  // 打平第一层嵌套:把每个讲师从lecturers数组拆成单独文档
  { $unwind: "$lecturers" },
  // 打平第二层嵌套:把每个授课分组从讲师的imparts数组拆成单独条目
  { $unwind: "$lecturers.imparts" },
  // 关联group集合,匹配分组ID
  {
    $lookup: {
      from: "group",
      localField: "lecturers.imparts.groupID",
      foreignField: "_id",
      as: "matchedGroup"
    }
  },
  // 用匹配到的完整分组文档替换原有imparts条目,未匹配到则保留原内容
  {
    $set: {
      "lecturers.imparts": {
        $cond: [
          { $gt: [{ $size: "$matchedGroup" }, 0] },
          { $first: "$matchedGroup" },
          "$lecturers.imparts"
        ]
      }
    }
  },
  // 删除临时关联字段
  { $unset: "matchedGroup" },
  // 第一次重组:按院系+讲师维度归组,还原每个讲师的imparts数组
  {
    $group: {
      _id: {
        deptId: "$_id",
        lecturerId: "$lecturers.lecturerID"
      },
      deptName: { $first: "$name" },
      faculty: { $first: "$faculty" },
      lecturerName: { $first: "$lecturers.name" },
      lecturerEmail: { $first: "$lecturers.email" },
      imparts: { $push: "$lecturers.imparts" }
    }
  },
  // 第二次重组:按院系维度归组,还原完整的department结构
  {
    $group: {
      _id: "$_id.deptId",
      name: { $first: "$deptName" },
      faculty: { $first: "$faculty" },
      lecturers: {
        $push: {
          lecturerID: "$_id.lecturerId",
          name: "$lecturerName",
          email: "$lecturerEmail",
          imparts: "$imparts"
        }
      }
    }
  },
  // 新增统计字段:计算该院系所有讲师授课的学生总数
  {
    $set: {
      totalStudentCount: {
        $sum: {
          $map: {
            input: "$lecturers",
            as: "lecturer",
            in: {
              $sum: {
                $map: {
                  input: "$$lecturer.imparts",
                  as: "courseGroup",
                  in: { $size: { $ifNull: ["$$courseGroup.students", []] } }
                }
              }
            }
          }
        }
      }
    }
  }
])

性能优化建议

如果业务目标仅为生成统计报表,不需要还原完整的嵌套文档结构,可以跳过结构重组步骤,直接在关联后聚合计算,查询效率会高很多,简化版统计查询如下:

db.department.aggregate([
  { $unwind: "$lecturers" },
  { $unwind: "$lecturers.imparts" },
  {
    $lookup: {
      from: "group",
      localField: "lecturers.imparts.groupID",
      foreignField: "_id",
      as: "matchedGroup"
    }
  },
  // 拆分关联结果,未匹配到分组的条目保留为空,避免统计丢失
  { $unwind: { path: "$matchedGroup", preserveNullAndEmptyArrays: true } },
  // 按讲师维度统计授课学生数
  {
    $group: {
      _id: {
        faculty: "$faculty",
        deptId: "$_id",
        deptName: "$name",
        lecturerId: "$lecturers.lecturerID",
        lecturerName: "$lecturers.name"
      },
      lecturerStudentCount: { $sum: { $size: { $ifNull: ["$matchedGroup.students", []] } } }
    }
  },
  // 按院系维度归组,输出统计报表所需结构
  {
    $group: {
      _id: {
        faculty: "$_id.faculty",
        deptId: "$_id.deptId",
        deptName: "$_id.deptName"
      },
      deptTotalStudents: { $sum: "$lecturerStudentCount" },
      lecturerList: {
        $push: {
          lecturerID: "$_id.lecturerId",
          name: "$_id.lecturerName",
          teachStudentCount: "$lecturerStudentCount"
        }
      }
    }
  }
])

注意:group是MongoDB的保留关键字,生产环境建议将集合名修改为非关键字名称(比如course_group),避免后续查询出现语法兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:27:34