【问题标题】:How to calculate the average number of documents in each group in MongoDb?如何计算MongoDb中每组的平均文档数?
【发布时间】:2021-05-30 08:25:24
【问题描述】:

我有一个名为 UserRecords 的 MongoDb 集合。它存储我们用户的所有记录,每个用户可以有许多记录。

我正在尝试计算一些与每个用户的记录数相关的基本统计数据。

具体来说,我想要每个用户的记录数的平均值、中位数和众数。

到目前为止,我有一个查询,它按 User_Id (uid) 对所有 UserRecords 进行分组,并计算每个用户的 UserRecords 数量。

db.UserRecords.aggregate([
{$group: 
    {_id:{"uid":"$uid"},
    count:{$sum:1}}}
])

我的查询产生如下所示的结果:

{ 
    "_id" : {
        "uid" : UUID("f22880a8-94d2-4524-a974-a2e500e2c2a2")
    }, 
    "count" : 100
}
{ 
    "_id" : {
        "uid" : UUID("1b3a3b81-d107-4345-8df5-a5ef00e23598")
    }, 
    "count" : 200
}

我需要我的查询来计算所有“计数”值的平均值。例如,假设上述结果是仅产生的 2 组。我需要我的查询来执行 (100 + 200) / 2 = 150 并将 150 的值打印到控制台。

有谁知道我可以在查询中添加什么来完成此操作?

*编辑,我希望我的结果结构是:

{
    "mean": 1000,
    "median": 850
    "mode": 900
}

【问题讨论】:

  • 你应该能够链接另一个聚合管道阶段做一个组 + $avg。
  • 请提供预期的结果结构。
  • @turivishal 完成,谢谢
  • 这个平均值会去哪里?下面mode?
  • 看类似的question1, question2 进行中位数计算,我认为这种计算应该用客户端语言而不是mongodb查询。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

正如 cmets 中提到的,在 db 级别计算 medianmode 可能不是一个好习惯,但只是为了热情,试试这个。

顺便说一句,它在 uids 的累积 count 上计算中位数和众数。

db.UserRecords.aggregate([
    {
        $group: {
            _id: { "uid": "$uid" },
            count: { $sum: 1 }
        }
    },
    {
        $group: {
            _id: null,
            mean: { $avg: "$count" },
            numbers: { $push: "$count" }
        }
    },
    {
        $addFields: {
            median: {
                $function: {
                    body: function(numbers) {
                        if (numbers.length === 0) return 0;
                        numbers.sort(function(a, b) { return a - b; });
                        var half = Math.floor(numbers.length / 2);
                        if (numbers.length % 2) return numbers[half];
                        return (numbers[half - 1] + numbers[half]) / 2.0;
                    },
                    args: ["$numbers"],
                    lang: "js"
                }
            },
            mode: {
                $function: {
                    body: function(numbers) {
                        return Object.values(
                            numbers.reduce((count, e) => {
                                if (!(e in count)) {
                                    count[e] = [0, e];
                                }

                                count[e][0]++;
                                return count;
                            }, {})
                        ).reduce((numbers, v) => v[0] < numbers[0] ? numbers : v, [0, null])[1];
                    },
                    args: ["$numbers"],
                    lang: "js"
                }
            }
        }
    },
    {
        $project: {
            _id: 0,
            numbers: 0
        }
    }
]);

【讨论】:

  • 非常令人印象深刻,很好的答案!
  • 感谢您提出这个问题。我还学到了一些新东西!
  • 我认为另一种(可能更简单的)计算模式的方法是只进行 2 个小组赛阶段。第一阶段按 User_Id 对用户进行分组,然后第二阶段将按第一阶段的“计数”字段进行分组。然后,您可以按降序对结果进行排序,第一个文档将包含模式。
【解决方案2】:

您可以将groupnull 设为_id,在这种情况下,结果是单个 将您的累加器表达式应用于管道中的每个文档的文档

根据MongoDB docs

如果您指定 _id 值为 null 或任何其他常量值,则 $group 阶段将计算所有输入文档的累积值作为一个整体

db.UserRecords.aggregate([
    {
        $group: {
            _id: {"uid": "$uid"},
            count: {$sum: 1}
        },
    },
    {
        // will result in a single document which contains sumaries of
        // the previous groups data
        $group: {
            _id: null,

            recordsCount: {$sum: '$count'}, // the number of records in the collection
            usersCount: {$sum: 1}, // i.e "groups count"
        }
    },
    {
        $project: {
            mean: {$divide: ['$recordsCount', '$usersCount']}
            // ... you can add other measures here
        }
    }
])

【讨论】:

  • medianmode 将需要复杂的查询,因为需要排序。在第二个$group 阶段,这些度量需要一个包含排序“计数”的字段
猜你喜欢
  • 1970-01-01
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 2021-07-17
相关资源
最近更新 更多