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

不执行$unwind时能否对前序$lookup返回结果执行二次关联查询

解决方案

完全可以不使用$unwind实现二次关联,通过嵌套$lookup的写法就能保留原有projects数组结构,实现你要的输出效果。

注意:该写法需要MongoDB 3.6及以上版本支持,这也是目前主流生产环境的最低版本要求

现有集合结构

// Client集合
Client {
  _id,
  companyId // 初始匹配字段
}

// Project集合
Project {
  _id,
  clientId, // 第一阶段lookup关联字段(对应client._id)
  createdBy
}

// User集合
User {
  _id, // 第二阶段lookup关联字段(对应project.createdBy)
  firstName,
  lastName
}

原有聚合代码

Client.aggregate([
  {
    $match: {
      companyId: mongoose.Types.ObjectId(req.user.companyId)
    }
  },
  {
    $lookup: {
      from: 'projects',
      localField: '_id',
      foreignField: 'clientId',
      as: 'projects'
    }
  },
])
.sort({ createdAt: -1 })

修改后的完整聚合代码

Client.aggregate([
  {
    $match: {
      companyId: mongoose.Types.ObjectId(req.user.companyId)
    }
  },
  {
    $lookup: {
      from: 'projects',
      localField: '_id',
      foreignField: 'clientId',
      as: 'projects',
      // 嵌套pipeline处理每个project的关联查询
      pipeline: [
        {
          $lookup: {
            from: 'users',
            localField: 'createdBy',
            foreignField: '_id',
            as: 'createdBy'
          }
        },
        // 把createdBy从单元素数组转成对象
        {
          $addFields: {
            createdBy: { $first: '$createdBy' }
          }
        },
        // 可选:只保留需要的字段,不需要可以删除该阶段
        {
          $project: {
            _id: 1,
            'createdBy.firstName': 1,
            'createdBy.lastName': 1
            // 其他需要保留的project字段自行补充
          }
        }
      ]
    }
  }
])
.sort({ createdAt: -1 })

实现逻辑说明

  • 外层$lookup保持原有逻辑关联projects集合,生成projects数组
  • 通过给$lookup加pipeline参数,在查询每个project的同时直接执行第二层$lookup,关联users集合获取对应用户信息
  • 用$addFields配合$first把createdBy从默认的数组格式转成对象,符合输出要求
  • 全程不需要对projects数组执行$unwind,输出结构和期望的完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 03:24:08