Mongodb 对文档进行分组并限制每个组

Mongodb group documents and limit each group

考虑这些文档:

{
    "Field1":"Test_1",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-15T14:17:49.336Z")
},
{
    "Field1":"Test_2",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-13T14:17:49.336Z")
},
{
    "Field1":"Test_3",
    "Speaker":1,
    "Listener":3,
    "ListenTime": ISODate("2016-10-10T14:10:49.336Z")
}

我要做的是在 MongoDB (3.2) 中仅使用单个查询提取那些按 Speaker 和 Listener 分组的文档,仅提取 ListenTime 最旧的文档。
因此,在这种情况下,结果将是:

{
    "Field1":"Test_1",
    "Speaker":1,
    "Listener":2,
    "ListenTime": ISODate("2016-10-15T14:17:49.336Z")
},
{
    "Field1":"Test_3",
    "Speaker":1,
    "Listener":3,
    "ListenTime": ISODate("2016-10-10T14:10:49.336Z")
}

是否可以通过单个查询做到这一点?

可以使用聚合框架来实现这一点。在第一阶段,使用 $group 将文档按 Speaker 和 Listener 分组。在此阶段,使用 $push 运算符将组中的所有文档添加到列表中,并使用 $max 运算符计算最近的 ListenTime。按照此,通过 $redact 阶段将具有最近 ListenTime 的文档保留在列表中。然后,使用 $unwind 阶段将列表扁平化为文档。然后,使用最后的 $project 阶段来获取所需的字段。

聚合查询如下所示。

db.sampleCollection.aggregate([
    {
        "$group":{
            "_id":{"Speaker":"$Speaker", "Listener":"$Listener"}, 
            ListenTime : {"$max":"$ListenTime"}, 
            "docs":{"$push":"$$ROOT"}
        }
    },
    {
        $redact:{
            $cond:[{$eq:["$ListenTime","$$ROOT.ListenTime"]},"$$DESCEND","$$PRUNE"]
        }
    },

    {
        "$project":{
            "ListenTime":1, 
            "Field1":"$docs.0.Field1", 
            "Speaker":"$docs.0.Speaker", 
            "Listener":"$docs.0.Listener"
        }
    }
])

示例输出:

{
        "ListenTime" : ISODate("2016-10-10T14:10:49.336Z"),
        "Field1" : "Test_3",
        "Speaker" : 1,
        "Listener" : 3
}
{
        "ListenTime" : ISODate("2016-10-15T14:17:49.336Z"),
        "Field1" : "Test_1",
        "Speaker" : 1,
        "Listener" : 2
}

运行 以下聚合管道以获得所需的结果:

db.collection.aggregate([
    { "$sort": { "ListenTime": -1 } },
    {
        "$group": {
            "_id": {
                "Speaker": "$Speaker",
                "Listener": "$Listener"
            },
            "Field1" : { "$first": "$Field1" },
            "ListenTime" : { "$first": "$ListenTime" }
        }
    },
    {
        "$project": {
            "Field1": 1,
            "Speaker": "$_id.Speaker",
            "Listener": "$_id.Listener",
            "ListenTime": 1,
            "_id": 0
        }
    }
])

示例输出

/* 1 */
{
    "Field1" : "Test_3",
    "ListenTime" : ISODate("2016-10-10T14:10:49.336Z"),
    "Speaker" : 1,
    "Listener" : 3
}

/* 2 */
{
    "Field1" : "Test_1",
    "ListenTime" : ISODate("2016-10-15T14:17:49.336Z"),
    "Speaker" : 1,
    "Listener" : 2
}