我正在尝试处理一个数字系列(集合),分别得到奇数/偶数的总和以及计算每个数字的元素。
数字文档结构如下:
{
_id: <Autogenerated>,
number: <any number, it can repeat. Even if it repeats, it should be added each time. >
}
输出类似于下面的内容(不完全一般)
{
..
{
"odd":<result>, elements:{n1,n3,n5}
},
{
"even":<result>, elements:{n2,n4,n6}
}
..
}
地图功能:
mapf = function(){
var value = { sum : 0, elements :[] };
value.sum = this.number;
value.elements.push(this.number);
print(tojson(value));
if( this.number % 2 != 0 ){
emit( "odd", value );
}
if( this.number % 2 == 0 ){
emit( "even", value );
}
}
减少Values
参数:
值是从map:
发出的JSON数组 [{
"sum": 1,
"elements": [1]
}, {
"sum": 3,
"elements": [3]
} ... ]
减少功能:
reducef = function(key, values){
var result = { sum : 0 , elements:[] };
print("K " + key +"Values array " + tojson(values) );
for(var i = 0; i<values.length;i++ ){
v = values[i];
print("Key "+key+"V.JSON"+tojson(v)+" V.SUM -> "+v.sum);
result.sum += v.sum;
result.elements.push(v.elements[0]);
print(tojson(result));
}
return result;
}
我正确地得到了总和,但是元素数组没有正确填充。它只包含一些考虑用于计算的元素。
<小时/> 的更新
根据Neil给出的答案,我进一步验证了我的代码。我发现我的代码没有任何修改,适用于小型数据集,但不适用于大型数据集。
以下是我已经验证过的点,我发现我的代码是正确的。
print("K " + key +"Values array " + tojson(values) );
reduce函数的上一行导致打印出values
个对象。
[{
"sum": 1,
"elements": [1]
}, {
"sum": 3,
"elements": [3]
}, {
"sum": 5,
"elements": [5]
}, {
"sum": 7,
"elements": [7]
}, {
"sum": 9,
"elements": [9]
}, {
"sum": 11,
"elements": [11]
}, {
"sum": 13,
"elements": [13]
}, {
"sum": 15,
"elements": [15]
}, {
"sum": 17,
"elements": [17]
}, {
"sum": 19,
"elements": [19]
}]
因此,在最终结果result.elements.push(v.elements[0]);
中将元素推送到数组的行应该是正确的。
在map函数中,在发出之前,我正在修改value.sum
,如下所示
value.sum = this.number;
这样可以确保sum
不为零,并且由于此原因正确添加了数字。
在后面的情况下,我得到以下信息:
查询未录制(太大)
答案 0 :(得分:0)
好的,有一个明显的原因,你似乎已经阅读了一些the documentation并且至少应用了这条规则:
- “返回对象的类型必须与map函数发出的值的类型相同......”
这意味着map函数和reduce函数基本上具有相同的输出,你做了:
{ sum : 0, elements :[] };
但有一段文件尚未被理解:
- “MongoDB可以为同一个键多次调用reduce函数。在这种情况下,该键的reduce函数的前一个输出将成为下一个reduce函数调用的输入值之一键“。
所以整个事情出错的地方在于你假设因为你的“map”函数只发出一个元素,那么“elements”数组中只有一个元素。仔细阅读上述内容后说这不是真的。事实上,“减少”的输出很可能会再次反馈到“减少”功能。这确实是mapReduce如何处理“values”数组的大量值。
要解决此问题,请在“减少”功能中更改此项:
result.elements.push(v.elements[0]);
对此:
v.elements.forEach(function(element) {
result.elements.push(element);
}
以这种方式,当“reduce”函数返回已经总结了几个“元素”并将它们推送到列表的结果时,那个“输入”将被正确处理并与任何其他“值”合并“随之而来。
顺便说一句。我认为你在映射器中实际意味着这个:
var value = { sum : 1, elements :[] };
否则此处的代码只是汇总0
:
result.sum += v.sum;
所有这些都说下面的聚合框架语句做同样的事情,但在本机代码中实现更好更快:
db.collection.aggregate([
{ "$project": {
"type": { "$cond": [
{ "$eq": [ { "$mod": [ "$number", 2 ] }, 0 ] },
"even",
"odd"
]},
"number": 1
}},
{ "$group": {
"_id": "$type",
"sum": { "$sum": 1 },
"elements": { "$push": "$number" }
}}
])
并且还要注意,在这两种情况下,你并不是真正“总结元素”,而是“计算”它们。因此,如果您想要总和,则mapReduce部分变为:
//result.sum += v.sum;
v.elements.forEach(function(element) {
result.sum += element;
result.elements.push(element);
}
聚合部分变为:
{ "$group": {
"_id": "$type",
"sum": { "$sum": "$number" },
"elements": { "$push": "$number" }
}}
真正总结了您的收藏中的“奇数”或“偶数”数字。