【问题标题】:Peculiar Map/Reduce result from CouchDB来自 CouchDB 的特殊 Map/Reduce 结果
【发布时间】:2011-09-22 01:20:57
【问题描述】:

我使用 CouchDB 已经有一段时间了,没有任何问题。直到现在。我最近在我的 map/reduce 结果中看到了一些我忽略的东西!

这是在对“avgs”变量执行sum 之前。我基本上是在尝试找到与特定键有关的所有值的平均值。没有什么花哨。结果符合预期。

注意时间戳 1308474660000(表中的第 4 行)的结果:


现在我 sum "avgs" 数组。现在这里有一些关于结果的特殊之处。时间戳为 1308474660000 的键的总和是 null!!为什么 CouchDB 为一个简单的sum 吐出nulls?我尝试了自定义添加功能,但问题相同。

有人可以向我解释为什么我的 map/reduce 结果存在这个问题吗?

CouchDB 版本:1.0.1


更新:

在执行 rereduce 后,我得到一个 reduce 溢出错误!

Error: reduce_overflow_error

Reduce output must shrink more rapidly: Current output: '["001,1,1,1,1,1,11,1,1,1,1,1,1,11,1,1,1,1,1,1,11,1,1,1,1,1,1,11,1,1,1,1,1,101,1,1,1,1,1,1,11,1,1,1,1'... (first 100 of 396 bytes)

这是我修改后的reduce函数:

function (key, values, rereduce) {
  if(!rereduce) {
    var avgs = [];
    for(var i=values.length-1; i>=0 ; i--) {
      avgs.push(Number(values[i][0])/Number(values[i][1]));
    }
    return avgs;
  } else {
    return sum(values);
  };
}

更新 2:

现在情况变得更糟了。其选择性地减少。此外,它减少的那些显示错误的结果。时间戳 (1308474660000) 的第 4 行值的长度应为 2 而不是 3。

更新 3:

我终于让它工作了。我没有正确理解 rereduce 的细节。 AFAIK,Couchdb 本身决定如何/何时重新减少。在这个例子中,只要数组足够长可以处理,Couchdb 就会将它发送到 rereduce。所以我基本上不得不sum 两次。一次在reduce中,再次在rereduce中。

function (key, values, rereduce) {
  if(!rereduce) {
    var avgs = [];
    for(var i=values.length-1; i>=0 ; i--) {
      avgs.push(Number(values[i][0])/Number(values[i][1]));
    }
    return sum(avgs);
  } else {
    return sum(values); //If my understanding of rereduce is correct, it only receives only the avgs that are large enough to not be processed by reduce.
  }
}

【问题讨论】:

  • 跟进:我对 CouchBase/Bigcouch 进​​行了同样的尝试,它正确计算了时间戳的 sum,但其他键显示 nulls。奇怪!

标签: nosql couchdb mapreduce


【解决方案1】:

reduce 函数中的for 循环可能没有按照您的想法进行。例如,它可能会抛出一个你没有预料到的异常。

您期望一个 2 元组数组:

// Expectation
values = [ [value1, total1]
         , [value2, total2]
         , [value3, total3]
         ];

在re-reduce期间,函数会从自己之前获取旧的结果。

// Re-reduce values
values = [ avg1
         , avg2
         , avg3
         ]

因此,我将首先检查您的代码在rereduce 为真时的工作方式。也许一些简单的事情可以解决它(虽然我经常要log() 事情直到我发现问题。)

function(keys, values, rereduce) {
  if(rereduce)
     return sum(values);

  // ... then the same code as before.
}

【讨论】:

  • @jhs:感谢您的解决方案。等了很久才有人回复这个。我会尝试并回复。但是,你能解释一下你在沙发上是怎么log() 的吗?我虽然没有办法log/console.log。这将使我的事情变得更容易。万分感谢! :)
  • 我认为你的问题已经解决了。下一个错误是无关的。 (Reduce 必须减少数据量,而不是简单地将其转换为不同的格式,即数组。)
  • 另外,这可能是您的数据中混合的字符串吗?看起来可能是字符串 "001" 触发了串联而不是数字加法。
  • 无字符串。我很确定。另外,我将结果作为额外的预防措施。见图 1。Number(values[i][0])。到目前为止,我还没有做rereduce。我只是将数组发送给客户端,客户端计算平均值。所以如果它在客户端工作,它也应该在 CouchDB 中工作,对吧?
  • 好的。问题的更新并未显示我正在投射价值和总数。然而,我在减少视图中这样做。事实上,如果我只返回values 而不是返回sum,我会得到数组本身的null。所以 Couchdb 出于某种奇怪的原因传递 null 而不是要重新减少的数组。
【解决方案2】:

我会详细说明我的计数/总和评论,以防你好奇。

此代码未经测试,但希望您能理解。最终结果始终是一个简单的对象{"count":C, "sum":S},您可以通过计算S / C 知道平均值。

function (key, values, rereduce) {
  // Reduce function
  var count = 0;
  var sum = 0;
  var i;

  if(!rereduce) {
    // `values` stores actual map output
    for(i = 0; i < values.length; i++) {
      count += Number(values[i][1]);
      sum += Number(values[i][0]);
    }

    return {"count":count, "sum":sum};
  }

  else {
    // `values` stores count/sum objects returned previously.
    for(i = 0; i < values.length; i++) {
      count += values[i].count;
      sum   += values[i].sum;
    }

    return {"count":count, "sum":sum};
  }
}

【讨论】:

  • 啊!这很好。适用于大多数情况。但是,就我而言,我正在存储预先计算的值和计数,只需要找到平均值。所以我也这样做,但平均而言。详细说明:对于特定的时间戳,我有一个通过 redis 计算的键的值/计数(在某些情况下,mongodb .. 取决于数据的类型)。然后我将其存储到 couchdb 以进行报告/图表。 :) 使用 map/reduce 来计算平均值(值/计数)
  • 酷。我想你有一个讨厌的现实世界的要求:)
【解决方案3】:

我使用以下代码进行平均。希望对您有所帮助。

function (key, values) {
    return sum(values)/values.length;    
}

【讨论】:

    猜你喜欢
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多