在 mongodb 中将多个组合并为一个聚合

Combine multiple groups in an aggregation in mongodb

如果我有这样的collection:

{
    "store" : "XYZ",
    "total" : 100
},
{
    "store" : "XYZ",
    "total" : 200
},
{
    "store" : "ABC",
    "total" : 300
},
{
    "store" : "ABC",
    "total" : 400
}

我可以通过聚合获得 collection 中的 $sum 个订单:

db.invoices.aggregate([{$group: { _id: null, total: { $sum: "$total"}}}])

{
    "result": [{
            "_id": null,
            "total": 1000
        }
    ],
    "ok": 1
}

而且我可以获得 $sum 按商店分组的订单:

db.invoices.aggregate([{$group: { _id: "$store", total: { $sum: "$total"}}}])

{
    "result": [{
            "_id": "ABC",
            "total": 700
        }, {
            "_id": "XYZ",
            "total": 300
        }
    ],
    "ok": 1
}

但是我怎样才能在一个查询中做到这一点呢?

您可以汇总如下:

  • $groupstore字段,计算出subtotal.

  • $project 字段 doc 以保持 subtotal 组在接下来的 组.

  • $group乘以null,累计净额

代码:

db.invoices.aggregate([{
            $group: {
                "_id": "$store",
                "subtotal": {
                    $sum: "$total"
                }
            }
        }, {
            $project: {
                "doc": {
                    "_id": "$_id",
                    "total": "$subtotal"
                }
            }
        }, {
            $group: {
                "_id": null,
                "total": {
                    $sum: "$doc.total"
                },
                "result": {
                    $push: "$doc"
                }
            }
        }, {
            $project: {
                "result": 1,
                "_id": 0,
                "total": 1
            }
        }
    ])

输出:

{
    "total": 1000,
    "result": [{
            "_id": "ABC",
            "total": 700
        }, {
            "_id": "XYZ",
            "total": 300
        }
    ]
}

另一种方法是使用 $facet 聚合阶段。

  • $facet 允许你做多个嵌套 sub-aggregations 在您的主要聚合中。
  • 每个 sub-aggregation 都有自己的管道。
  • 对于 sub-aggregation 的每个结果,我们定义另一个字段。

像这样,例如:

db.invoices.aggregate([
    {
        $facet: {
            total: [
                {
                    $group: {
                        _id: null,
                        total: { $sum: "$total"}
                    }
                }
            ],
            store_totals: [
                {
                    $group: {
                        _id: "$store",
                        total: { $sum: "$total"}
                    }
                }
            ]
        }
    },{
        $unwind: "$total"
    },{
        $project: {
            _id: 0,
            total: "$total.total",
            store_totals: "$store_totals"
        }
    }
]

@BatScream wrote, that an $unwind stage might be costly. However we're unwinding an array of length 1 here. So I'm curious which approach is more efficient under which circumstances. If someone can compare those with console.time(), I'd be happy to include the results.


输出

Should be the same as in the accepted answer.