【问题标题】:Map Reduce Mongo DB: Sum of ODD and EVEN numbers with elementsMap Reduce Mongo DB:奇数和偶数与元素的总和
【发布时间】:2014-09-11 08:40:27
【问题描述】:

我正在尝试处理一个数字系列(集合),分别获取奇数/偶数的总和以及计算每个考虑的元素。

numberseries 文档结构如下:

{
_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 参数:

Values 是从地图发出的 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 不为零,并且因此可以正确添加数字。

  • 当我用 20 条记录、40 条记录、100 条记录测试这段代码时,它运行良好。
  • 当我用 20000 条记录测试这段代码时,总和值是正确的,但元素数组 每个不包含 10000 个元素(奇数和偶数在集合中平均分布)。

在以后的情况下,我收到以下消息:

查询未记录(太大)

【问题讨论】:

    标签: mongodb mapreduce mongodb-query aggregation-framework


    【解决方案1】:

    好的,有一个明确的原因,您似乎确实阅读了一些 the documentation 并至少应用了这条规则:

    • “返回对象的类型必须与map函数发出的值的类型相同……”

    这意味着 map 函数和 reduce 函数本质上具有相同的输出,就像您所做的那样:

    { sum : 0, elements :[] };
    

    但是有一段文档没看懂:

    • “MongoDB 可以为同一个键多次调用 reduce 函数。在这种情况下,该键的 reduce 函数的先前输出将成为该键的下一个 reduce 函数调用的输入值之一键。”

    所以整个事情出错的地方在于您假设由于您的“map”函数只发出一个元素,那么“elements”数组中将只有一个元素。仔细重读上面的内容,这不是真的。事实上,“reduce”的输出很可能会再次反馈到“reduce”函数中。这确实是 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" }
        }}
    

    真正将您收藏中的“奇数”或“偶数”数相加。

    【讨论】:

    • 感谢您的详细解答。我验证了我的代码。我发现它适用于小型数据集,但不适用于大型数据集。我添加了更多细节来澄清。
    • @LearnMore 再读一遍。这正是你被告知的。当使用相同的键抛出 30 个元素或周围的东西时,“减少”功能不会仅在一次通过中“减少”。也许您甚至应该尝试代码并意识到当它起作用时,关于原因的解释很清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    相关资源
    最近更新 更多