我在mongodb中有一组文档,每个文档都有一个“组”字段,该字段指的是拥有该文档的组。文件如下:
{
group: <objectID>
name: <string>
contents: <string>
date: <Date>
}
我想构建一个查询,它返回每个组的最新N个文档。例如,假设有5个组,每个组有20个文档。我想编写一个查询,它将返回每个组的前3个,这将返回15个文档,每组3个。每组获得3分,即使另一组有最近的第4组。
在SQL世界中,我相信这种类型的查询是通过“分区依据”和计数器来完成的。在mongodb中是否有这样的事情,没有为N组做N + 1个单独的查询?
答案 0 :(得分:5)
您无法使用聚合框架执行此操作 - 您可以获得每个组的$ max或top日期值,但聚合框架还没有办法累积前N个加上没有办法将整个文档推入结果集(仅限单个字段)。
所以你必须回到MapReduce上。这是可行的,但我确定有很多变种(都需要以某种方式根据特定属性对对象数组进行排序,我从the answers in this question之一借用了我的解决方案。
Map函数 - 将组名称作为键输出,将文档的其余部分作为值输出 - 但它将其作为包含数组的文档输出,因为我们将尝试累积每组的结果数组:
map = function () {
emit(this.name, {a:[this]});
}
reduce函数会将属于同一组的所有文档累积到一个数组中(通过concat)。请注意,如果优化reduce以通过检查日期仅保留前五个数组元素,那么您将不需要finalize函数,并且在运行mapreduce期间将使用更少的内存(它也会更快)。
reduce = function (key, values) {
result={a:[]};
values.forEach( function(v) {
result.a = v.a.concat(result.a);
} );
return result;
}
由于我保留了每个键的所有值,因此我需要一个finalize函数来为每个键仅提取最新的五个元素。
final = function (key, value) {
Array.prototype.sortByProp = function(p){
return this.sort(function(a,b){
return (a[p] < b[p]) ? 1 : (a[p] > b[p]) ? -1 : 0;
});
}
value.a.sortByProp('date');
return value.a.slice(0,5);
}
使用类似于您提供的模板文档,通过调用mapReduce命令运行此文件:
> db.top5.mapReduce(map, reduce, {finalize:final, out:{inline:1}})
{
"results" : [
{
"_id" : "group1",
"value" : [
{
"_id" : ObjectId("516f011fbfd3e39f184cfe13"),
"name" : "group1",
"date" : ISODate("2013-04-17T20:07:59.498Z"),
"contents" : 0.23778377776034176
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe0e"),
"name" : "group1",
"date" : ISODate("2013-04-17T20:07:59.467Z"),
"contents" : 0.4434165076818317
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe09"),
"name" : "group1",
"date" : ISODate("2013-04-17T20:07:59.436Z"),
"contents" : 0.5935856597498059
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe04"),
"name" : "group1",
"date" : ISODate("2013-04-17T20:07:59.405Z"),
"contents" : 0.3912118375301361
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfdff"),
"name" : "group1",
"date" : ISODate("2013-04-17T20:07:59.372Z"),
"contents" : 0.221651989268139
}
]
},
{
"_id" : "group2",
"value" : [
{
"_id" : ObjectId("516f011fbfd3e39f184cfe14"),
"name" : "group2",
"date" : ISODate("2013-04-17T20:07:59.504Z"),
"contents" : 0.019611883210018277
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe0f"),
"name" : "group2",
"date" : ISODate("2013-04-17T20:07:59.473Z"),
"contents" : 0.5670706110540777
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe0a"),
"name" : "group2",
"date" : ISODate("2013-04-17T20:07:59.442Z"),
"contents" : 0.893193120136857
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe05"),
"name" : "group2",
"date" : ISODate("2013-04-17T20:07:59.411Z"),
"contents" : 0.9496864483226091
},
{
"_id" : ObjectId("516f011fbfd3e39f184cfe00"),
"name" : "group2",
"date" : ISODate("2013-04-17T20:07:59.378Z"),
"contents" : 0.013748752186074853
}
]
},
{
"_id" : "group3",
...
}
]
}
],
"timeMillis" : 15,
"counts" : {
"input" : 80,
"emit" : 80,
"reduce" : 5,
"output" : 5
},
"ok" : 1,
}
每个结果的_id作为组名称和值,作为该组名称集合中最近五个文档的数组。
答案 1 :(得分:-1)
你需要聚合框架$ group stage在$ limit阶段管道... 你还希望以某种方式对记录进行排序,否则限制将具有未定义的行为,返回的文档将是伪随机的(mongo内部使用的顺序)
这样的事情: db.collection.aggregate([{$组:...},{$排序:...},{$限制:...}])