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

能否在不使用Unwind的情况下对MongoDB嵌套数组分组统计?

无需$unwind实现MongoDB嵌套数组分组计数

完全可以不使用$unwind阶段,通过MongoDB的数组聚合操作符(比如$reduce、$map、$mergeObjects等)实现嵌套数组的分组和计数。针对你的需求,以下是具体的聚合管道方案:

完整聚合管道

db.collection.aggregate([
  {
    $project: {
      _id: 0,
      things: {
        $map: {
          input: "$things",
          as: "thing",
          in: {
            Grand_Total: { $size: "$$thing.a_thing" },
            categories: {
              $reduce: {
                input: "$$thing.a_thing",
                initialValue: { categories: [], grandTotal: 0 },
                in: {
                  grandTotal: { $add: ["$$value.grandTotal", 1] },
                  categories: {
                    $let: {
                      vars: {
                        catIndex: { $indexOfArray: ["$$value.categories.cat", "$$this.cat"] }
                      },
                      in: {
                        $cond: {
                          if: { $ne: ["$$catIndex", -1] },
                          then: {
                            $concatArrays: [
                              { $slice: ["$$value.categories", 0, "$$catIndex"] },
                              [
                                {
                                  $mergeObjects: [
                                    { $arrayElemAt: ["$$value.categories", "$$catIndex"] },
                                    {
                                      sub_total: { $add: [{ $arrayElemAt: ["$$value.categories.sub_total", "$$catIndex"] }, 1] },
                                      items: { $concatArrays: [{ $arrayElemAt: ["$$value.categories.items", "$$catIndex"] }, [{ thing: "$$this.thing" }]] }
                                    }
                                  ]
                                }
                              ],
                              { $slice: ["$$value.categories", { $add: ["$$catIndex", 1] }] }
                            ]
                          },
                          else: {
                            $concatArrays: [
                              "$$value.categories",
                              [
                                {
                                  cat: "$$this.cat",
                                  sub_total: 1,
                                  items: [{ thing: "$$this.thing" }]
                                }
                              ]
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  {
    $project: {
      things: {
        $map: {
          input: "$things",
          as: "t",
          in: {
            Grand_Total: "$$t.Grand_Total",
            $arrayToObject: {
              $map: {
                input: "$$t.categories.categories",
                as: "c",
                in: {
                  k: "$$c.cat",
                  v: {
                    sub_total: "$$c.sub_total",
                    items: "$$c.items"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
])

管道说明

  1. 第一个$project阶段

    • 遍历外层things数组,对每个元素:
      • 用$size直接计算a_thing的总长度作为Grand_Total
      • 用$reduce遍历a_thing数组,实现分组逻辑:
        • 初始值包含空的分类数组和总计数
        • 用$indexOfArray检查当前元素的cat是否已存在于分类数组中
        • 若存在:找到对应分类,累加sub_total并将当前thing添加到items数组
        • 若不存在:新增一个分类项,sub_total设为1,items包含当前thing
  2. 第二个$project阶段

    • 用$map和$arrayToObject将分类数组转换为键值对格式(对应你期望的type A、type B等作为键的结构),最终输出符合需求的嵌套结构。

输出结果

执行后会得到与你期望结构一致的结果:

{
  "things": [
    {
      "Grand_Total": 5,
      "type A": {
        "sub_total": 3,
        "items": [
          { "thing": "a record" },
          { "thing": "an other record" },
          { "thing": "more records" }
        ]
      },
      "type B": {
        "sub_total": 1,
        "items": [ { "thing": "yet other record" } ]
      },
      "type C": {
        "sub_total": 1,
        "items": [ { "thing": "last record" } ]
      }
    }
  ]
}

你之前尝试的$filter可以用来单独筛选某类数据,但结合$reduce就能实现完整的分组统计,全程不需要$unwind,避免了因展开大数组带来的性能损耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:01:37