【问题标题】:Map/Reduce differences between Couchbase & CloudAnt映射/减少 Couchbase 和 CloudAnt 之间的差异
【发布时间】:2011-05-18 22:31:05
【问题描述】:

我一直在使用 Couchbase Server,现在只是尝试将我的本地数据库复制到 Cloudant,但是我的 map/reduce 函数对与他们的关联项目构建一组独特的标签时得到了冲突的结果...

// map.js
function(doc) {
  if (doc.tags) {
    for(var t in doc.tags) {
      emit(doc.tags[t], doc._id);
    }
  }
}

// reduce.js
function(key,values,rereduce) {
  if (!rereduce) {
    var res=[];
    for(var v in values) {
      res.push(values[v]);
    }
    return res;
  } else {
    return values.length;
  }
}

在 Cloudbase 服务器中,这会返回 JSON,如下所示:

{"rows":[
{"key":"3d","value":["project1","project3","project8","project10"]},
{"key":"agents","value":["project2"]},
{"key":"fabrication","value":["project3","project5"]}
]}

这正是我想要和期望的。但是,对 Cloudant 副本的相同查询会返回:

{"rows":[
{"key":"3d","value":4},
{"key":"agents","value":1},
{"key":"fabrication","value":2}
]}

所以它以某种方式只返回值数组的长度......非常令人困惑&感谢一些 M&R ninjas 的任何见解...... ;)

【问题讨论】:

    标签: couchdb mapreduce cloudant couchbase


    【解决方案1】:

    看起来这正是您在使用 reduce 函数时所期望的行为。关键部分是这样的:

    else {
    return values.length;
    }
    

    在 Cloudant 中,总是调用 rereduce(因为 reduce 需要跨越多个分片。)在这种情况下,rereduce 调用 values.length,它只会返回数组的长度。

    【讨论】:

    • 我明白了,但是我怎样才能编写这个查询,以便它在两个平台上产生相同的结果(#1)?我现在不在乎(我确实在乎 :) 我的 reduce 函数并不完美,但我会假设一个一致的行为......
    • +1,艾伦。 Toxi,我建议您重写,以便您可以“闻到”重新减少与初始减少的味道,而无需检查从沙发上传递的参数。我以前做过,所以我会尝试作为一个实际的答案,以获得更好的格式。
    • 您只需要记住 Cloudant 总是 调用 rereduce,因此如果您希望两者保持一致,则需要您的 rereduce 子句来匹配您的 reduce 子句。附带说明:CouchDB 中任何重要的 reduce 函数都将使用 rereduce,因此最好了解引擎盖下的机制。参见例如wiki.apache.org/couchdb/…
    • 我一直忽略生产中的重新缩减。我会在我的回答中告诉你。
    【解决方案2】:

    我更喜欢隐式地减少/重新减少,而不是依赖于rereduce 参数。

    function(doc) { // map
      if (doc.tags) {
        for(var t in doc.tags) {
          emit(doc.tags[t], {id:doc._id, tag:doc.tags[t]});
        }
      }
    }
    

    然后reduce检查它是从相同标签中累积文档ID,还是只是在计算不同的标签。

    function(keys, vals, rereduce) {
      var initial_tag = vals[0].tag;
    
      return vals.reduce(function(state, val) {
        if(initial_tag && val.tag === initial_tag) {
          // Accumulate ids which produced this tag.
          var ids = state.ids;
          if(!ids)
            ids = [ state.id ]; // Build initial list from the state's id.
          return { tag: val.tag, 
                 , ids: ids.concat([val.id])
                 };
        } else {
          var state_count = state.ids ? state.ids.length : state;
          var val_count   = val.ids   ? val.ids.length   : val;
          return state_count + val_count;
        } 
      })
    }
    

    (我没有测试这段代码,但你明白了。只要tag 的值相同,不管是reduce 还是rereduce 都无所谓。一次不同的标签一起开始减少,它检测到因为tag的值会改变。所以此时开始累积。

    我以前用过这个技巧,虽然 IMO 很少值得这样做。

    同样在您的具体情况中,这是一个危险的 reduce 函数。您正在构建一个广泛的列表以查看所有具有标签的文档。 CouchDB 喜欢高列表,而不是胖列表。如果你想查看所有有标签的文档,你可以映射它们。

    for(var a = 0; a < doc.tags.length; a++) {
      emit(doc.tags[a], doc._id);
    }
    

    现在你可以查询/db/_design/app/_view/docs_by_tag?key="3d",你应该得到

    {"total_rows":287,"offset":30,"rows":[
    {"id":"project1","key":"3d","value":"project1"}
    {"id":"project3","key":"3d","value":"project3"}
    {"id":"project8","key":"3d","value":"project8"}
    {"id":"project10","key":"3d","value":"project10"}
    ]}
    

    【讨论】:

    • 感谢大家提供的所有见解! (很抱歉,我花了这么长时间才回复,被拉走了)我已经设法解决了问题,但仍然发现有时很难在没有任何调试功能的情况下完全理解 reduce 部分。能够看到中间结果将有助于更好地了解正在发生的事情......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多