1

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

  • 姓名
  • 数量
  • 全部的

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

  • _ID
  • 创建
  • 项目

发票存在于一个专门的 Mongo 集合中,称为invoices

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

  • _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. 首先匹配具有给定名称的项目的所有发票。上有一个 Mongo 索引items.name,所以这$match是有效的。
  2. 发票是一个大对象,因此去掉所有字段,只留下以下结构:{_id: ?, created: ?, items: [{name: ?, qty: ?, total: ?}, ..., {name: ?, qty: ?, total: ?}]}
  3. 展开items数组,现在我们有了一个{_id: ?, created: ?, 'items.name': ?, 'items.qty': ?, 'items.total': ?}对象列表。
  4. 删除所有与给定名称不匹配的项目。
  5. 塑造最终发票投影。

然后通过以下代码输入发票预测的最终列表:

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 聚合管道中吗?

4

1 回答 1

2

您需要向管道添加一个 $group 步骤。

group 的 _id 将是您求和的值(在这种情况下是一个常数,因为您想要一个总计)。由于您想保留发票列表,您可以通过 $push 运算符将它们累积到一个数组字段中。总额和数量的总和将使用 $sum 处理。

{$group : { _id : 1,
            Total : { $sum : "$total" },
            ItemCount : { $sum : "$qty" },
            Invoices : { $push : { id : "$_id", created : "$created" }}
} }
于 2013-06-15T01:42:46.263 回答