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
}
考虑这些文档:
{
"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
}