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

MongoDB嵌套分组聚合:如何按年、月、otherObjectId聚合?

实现年→月→otherObjectId的嵌套分组聚合

当然可以实现你想要的嵌套分组结构!你只需要调整聚合的分组层级,把otherObjectId的统计数据嵌套到月份层级下面就行。我帮你修改并拆解完整的聚合语句:

完整聚合语句

db.Collection.aggregate([
  // 筛选状态为成功的记录(和你原来的逻辑一致)
  {$match: {status: 'succeeded'}},
  // 第一步分组:按年、月、otherObjectId的组合统计明细
  {
    $group: {
      _id: {
        year: {"$year": '$created'},
        month: {"$month": '$created'},
        otherObjectId: "$otherObjectId"
      },
      count: {$sum: 1}, // 统计该组合下的记录数
      totalAmount: {$sum: '$amount'} // 统计该组合下的总金额
    }
  },
  // 第二步分组:按年+月聚合,把同一月份的otherObjectId统计整合到一起
  {
    $group: {
      _id: {
        year: "$_id.year",
        month: "$_id.month"
      },
      otherObjectDetails: {
        $push: {
          otherObjectId: "$_id.otherObjectId",
          count: "$count",
          amount: "$totalAmount"
        }
      }
    }
  },
  // 第三步分组:按年聚合,把同一年的月份数据整合到一起
  {
    $group: {
      _id: "$_id.year",
      months: {
        $push: {
          month: "$_id.month",
          otherObjects: "$otherObjectDetails"
        }
      }
    }
  },
  // 可选:调整字段名,让结果结构更贴合你的期望
  {
    $project: {
      _id: 0, // 去掉默认的_id字段
      year: "$_id", // 把年份从_id移到year字段
      months: 1 // 保留月份数据
    }
  }
])

各阶段作用拆解

  • $match阶段:和你原来的逻辑一致,先筛选出状态为succeeded的记录,减少后续聚合的数据量。
  • 第一次$group:这是最细粒度的分组,以年、月、otherObjectId作为联合主键,计算每个组合下的记录数和总金额,确保每个otherObjectId在对应年月的统计数据准确。
  • 第二次$group:将分组粒度提升到年+月,用$push把同一个月内的所有otherObjectId统计数据整合到数组中,形成“月份→多个otherObjectId明细”的结构。
  • 第三次$group:最后按年分组,把同一年的所有月份数据整合到数组中,最终形成“年→多个月份→每个月份下的otherObjectId明细”的嵌套结构。
  • $project阶段:可选步骤,用来优化结果的字段名,让输出更符合你期望的格式(比如去掉默认的_id,把年份显式命名为year)。

示例返回结果

[
  {
    "year": 2020,
    "months": [
      {
        "month": 1,
        "otherObjects": [
          {
            "otherObjectId": ObjectId("60d21b4667d0d8992e610c85"),
            "count": 4,
            "amount": 200
          },
          {
            "otherObjectId": ObjectId("60d21b4667d0d8992e610c86"),
            "count": 1,
            "amount": 50
          }
        ]
      },
      {
        "month": 2,
        "otherObjects": [
          {
            "otherObjectId": ObjectId("60d21b4667d0d8992e610c85"),
            "count": 2,
            "amount": 150
          }
        ]
      }
    ]
  },
  {
    "year": 2021,
    "months": [
      // 2021年的月份数据...
    ]
  }
]

进阶:把otherObjectId作为对象键(可选)

如果你的期望是把otherObjectId作为对象的键(比如{ "60d21b46...": { amount: 200 } }),可以调整第二步分组后的逻辑,用$arrayToObject实现:

// 把原聚合中的第二步$group和后续阶段替换为:
{
  $group: {
    _id: {
      year: "$_id.year",
      month: "$_id.month"
    },
    otherObjectMapEntries: {
      $push: {
        k: {$toString: "$_id.otherObjectId"}, // ObjectId不能直接作为键,需转为字符串
        v: {amount: "$totalAmount", count: "$count"}
      }
    }
  }
},
{
  $addFields: {
    otherObjectMap: {$arrayToObject: "$otherObjectMapEntries"} // 把数组转为对象
  }
},
{
  $group: {
    _id: "$_id.year",
    months: {
      $push: {
        month: "$_id.month",
        otherObjectMap: "$otherObjectMap"
      }
    }
  }
},
{
  $project: {
    _id: 0,
    year: "$_id",
    months: 1
  }
}

这样返回的每个月份会是一个以otherObjectId字符串为键的对象:

{
  "year": 2020,
  "months": [
    {
      "month": 1,
      "otherObjectMap": {
        "60d21b4667d0d8992e610c85": { "amount": 200, "count": 4 },
        "60d21b4667d0d8992e610c86": { "amount": 50, "count": 1 }
      }
    }
  ]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:02:41