能否在不使用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" } } } } } } } } } ])
管道说明
第一个$project阶段
- 遍历外层
things数组,对每个元素:- 用
$size直接计算a_thing的总长度作为Grand_Total - 用
$reduce遍历a_thing数组,实现分组逻辑:- 初始值包含空的分类数组和总计数
- 用
$indexOfArray检查当前元素的cat是否已存在于分类数组中 - 若存在:找到对应分类,累加
sub_total并将当前thing添加到items数组 - 若不存在:新增一个分类项,
sub_total设为1,items包含当前thing
- 用
- 遍历外层
第二个$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
相关产品推荐
相关产品推荐

