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

MongoDB聚合查询如何结合$group与$project实现关联去重查询

问题根因
  • 原聚合语句存在基础语法错误:第二个$lookup阶段的localField字段值缺少闭合引号,且关联逻辑不符合原SQL要求:原SQL中service、property表均通过service_id = p.service_id关联,原写法错误使用producer_id关联property_id,关联结果本身就不对。
  • 字段丢失的核心原因是MongoDB聚合为管道执行模型,只有上一阶段输出的字段能流转到下一阶段。原写法$group阶段仅将service_id、property_id设为分组_id,producer表的name、service表的value、property表的property等字段均未被保留到分组输出,后续$project阶段自然无法读取到对应值。
  • 原写法执行逻辑存在性能问题:先对两个关联表做全量$lookup再$unwind会产生大量冗余笛卡尔积数据,后续全局分组的计算量会随数据量上涨指数级提升,把所有字段加入分组键确实会进一步降低性能,不是最优解。
优化实现方案

核心思路是将子表去重逻辑下推到$lookup子管道内执行,关联阶段就完成去重过滤,避免产生冗余数据,也不需要后续全局分组导致字段丢失,完全对齐原SQL的执行逻辑:

db.producer.aggregate([
  // 关联service集合,子管道内完成过滤+去重
  {
    $lookup: {
      from: "service",
      let: { pId: "$producer_id" },
      pipeline: [
        // 匹配service_id = producer.producer_id的记录
        { $match: { $expr: { $eq: ["$service_id", "$$pId"] } } },
        // 按service_id+value去重,对齐原SQL distinct逻辑
        { $group: {
          _id: { service_id: "$service_id", value: "$value" }
        }},
        // 把分组结果提升为普通文档结构
        { $replaceRoot: { newRoot: "$_id" } }
      ],
      as: "ps"
    }
  },
  // 关联property集合,子管道内完成过滤+去重
  {
    $lookup: {
      from: "property",
      let: { pId: "$producer_id" },
      pipeline: [
        // 匹配service_id = producer.producer_id的记录
        { $match: { $expr: { $eq: ["$service_id", "$$pId"] } } },
        // 按property_id+property去重
        { $group: {
          _id: { property_id: "$property_id", property: "$property" }
        }},
        { $replaceRoot: { newRoot: "$_id" } }
      ],
      as: "pp"
    }
  },
  // 依次展开两个关联数组,得到组合后的结果
  { $unwind: "$ps" },
  { $unwind: "$pp" },
  // 直接投影需要的字段,无需额外全局分组
  {
    $project: {
      producer_id: 1,
      name: 1,
      service_id: "$ps.service_id",
      value: "$ps.value",
      property_id: "$pp.property_id",
      property: "$pp.property",
      _id: 0
    }
  }
])
方案优势
  • 性能更高:去重逻辑在关联阶段就完成,不会产生多余的笛卡尔积数据,也不需要对全量关联结果做全局分组,数据量越大性能优势越明显。
  • 无字段丢失问题:所有需要的字段在管道流转过程中全程保留,不需要把所有字段加入分组键,$project阶段可以直接读取返回。
  • 逻辑对齐:执行逻辑和原SQL完全一致,先过滤子表、对子表去重,再和主表关联,返回结果和原SQL执行结果匹配。基于你给出的样例数据,执行后会返回如下结果:
[
  { producer_id: 1, name: 'test', service_id: 1, value: 12, property_id: 1, property: 12 },
  { producer_id: 1, name: 'test', service_id: 1, value: 12, property_id: 1, property: 56 },
  { producer_id: 1, name: 'test', service_id: 1, value: 13, property_id: 1, property: 12 },
  { producer_id: 1, name: 'test', service_id: 1, value: 13, property_id: 1, property: 56 },
  { producer_id: 2, name: 'test2', service_id: 2, value: 14, property_id: 2, property: 34 }
]

注:原SQL语句末尾缺少和property子查询的关联条件,上述实现是基于你给出的集合样例数据、字段对应关系补全关联逻辑后的版本,如果实际关联字段有调整,修改$lookup内的$match条件即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:28:04