【问题标题】:MapReduce on MongoDB collection is turning up emptyMongoDB 集合上的 MapReduce 变为空
【发布时间】:2012-03-25 13:40:54
【问题描述】:

我一直在尝试将许多大型数据集归为一个集合,但我在编写 MapReduce 函数来实现这一点时遇到了问题。

这就是我的数据的样子(这里有 17 行,实际上我有 4+ 百万):

{"user": 1, "day": 1, "type": "a", "sum": 10}
{"user": 1, "day": 2, "type": "a", "sum": 32}
{"user": 1, "day": 1, "type": "b", "sum": 11}
{"user": 2, "day": 4, "type": "b", "sum": 2}
{"user": 1, "day": 2, "type": "b", "sum": 1}
{"user": 1, "day": 3, "type": "b", "sum": 9}
{"user": 1, "day": 4, "type": "b", "sum": 12}
{"user": 2, "day": 2, "type": "a", "sum": 3}
{"user": 3, "day": 2, "type": "b", "sum": 81}
{"user": 1, "day": 4, "type": "a", "sum": 22}
{"user": 1, "day": 5, "type": "a", "sum": 39}
{"user": 2, "day": 5, "type": "a", "sum": 8}
{"user": 2, "day": 3, "type": "b", "sum": 1}
{"user": 3, "day": 3, "type": "b", "sum": 99}
{"user": 2, "day": 3, "type": "a", "sum": 5}
{"user": 1, "day": 3, "type": "a", "sum": 41}
{"user": 3, "day": 4, "type": "b", "sum": 106}
...  

我试图让它最终看起来像这样(每种类型的数组,其中内容只是当天决定的适当索引中的总和,如果该类型不存在该天, 只是 0):

{"user": 1, "type_a_sums": [10, 32, 41, 22, 39], "type_b_sums": [11, 1, 9, 12, 0]}
{"user": 2, "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0]}
{"user": 3, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 81, 99, 106, 0]}
...

这是我一直在尝试的 MapReduce:

var mapsum = function(){
    var output = {user: this.user, type_a_sums: [0, 0, 0, 0, 0], type_b_sums: [0, 0, 0, 0, 0], tempType: this.type, tempSum: this.sum, tempDay: this.day}

    if(this.type == "a") {
        output.type_a_sums[this.day-1] = this.sum;
    }

    if(this.type == "b") {
        output.type_b_sums[this.day-1] = this.sum;
    }

    emit(this.user, output);
};

var r = function(key, values) {
    var outs = {user: 0, type_a_sums: [0, 0, 0, 0, 0], type_b_sums: [0, 0, 0, 0, 0], tempType: -1, tempSum: -1, tempDay: -1}

    values.forEach(function(v){

        outs.user = v.user;

        if(v.tempType == "a") {
            outs.type_a_sums[v.tempDay-1] = v.tempSum;
        }

        if(v.tempType == "b") {
            outs.type_b_sums[v.tempDay-1] = v.tempSum;
        }

    });

    return outs;
};


res = db.sums.mapReduce(mapsum, r, {out: 'joined_sums'})

这给了我在小样本上的输出,但是当我运行它超过 400 万个时,我得到了大量的输出,如下所示:

{"user": 1, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 0, 0, 0, 0]}
{"user": 2, "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0]}
{"user": 3, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 0, 0, 0, 0]}

users 的大部分应该在其数组中包含总和实际上只是填充了 reduce 函数 outs 对象中的虚拟数组中的 0,然后我用实际函数填充它.

真正奇怪的是,如果我在同一个集合上运行相同的确切函数,但只检查一个用户 res = db.sums.mapReduce(mapsum, r, {query: {user: 1}, out: 'joined_sums'}),我知道他们的数组中应该有总和但以前一直显示为全 0,我实际上会获得我想要的那个用户的输出。再次运行它超过 400 万,我到处都是 0。就好像它只是重写了它对虚拟填充数组所做的所有工作。

我的数据太多了吗?给定时间,它不应该能够通过它吗?还是我遇到了一些我不知道的障碍?

【问题讨论】:

  • 是否有可能是过时版本的 MongoDB 中的错误?
  • 我认为这与reduce() 每个键被多次调用有关。我正在尝试使用finalize,但我对它的工作原理感到非常困惑。

标签: javascript mongodb mapreduce


【解决方案1】:

感谢您提供大量详细信息。这里有几个问题。

让我们从头开始。

我试图让它最终看起来像这样

{"user": 2, "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0]}

它实际上看起来像这样:

{ _id: { "user": 2 }, value: { "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0] }

请注意,_id 有点像您的“分组依据”,value 有点像您的“总和”列。

所以问题 #1 是您发出 user 作为您的密钥,但它也是您价值的一部分。这不是必需的。 reduce 只会减少共享相同键的两个值,您也不需要此行:outs.user = v.user;

您还有问题2:您的reduce 不正确

我认为这与每个键多次调用 reduce() 有关。

reduce() 的目标是它会被多次调用。它应该跨服务器扩展。因此,一台服务器可以调用 reduce 几次,这些结果可以合并并发送给另一台服务器。

这是另一种看待它的方式。 Reduce 采用 value 对象数组并将它们缩减为单个 value 对象

这里有一些推论:

  • 如果我做reduce([a, b]),它应该和reduce([b, a])一样。
  • 如果我这样做 reduce([a, reduce([b,c])) 它应该与 reduce([reduce([a,b]), c]) 相同

所以无论我以什么顺序运行它们或值减少多少次都无关紧要,它始终是相同的输出。

如果您查看您的代码,这不是正在发生的事情。只需看看type_a_sums。如果我得到以下两个values 来减少会发生什么?

reduce([ [0,0,1,0,0], [0,2,0,0,0] ]) => ???

对我来说,这看起来应该是[0,2,1,0,0]。如果这是真的,那么您不需要所有这些 temp_X 字段。相反,您需要专注于 emiting 正确的数组,然后正确合并这些数组。

【讨论】:

  • 谢谢!我更改了代码 (pastie.org/private/dc9gizrsrzckzq6hvjkqq),但要到早上才能运行它。我删除了临时值,而是更改了reduce() 中的代码,以将每个emit() 数组的元素添加到reduce() 输出数组中的相应元素。这更有意义吗?我不明白为什么只使用数组会有所帮助。为什么不能从temp 变量中添加元素也能正常工作。这样,我只是使用数组的元素相加而不是int,但我觉得这是一回事。我需要finalize吗?
  • 看起来好多了。您不需要 temps 的原因是 emit 可能 的输出是最终结果。它可能不是,但它可能是。鉴于您最终真正想要的是数组,因此您希望数组在每个阶段都是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-28
  • 2012-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多