在mongodb中,如何将多个分组合并到一个聚合中?

8
如果我有这样一个集合:
{
    "store" : "XYZ",
    "total" : 100
},
{
    "store" : "XYZ",
    "total" : 200
},
{
    "store" : "ABC",
    "total" : 300
},
{
    "store" : "ABC",
    "total" : 400
}

我可以通过聚合操作得到集合中订单总额$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
}

但是我该如何在一次查询中完成呢?

2个回答

21
你可以如下聚合:
  • store字段进行$group,计算subtotal

  • $project一个字段doc来保留subtotal组,在下一步中继续使用。

  • null进行$group并累加净总额。

代码:

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
        }
    ]
}

1
刚刚完成了!顺便问一下,对于这种聚合操作,分别调用两个不同的调用是否更有效率? - Matt Kim
2
@MattKim - 谢谢。虽然聚合解决方案涉及多个阶段,但它比进行两个数据库调用,然后在应用程序代码中进行一些后处理更有效。如果解决方案涉及一些阶段,例如 $unwind,那么情况就会有所不同,因为这是非常昂贵的。 - BatScream

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

  • $facet 允许您在主聚合中执行多个嵌套的子聚合。
  • 每个子聚合都有自己的管道。
  • 对于每个子聚合的结果,我们定义另一个字段。

例如:

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写道,$unwind阶段可能会很昂贵。然而,在这里我们只是展开了一个长度为1的数组。因此,我很好奇在哪种情况下哪种方法更有效。如果有人能用console.time()比较它们的结果,我将很高兴包含这些结果。


输出

应该与被接受的答案相同。


是否可以通过唯一字段将来自A面的管道的结果与来自B面的另一个文档合并为一个文档?就像在SQL中,您可以执行子查询“SELECT a,b,c(SELECT ... LIMIT 1)as otherColumn From T1”。 - Hector

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接