mongodb - 使用聚合框架对每个组进行分组并取前 K

标签 mongodb aggregation-framework

我有一个 MongoDB 集合,其中包含该格式的文档:

{ "_id":..., "Group": 1, "Value": 4 }
{ "_id":..., "Group": 2, "Value": 8 }
{ "_id":..., "Group": 1, "Value": 10 }

等等……

给定 X、Y 和 K,我想使用聚合框架来执行以下操作:

  • 选择所有 Group 属性在 X 和 Y 之间的文档
  • Group 属性分组
  • 在每组中:只选择具有最大Value属性的K个文档

知道怎么做吗?

最佳答案

前两步很简单:

X = 1; Y = 3
db.collection.aggregate( [
    { $match: { Group: { $gte: X, $lt: Y } } },
    { $group: { _id: '$Group' } }
] );

使用上述数据集,您可以:

{ "result" : [ { "_id" : 2 }, { "_id" : 1 } ], "ok" : 1 }

为了能够选择具有最大值的 K 个文档,您需要修改您的组以包括每个文档和该组的最大值,然后我们展开以便我们可以对组和值进行排序(描述):

db.collection.aggregate( [
    { $match: { Group: { $gte: X, $lt: Y } } },
    { $group: { 
        _id: '$Group', 
        docs: { $push: { _id: '$_id', Group: '$Group', Value: '$Value' } }
    } },
    { $unwind: '$docs' },
    { $sort: { 'docs.Group': 1, 'docs.Value': -1 } }
] );

从现在开始,我们感到震惊,因为与普通查询运算符 $push 不同,我们还不能在聚合框架中执行 $push + $slice。我们唯一能做的,是另一个组,这样您的应用程序就可以从每组中挑选出具有最高值的 K 个文档:

db.collection.aggregate( [
    { $match: { Group: { $gte: X, $lt: Y } } },
    { $group: { 
        _id: '$Group', 
        docs: { $push: { _id: '$_id', Group: '$Group', Value: '$Value' } }
    } },
    { $unwind: '$docs' },
    { $sort: { 'docs.Group': 1, 'docs.Value': -1 } }
    { $group: {
        _id: '$docs.Group', 
        docs: { $push: { 
            _id: '$docs._id', 
            Group: '$docs.Group', 
            Value: '$docs.Value' 
        } } 
    } }
] );

然后输出(在添加更多文档之后):

{
    "result" : [
        {
            "_id" : 2,
            "docs" : [
                {
                    "_id" : ObjectId("51e3a73dea832e98dd545f68"),
                    "Group" : 2,
                    "Value" : 22
                },
                {
                    "_id" : ObjectId("51e3a738ea832e98dd545f66"),
                    "Group" : 2,
                    "Value" : 17
                },
                {
                    "_id" : ObjectId("51e3a73aea832e98dd545f67"),
                    "Group" : 2,
                    "Value" : 13
                },
                {
                    "_id" : ObjectId("51e3a2aaea832e98dd545f64"),
                    "Group" : 2,
                    "Value" : 8
                },
                {
                    "_id" : ObjectId("51e3a736ea832e98dd545f65"),
                    "Group" : 2,
                    "Value" : 7
                }
            ]
        },
        {
            "_id" : 1,
            "docs" : [
                {
                    "_id" : ObjectId("51e3a740ea832e98dd545f69"),
                    "Group" : 1,
                    "Value" : 21
                },
                {
                    "_id" : ObjectId("51e3a2a5ea832e98dd545f63"),
                    "Group" : 1,
                    "Value" : 10
                },
                {
                    "_id" : ObjectId("51e3a742ea832e98dd545f6a"),
                    "Group" : 1,
                    "Value" : 5
                },
                {
                    "_id" : ObjectId("51e3a2a3ea832e98dd545f62"),
                    "Group" : 1,
                    "Value" : 4
                },
                {
                    "_id" : ObjectId("51e3a745ea832e98dd545f6b"),
                    "Group" : 1,
                    "Value" : 2
                }
            ]
        }
    ],
    "ok" : 1
}

MongoDB 更新 >= v3.2:

您现在可以将 $project 阶段添加到聚合管道的末尾,以限制每组的项目数:

$project: {
    _id: '$_id',
    docs: {
        $slice: [ 
            '$docs',
            3 // max number of elements returned from the start of the array
        ]
    } 
}

关于mongodb - 使用聚合框架对每个组进行分组并取前 K,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17642279/

相关文章:

javascript - $group 嵌套数组元素

javascript - *安全地将 HTML5 应用程序连接到服务器上的数据库

node.js - 使用 arrayFilters 更新嵌套数组

mongodb - 从 mongodb 导出 csv

java - 使用 spring 数据存储库和 mongodb 设置 spring 应用程序

javascript - 当前聚合的 MongoDB Node Driver Count

mongodb - $unwind 聚合框架中的对象

Node.Js MongoDB 在获取列表时格式化日期

MongoDB 2.6 聚合更新 $out 集合

javascript - 无法找到 MongoDB