Mongodb Map / Reduce - Reduce不会被调用

时间:2011-04-10 12:06:21

标签: javascript mongodb map mapreduce reduce

我正在尝试在Mongo shell中执行简单的map reduce,但reduce函数永远不会被调用。这是我的代码:

db.sellers.mapReduce( 
    function(){ emit( this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

结果是

{
"results" : [
    {
        "_id" : ObjectId("4da0bdb56bd728c276911e1a"),
        "value" : "Map"
    },
    {
        "_id" : ObjectId("4da0df9a6bd728c276911e1b"),
        "value" : "Map"
    }
],
"timeMillis" : 0,
"counts" : {
    "input" : 2,
    "emit" : 2,
    "output" : 2
},
"ok" : 1,

}

怎么了?

我在Ubuntu 10.10上使用MongoDB 1.8.1 32位

4 个答案:

答案 0 :(得分:18)

reduce的目的是, ekhem ,将与给定键相关联的值集合减少为一个值(聚合结果)。如果您只为每个MapReduce键发出一个值,则不需要reduce,所有工作都已完成。但是如果你为给定的_id发出两对,则会调用reduce:

emit(this._id, 'Map1');
emit(this._id, 'Map2');

这将使用以下参数调用reduce:

reduce(_id, ['Map1', 'Map2'])

在过滤数据集时,您更有可能希望使用_id来表示MapReduce密钥:emit仅当给定记录满足某些条件时。但同样,在这种情况下不会调用reduce,这是预期的。

答案 1 :(得分:6)

嗯,如果只有一个值,MongoDB就不会调用Reduce函数。

在我看来,这很糟糕。应该留给我的reducer代码来决定是跳过一个奇异值还是对它进行一些操作。

现在,如果我必须对奇异值进行一些操作,我最终会编写finalize函数,并且在finalize中,我尝试区分哪个值已经通过reducer或哪个没有。

我非常确定,在Hadoop的情况下不会发生这种情况。

答案 2 :(得分:1)

地图缩减会将使用公共密钥的值收集到一个值中。

在这种情况下,没有任何事情要做,因为map发出的每个值都有不同的键。不需要减少。

db.sellers.mapReduce( 
    function(){ emit( this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

阅读文档时并不完全清楚。

如果你想调用reduce,你可以硬编码这样的ID:

db.sellers.mapReduce( 
    function(){ emit( 1, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

现在地图发出的所有值都将减少,直到只剩下一个。

答案 3 :(得分:1)

还应该提到的是,根据documentation,“MongoDB可以为同一个密钥多次调用reduce函数。在这种情况下,该密钥的reduce函数的先前输出将变为该键的下一个reduce函数调用的输入值之一。“。

此外,reduce应该是关联的,可交换的和幂等的:

reduce(key, [ C, reduce(key, [ A, B ]) ] ) == reduce( key, [ C, A, B ] )
reduce( key, [ reduce(key, valuesArray) ] ) == reduce( key, valuesArray )
reduce( key, [ A, B ] ) == reduce( key, [ B, A ] )

因此,这意味着reduce函数应该准备好接收对象,该对象是之前调用自身的结果。哪个(至少对我个人而言)意味着实现mapReduce的最佳方法是使map函数(如果可能)以与reduce函数相同的格式发出值回报。然后可以实现reduce函数以仅支持一种输入格式。并且,因此,即使map只发出一个对象(并且跳过了reduce的调用,结果),在mapReduce的最终结果中,从未调用reduce的键的值仍将与其余键的值格式相同。

例如,如果我们有以下文档结构:

{ 
    "foo": <some_string>,
    "status": ("foo"|"bar")
}

map函数可能如下:

function() {
    var value = {
       "num_total": 1,
       "num_foos": 0,
       "num_bars": 0
    };

    if (this.status == "foo") {
        value["num_foos"] += 1;
    }

    if (this.status == "bar") {
        value["num_bars"] += 1;
    }

    emit(this.foo, value);
}

并且reduce函数将是:

function(key, values) {
    var reduced = {
       "num_total": 0,
       "num_foos": 0,
       "num_bars": 0
    };

    values.forEach(function(val) {
        reduced["num_total"] += val["num_total"];
        reduced["num_foos"] += val["num_foos"];
        reduced["num_bars"] += val["num_bars"];
    });

    return reduced;
}