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

如何在MongoDB聚合中使用$lookup关联数组类型外键字段?

基于MongoDB $lookup查询关联数组字段的解决方案

需求背景

现有两个集合:

  • issues集合:存储问题数据,关联项目ID和负责人用户ID
  • project集合:存储项目数据,包含成员数组,每个成员有用户ID和所属领域(discipline)

需要通过project集合中成员的discipline字段,筛选出issues集合中对应的数据,要求使用MongoDB的$lookup聚合操作。

集合结构

// issues-collection
[
  {
     project: ObjectId("67"), // 关联project集合的_id
     id: 101,
     assignedTo: ObjectId("1212") // 关联user集合的_id
  }
]

// project-collection
[
  {
     _id: ObjectId("67"),
     title: "Proj 1",
     members: [
       {
          _id: ObjectId("213213"),
          user: ObjectId("1212"), // 关联user集合的_id
          discipline: "Architect"
       }
     ]  
  }
]

解决方案

由于project集合的members是数组字段,直接使用$lookup的localField和foreignField只能匹配到包含目标用户的项目,但无法精准过滤指定discipline的成员。必须借助$lookup的pipeline参数,在子管道内完成数组展开、条件匹配的逻辑。

完整代码实现

const getIssuesByDiscipline = async (projectId, discipline: string) => {
  const res = await mongoose.model("issues").aggregate([
    {
      $lookup: {
        from: "project",
        let: {
          issueAssignedTo: "$assignedTo",
          targetProjectId: mongoose.Types.ObjectId(projectId),
          targetDiscipline: discipline
        },
        pipeline: [
          // 匹配指定项目
          { $match: { $expr: { $eq: ["$_id", "$$targetProjectId"] } } },
          // 展开members数组,便于逐个匹配
          { $unwind: "$members" },
          // 匹配成员的userID和指定discipline
          {
            $match: {
              $expr: {
                $and: [
                  { $eq: ["$members.user", "$$issueAssignedTo"] },
                  { $eq: ["$members.discipline", "$$targetDiscipline"] }
                ]
              }
            }
          },
          // 可选:只保留需要的项目字段,减少数据量
          { $project: { _id: 1, title: 1, matchedMember: "$members" } }
        ],
        as: "matchedProject"
      }
    },
    // 过滤出存在匹配项目的issues(即符合discipline条件的)
    { $match: { matchedProject: { $ne: [] } } },
    // 可选:调整输出结构,移除多余字段
    { $project: { _id: 1, id: 1, project: 1, assignedTo: 1, matchedProject: 1 } }
  ]);
  return res;
};

代码说明

  1. let参数:将issues文档的assignedTo、传入的projectId和discipline定义为子管道可访问的变量,注意projectId需要转为MongoDB的ObjectId类型。
  2. 子管道逻辑:
    • $match:先锁定指定项目,避免跨项目匹配。
    • $unwind:展开members数组,将数组元素转为独立文档,方便后续条件匹配。
    • $match:同时匹配成员的userID和discipline字段,确保只有符合条件的成员会被保留。
  3. 外层过滤:通过$match: { matchedProject: { $ne: [] } }筛选出真正符合discipline条件的issues文档。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:02:03