我正在尝试在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位
答案 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;
}