如何仅保留集合的已定义子集

时间:2011-10-07 14:40:02

标签: mongodb mapreduce

我想只保留一个集合的已定义子集。我没有找到任何有关它的相关信息。这很难解释,所以我举了一个例子:

假设我有这个系列:

db.mycollection.save({ "category" : 1201, "score" : 0.5});
db.mycollection.save({ "category" : 1201, "score" : 0.4});
db.mycollection.save({ "category" : 1201, "score" : 0.3});
db.mycollection.save({ "category" : 1201, "score" : 0.5});
db.mycollection.save({ "category" : 1201, "score" : 0.1});

db.mycollection.save({ "category" : 1202, "score" : 0.5});
db.mycollection.save({ "category" : 1202, "score" : 0.6});
db.mycollection.save({ "category" : 1202, "score" : 0.1});
db.mycollection.save({ "category" : 1202, "score" : 0.3});
db.mycollection.save({ "category" : 1202, "score" : 0.1});
db.mycollection.save({ "category" : 1202, "score" : 0.4});
db.mycollection.save({ "category" : 1202, "score" : 0.3});

db.mycollection.save({ "category" : 1203, "score" : 0.8});
db.mycollection.save({ "category" : 1203, "score" : 0.4});
db.mycollection.save({ "category" : 1203, "score" : 0.7});
db.mycollection.save({ "category" : 1203, "score" : 0.3});

db.mycollection.save({ "category" : 1204, "score" : 0.2});
db.mycollection.save({ "category" : 1204, "score" : 0.8});
db.mycollection.save({ "category" : 1204, "score" : 0.7});
db.mycollection.save({ "category" : 1204, "score" : 0.9});

我的目标是获得所有类别中最好的3行(关于分数)。 在这个例子中,我尝试得到这样的结果:

{ "category" : 1201, "score" : 0.5 }
{ "category" : 1201, "score" : 0.5 }
{ "category" : 1201, "score" : 0.4 }
{ "category" : 1202, "score" : 0.6 }
{ "category" : 1202, "score" : 0.5 }
{ "category" : 1202, "score" : 0.4 }
{ "category" : 1203, "score" : 0.8 }
{ "category" : 1203, "score" : 0.7 }
{ "category" : 1203, "score" : 0.4 }
{ "category" : 1204, "score" : 0.9 }
{ "category" : 1204, "score" : 0.8 }
{ "category" : 1204, "score" : 0.7 }

但我真的不知道该怎么做。 我找到了一个运行map reduce功能的解决方法,但它真的很慢。 这就是我所做的:

var map = function()
{
    emit(this.category, this.score);
}

var reduce = function(key, values)
{
    var total = [];
    values.forEach(function(value)
    {
        total.push(value);
    });
    total.sort();
    total.reverse();
    total = total.splice(0, 3);

    return {scores: total};
}

db.mycollection.mapReduce(map, reduce, { out : "myoutput" } );
db.myoutput.find();
db.myoutput.drop();

结果是:

{ "_id" : 1201, "value" : { "scores" : [ 0.5, 0.5, 0.4 ] } }
{ "_id" : 1202, "value" : { "scores" : [ 0.6, 0.5, 0.4 ] } }
{ "_id" : 1203, "value" : { "scores" : [ 0.8, 0.7, 0.4 ] } }
{ "_id" : 1204, "value" : { "scores" : [ 0.9, 0.8, 0.7 ] } }

这不是我想要的,但是它可以完成这项工作。

我的问题是:不使用map-reduce就可以做到这一点? (或者表现良好?)

PS:请原谅我糟糕的英语。我不会流利。


编辑:

我终于找到了这个解决方案:

var map = function()
{
   emit(this.category, this.score);
}

var reduce = function(key, values)
{
    var total = [];
    values.forEach(function(value)
    {
        if (value instanceof Array)
            total.concat(value);
        else if (value instanceof Object)
        {
             if (value.scores instanceof Array)
                total.concat(value.scores);
             else
                total.push(value.scores);
        }
        else
            total.push(value);
    });
    total.sort(function (a,b) { return b - a} );
    total = total.splice(0, 3);

    return {scores: total};
}

1 个答案:

答案 0 :(得分:0)

您可以很容易地获得给定catagery的结果

db.myCollection.find({category : 1204}).sort({score : -1}.limit(3)

这将给出给定类别的3个最佳分数

然后你可以做一个类别循环,但这需要很多请求(每个类别一个)。

地图缩减解决方案是实现此目的的唯一方法,您似乎有一个可行的解决方案。 如果你想提高你的表现,可以使用reduce功能,尤其是以下不太好的部分:

values.forEach(function(value)
{
    total.push(value);
});
total.sort();
total.reverse();
total = total.splice(0, 3);