【问题标题】:MongoDb - Aggregate: How to get Grand Totals with Individual, Ungrouped Records?MongoDb - 聚合:如何获得个人、未分组记录的总计?
【发布时间】:2018-01-07 17:24:57
【问题描述】:

假设我们有如下文件:

{ type: "hourly", amount: 100 },
{ type: "flat", amount: 350 },
{ type: "hourly", amount: 200 },
{ type: "payment", amount: 100 },
{ type: "payment", amount: 200 }

在管道中的某个地方,我想为每种“类型”累积总数。 连同所有个人记录,我想知道我们的总计如下:

hourly:  $300
flat:    $350
payment: $300

我查看了 $add$sum,它们似乎对于为给定记录添加字段而不是累积总计很有用。

到目前为止,我的聚合过滤了我想要的所有记录并且不使用分组。我不想对记录进行分组。我需要个人记录以及总计。

我相信 $push 命令可能是相关的,如果我必须 $group - 可能是 $push - 将个人记录放入一个数组,以将其与所有其余记录一起取出。在这里查看答案:Using Mongo aggregation to calculate sum of values

在我的情况下,我有一个汇总,没有收集任何总数,如下所示。对于每个返回的文档,都有一个“类型”字段(例如,“hourly”、“flat”、“payment”)。

myAggregate = [
    { $sort: { date: -1 } },
    {
        $match: query
    },
    {
        $lookup: {
            from: "contacts", // collection name in db
            localField: "billerId",
            foreignField: "_id",
            as: "biller"
        }
    },
    {
        $unwind: "$biller"
    },
    {
        $lookup: {
            from: "matters", // collection name in db
            localField: "matterId",
            foreignField: "_id",
            as: "matter"
        }
    },
    {
        $unwind: "$matter"
    },
    {
        $lookup: {
            from: "contacts", // collection name in db
            localField: "matter.clientId",
            foreignField: "_id",
            as: "client"
        }
    },
    {
        $unwind: "$client"
    },
];

“myAggregate”应该是什么样子才能累积总计并返回所有单独的(未分组的)记录?

事后思考:如果“手动”遍历记录以获得总计对我来说同样有效,我可以这样做。我假设 MongoDb 这样做必须更有效率,因为他们费心制作这些功能。

编辑 1:

以下是根据 Veeram 的建议附加“myAggregate”的方式:

// Do we need to get only records which match the matter's contactId?
if (query2 != "") {
    myAggregate.push( { $match: query2 } );
}

// Get grand totals.
myAggregate.push({ "$group": { "_id": "$transactionType",
                "documents": { "$push": "$$ROOT" },
                "grandtotal": {"$sum":"$amount"}
           }
});

【问题讨论】:

  • 您可以添加{"$group": { "_id": "$type", "documents": { "$push": "$$ROOT" }, "grandtotal":{$sum:"$amount"} } }作为最后阶段。更多info
  • @Veeram,看来现在是我学习如何正确构建表达式的时候了。我正在阅读Expressions 上的部分,但我不太了解它们是如何组合在一起的。事实上,我引用的关于表达式的部分根本不包含任何示例。您在上面使用了相当多的表达方式,我需要一个很好的参考来理解每个表达方式。例如,您没有在 $sum 周围加上引号。是否有关于构建此类表达式的良好参考?
  • 我只是想念他们。当您引用文档字段并使用嵌入的属性名称时,这很重要。始终使用引号。阅读 [$group](docs.mongodb.com/manual/reference/operator/aggregation/group) 并有一些具有不同累加器的示例。$$ROOT 是一个系统变量,它提供对当前文档的访问权限。
  • @Veeram。谢谢。我只是有机会尝试一下,看看会发生什么。我看到 1 个数组 [] 有 1 个元素。第一个元素的 id:“hourly”,documents:,grandtotal:1235。结果不包括 type=="flat" 或任何其他类型的元素。即使每种类型都有额外的元素,我的结果仍然会被分组。我希望以自然顺序或任何排序顺序获得所有结果,而无需进行分组。如果放弃分组并遍历所有元素以求和它们的数量对我来说同样有效,我可以这样做。请告诉我。
  • Np。不完全确定为什么只出现一种类型。我需要查看完整的聚合查询以及用于运行代码的数据。关于分组问题:所以 mongodb 将数据从一个阶段传递到另一个阶段。 $group 阶段就像 sql 组将要求您选择下一阶段所需的所有数据。如果您希望展平数据,以便每个小时文档包含相同的总计,您可以{$unwind:"$document"} 并且您可以在末尾添加$sort 阶段以向输出文档添加顺序。关于效率:您可以尝试两者并进行比较指标。

标签: mongodb


【解决方案1】:

在 Veeram 的帮助下,我还通过在他建议的 group 阶段之前添加一个 project 阶段来获得总数。我还将group 的_id 设置为null(根据Mongo documentation),以避免将我的所有文档分成组。

    $project: {
        _id: 1,
        firmId: 1,

        ... plus, a bunch more fields.

        // Project some fields to allow totaling by transactionType.
        hourlyAmount: { $cond: { if: { $eq: ["$transactionType", "hourly"] }, then: "$amount", else: 0 } },
        flatAmount: { $cond: { if: { $eq: ["$transactionType", "flat"] }, then: "$amount", else: 0 } },
        paymentAmount: { $cond: { if: { $eq: ["$transactionType", "payment"] }, then: "$amount", else: 0 } },
        expenseAmount: { $cond: { if: { $eq: ["$transactionType", "expense"] }, then: "$amount", else: 0 } },
        creditAmount: { $cond: { if: { $eq: ["$transactionType", "credit"] }, then: "$amount", else: 0 } },
        debitAmount: { $cond: { if: { $eq: ["$transactionType", "debit"] }, then: "$amount", else: 0 } }

    }

然后,根据 Veeram,我将其分组如下:

myAggregate.push({
    "$group": {
        "_id": null, //"$transactionType",
        "documents": { "$push": "$$ROOT" },
        "hourlyTotal": {"$sum": "$hourlyAmount"},
        "flatTotal" :  {"$sum": "$flatAmount"},
        "paymentTotal" :  {"$sum": "$paymentAmount"},
        "expenseTotal" :  {"$sum": "$expenseAmount"},
        "creditTotal" :  {"$sum": "$creditAmount"},
        "debitTotal" :  {"$sum": "$debitAmount"},
    }
});

它运行良好,并且所有内容都作为具有单个元素的数组返回。注意“documents”是数组元素的一个属性,所以它们可以被<arrayElements>[0].documents 引用。我所有的个人总数都在那里(例如,arrayElements>[0].hourlyTotal)。

【讨论】:

    猜你喜欢
    • 2013-12-19
    • 2020-03-05
    • 2020-10-13
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多