2014-11-04
最近,遇到这样一个需求,就是计算mongodb中一个集合里某个键的最大最小值,~~官方没有比较简洁的指令,只能想想其他办法。~~
MongoDB版本是2.4,在数据库s中test集合下,每个文档都是这样的形式(文档中省略了其他的键):
> db.test.findOne()
{
"_id" : ObjectId("5456d5b9cc7a670d39a9ecdb"),
"vid" : 6096,
}
>
我的目标是获取vid
的最大值和最小值。
第1种解决办法:使用MapReduce
我在 MapReduce in MongoDB 介绍过在MongoDB中如何使用mapreduce。官方的一篇文档Finding Max And Min Values for a given Key也介绍了如何在某种情况下如何使用mapreduce获取最大值/最小值。
对这次需求而言,需要使用下面的代码:
var map = function() {
id = this.vid;
emit("vid",{max:id, min:id})
}
var reduce = function(key, values) {
var min = values[0].min;
var max = values[0].max;
for ( var i=1; i<values.length; i++ ) {
if (min > values[i].min) {
min = values[i].min;
}
if (max < values[i].max) {
max = values[i].max;
}
}
return {"min":min, "max":max};
}
db.test.mapReduce(
map,
reduce,
{out:{inline:1}}
);
执行结果如下:
{
"results" : [
{
"_id" : "vid",
"value" : {
"min" : 1,
"max" : 6110
}
}
],
"timeMillis" : 1078,
"counts" : {
"input" : 6110,
"emit" : 6110,
"reduce" : 62,
"output" : 1
},
"ok" : 1,
}
对于小数据集,速度很快。然后我用了一个大数据集,一个有12000左右个文档的集合,文档数量不算多,但是每个文档很大,最终占用30GB左右的空间,在对vid
进行索引的情况下,在10分钟内没有输出结果,我就把它中断了。
第2种解决办法:使用一个文档来记录最大最小值
在小数据集的情况下,将得到的最小值,最大值放入一个文档里。然后,当插入新的数据时,判断是否更新最小值、最大值。
还有更多方法
2015-05-23补充。 感谢@lhrkkk的提醒,可见自己对mongodb了解的还不够。
假设现在集合link下有以下三个文档:
{ "_id" : NumberLong(22), "status" : 1, "topic_id" : NumberLong(34)}
{ "_id" : NumberLong(23), "status" : 1, "topic_id" : NumberLong(35)}
{ "_id" : NumberLong(24), "status" : 1, "topic_id" : NumberLong(35)}
使用sort
> db.link.find().sort({"topic_id":1}).limit(1) // topic_id最小的doc
{ "_id" : NumberLong(22), "status" : 1, "topic_id" : NumberLong(34)}
> db.link.find().sort({"topic_id":-1}).limit(1) // topic_id最大的doc
{ "_id" : NumberLong(23), "status" : 1, "topic_id" : NumberLong(35)}
> db.link.find().sort({"topic_id":-1}).limit(2) // topic_id最大两个的doc
{ "_id" : NumberLong(23), "status" : 1, "topic_id" : NumberLong(35)}
{ "_id" : NumberLong(24), "status" : 1, "topic_id" : NumberLong(35)}
使用aggregate
// 获取最小的_id
> db.link.aggregate(
[
{
$group:
{
_id: "min_id",
min_id: { $min: "$_id" }
}
}
]
) // Enter
{
"result" : [
{
"_id" : "min_id",
"min_id" : NumberLong(22)
}
],
"ok" : 1
}
// 按照topic_id分组,得到每组中的_id的最小值
> db.link.aggregate(
[
{
$group:
{
_id: "$topic_id",
min_id: { $min: "$_id" }
}
}
]
) // Enter
{
"result" : [
{
"_id" : NumberLong(35),
"min_id" : NumberLong(23)
},
{
"_id" : NumberLong(34),
"min_id" : NumberLong(22)
}
],
"ok" : 1
}