使用Mongo聚合计算值的总和

时间:2013-06-14 10:15:27

标签: mongodb aggregation-framework

我有发票,每张发票都包含一个项目列表。每个项目(以及其他内容)包括以下字段:

  • 名称
  • 数量

每张发票都有(除其他外)字段:

  • _id
  • 创建

发票存放在专门的Mongo系列中,名为发票

我想获取包含指定商品的所有发票,每张发票需要返回以下信息:

  • _id
  • 创建
  • 数量(给定项目)
  • 总计(给定项目)

让我们调用元组< id,date,qty,total>作为发票预测。因此,结果应该是发票预测列表。

如果发票两次列出给定项目,则相应的发票将生成两个投影实例。如果发票根本没有列出给定项目,则结果中不会显示此发票。

无论如何,我正在使用以下Mongo聚合管道检索所需的投影:

  pipeline = [
    {$match: {'items.name': req.params.name}},
    {$project: {created: 1, 'items.name': 1, 'items.qty': 1, 'items.total': 1}},
    {$unwind: '$items'},
    {$match: {'items.name': req.params.name}},
    {$project: {created: 1, qty: '$items.qty', total: '$items.total'}}
  ],

管道的工作原理如下:

  1. 首先匹配具有给定名称的项目的所有发票。 items.name上有一个Mongo索引,因此$match效率很高。
  2. 发票是一个大型对象,因此请删除所有字段,只留下以下结构:{_id: ?, created: ?, items: [{name: ?, qty: ?, total: ?}, ..., {name: ?, qty: ?, total: ?}]}
  3. 展开items数组,现在我们有{_id: ?, created: ?, 'items.name': ?, 'items.qty': ?, 'items.total': ?}个对象的列表。
  4. 删除与给定名称不匹配的所有项目。
  5. 整理最终发票预测。
  6. 然后将最终的发票预测列表放入以下代码:

    function prepareResult(projections) {
      var res = projections.reduce(function (acc, item) {
        acc.itemCount += item.qty;
        acc.total += item.total;
        delete item.total;
        return acc;
      }, {itemCount: 0, total: 0});
      res.items = projections;
      return res;
    }
    

    它对所有发票预测中的qtytotal字段求和,并返回包含预测的新对象以及计算的总和。从每个发票投影中删除total字段,因为它只是所需的最终总和。

    我的问题 - 我可以将prepareResult函数的逻辑移到Mongo聚合管道中吗?

1 个答案:

答案 0 :(得分:1)

您需要在管道中添加$ group步骤。

组的_id将是你所总结的(在这种情况下是一个常数,因为你想要一个总数)。由于您要保留发票清单,可以通过$ push运算符将它们累积到数组字段中。总金额和数量的金额将以$ sum处理。

{$group : { _id : 1,
            Total : { $sum : "$total" },
            ItemCount : { $sum : "$qty" },
            Invoices : { $push : { id : "$_id", created : "$created" }}
} }