我已经将下面的sql语句翻译为map reduce:
select
p_brand, p_type, p_size,
count(ps_suppkey) as supplier_cnt
from
partsupp, part
where
p_partkey = ps_partkey
and p_brand <> 'Brand#45'
and p_type not like 'MEDIUM POLISHED %'
and p_size in (49, 14, 23, 45, 19, 3, 36, 9)
and ps_suppkey not in (
select
s_suppkey
from
supplier
where
s_comment like '%Customer%Complaints%'
)
group by
p_brand, p_type, p_size
order by
supplier_cnt desc, p_brand, p_type, p_size;
Map reduce函数:
db.runCommand({
mapreduce: "partsupp",
query: {
"ps_partkey.p_size": { $in: [49, 14, 23, 45, 19, 3, 36, 9] },
"ps_partkey.p_brand": { $ne: "Brand#45" }
},
map: function() {
var pattern1 = /^MEDIUM POLISHED .*/;
var pattern2 = /.*Customer.*Complaints.*/;
var suppkey = this.ps_suppkey.s_suppkey;
if( this.ps_suppkey.s_comment.match(pattern1) == null ){
if(this.ps_suppkey.s_comment.match(pattern2) != null){
emit({p_brand: this.ps_partkey.p_brand, p_type: this.ps_partkey.p_type, p_size: this.ps_partkey.p_size}, suppkey);
}
}
},
reduce: function(key, values) {
return values.length;
},
out: 'query016'
});
输出结果(在我看来)没有一个减少:
{
"result" : "query016",
"timeMillis" : 46862,
"counts" : {
"input" : 122272,
"emit" : 54,
"reduce" : 0,
"output" : 54
},
"ok" : 1
}
什么错了吗?
map函数输出键和值对。
reduce函数的目的是为同一个键组合多个值。这意味着如果某个键值只发出一次,那么它只有一个值,并且没有什么可减少的。
这就是为什么在emit语句中输出的值必须与reduce函数返回的格式完全相同的原因之一。
地图输出:
emit(key1, valueX);
emit(key1, valueY);
emit(key2, valueZ);
Reduce将valueX和valueY组合起来返回key1的新valueXY,最终结果将是:
key1, valueXY
key, valueZ
注意,在key2上从来没有调用过reduce。对于每个键值,Reduce函数可能被调用零,一次或多次,因此您必须小心构造map和Reduce函数以允许这种可能性。
你的map函数没有发出一个正确的值——你想计数,所以你必须输出一个计数。您的reduce函数必须循环遍历已经累积的计数,并将它们相加并返回组合计数。您可能想看看MongoDB文档中提供的一些示例。
你可以使用聚合框架来做这个更简单的事情——我不认为这里需要MapReduce,除非你期望输出大量的结果。
我怀疑你叫emit(value,key)
而不是emit(key,value)
。
如前所述,映射值和简化后的值必须具有相同的结构。如果你只是想做一个计数,映射一个值=1,在减少函数只是返回Array.sum(values)
。