如何在通过CouchDB中的某些条件提取一组文档ID时使用MapReduce

时间:2014-03-22 10:00:09

标签: mapreduce couchdb

我正在进行CouchDB实验的第一周,并试图停止在SQL中思考。我有一组文档(5000个事件文件),它们都具有一些ID值,这些ID值对于文档组是通用的。所以可能有10个人都有TheID:'foobar'。

(如果有人问--TheID不是来自关系数据库的自动增量值 - 它是我们的合作伙伴公司分配的唯一ID。我无法重新设计我的源数据以通过其他方式识别自己,我有使用此TheID字段识别文档组。)

我想查询我的文件清单:

{ _id: 'document1', Message: { TheID: 'foobar' } }
{ _id: 'document2', Message: { TheID: 'xyz' } }
{ _id: 'document3', Message: { TheID: 'xyz' } }
{ _id: 'document4', Message: { TheID: 'foobar' } }
{ _id: 'document5', Message: { TheID: 'wibble' } }
{ _id: 'document6', Message: { TheID: 'foobar' } }

我想要结果:

'foobar': [ 'document1', 'document4', 'document6' ]
'xyz': [ 'document2', 'document3' ]
'wibble': [ 'document5' ]

目的是在我们的UI上表示按TheID分组的文档组,以便用户可以一起查看特定TheID的所有文档,并选择TheID钻取到仅通过该TheID值查询的数据。是的,每个文档的字符串id都很有用 - 在我们的例子中,每个文档的_id值是源事件标识符,因此它是用户希望在屏幕列表中看到的唯一且有用的值。

在SQL中,可以按TheID字段排序或分组,并适当地迭代结果集。我怀疑这个想法对于CouchDB查询是否有用。

我知道我可以使用map函数来提取每个文档的TheID值,例如:

function (doc) {
  emit(doc.Message.TheID, 1);
}

或者

function (doc) {
  emit(doc._id, doc.Message.TheID);
}

我不确定我应该发出什么作为关键和价值。即使这很有用,我也觉得我不应该使用reduce函数来尝试将大型地图输出(数据库中每个文档的1个结果行)“减少”到我想要的(每个结果3个结果)文件ID列表。

http://guide.couchdb.org/draft/views.html说“新CouchDB用户犯的一个常见错误是尝试使用reduce函数构造复杂的聚合值。完全减少应该产生标量值,如5,而不是,例如,JSON哈希带有一组唯一键和每个键的数量。“

我以为我可以使用reduce来扫描地图的结果,并以某种方式将具有共同TheID值的所有结果收集到单个结果对象中。我在阅读reduce文档时看到的是,它将被赋予包含相当不可预测的集合的键和值的数组,这些集合由地图结果底层的btree结构驱动。它不会被保证包含我可以扫描的所有类似的TheID值。这种方法似乎完全被打破了。

那么,map / reduce对是否正确?我应该看一下使用'show'或'list'吗?我打算围绕结果构建一个基于胡子的HTML模板引擎,所以'list'似乎是错误的方法。

提前感谢任何指导。

编辑我已经完成了一些本地开发工作,并提出了我认为破解的解决方案。希望这将向您展示我想要进入的方向。请参阅我在https://neek.iriscouch.com/_utils/database.html?test/_design/test/_view/collectByTheID创建的基于公共云的CouchDB

这是公开的。如果你想玩,请把它复制到一个新的视图,不要污染这个,以防其他人进来,并希望看到原件。

地图功能:

function(doc) {
  emit(doc.Message.TheID, doc._id);
}

减少功能:

function(keys, values, rereduce) {
  if (!rereduce) {
    return values;
  } else {
    var ret = [];
    values.forEach(function (ar) {
      ret.concat(ar);
    });
    return ret;
  }
}

结果:

"foobar"   ["document6", "document4", "document1"]
"wibble"   ["document5"]
"xyz"      ["document3", "document2"]

reduce函数首先单独保留值数组,并在第二遍中将它们连接在一起。但是,当我在我的大型5000+文档数据库上运行它时,它会出现一些带有空文档id数组的TheID值。我相信这会遇到我之前提到的问题,其中传递给reduce的值数组是依赖于它们从中提取的btree结构而构建的,并且不能保证包含给定键的一组完整值。

1 个答案:

答案 0 :(得分:2)

使用group_level功能:

地图:

emit([doc.message.TheID, doc._id], null)

减少

你必须包含一个reduce来使用group_level,它可以是空的,如下所示,或者_count

function(keys, values){
   return null;
}

group_level = 1的查询将返回:

/_design/d/_view/v?group_level=1

[
 {key: ["foobar"], value: null}, 
 {key: ["xyz"], value: null}, 
 {key: ["wibble"], value: null}
]

您可以使用此查询填充分组用户界面中的顶级。当用户展开类别时,您将使用group_level 2以及开始和结束键执行另一个查询:

/_design/d/_view/v?group_level=2&startkey=["foobar"]&endkey=["foobar",{}]

[
  {key: ["foobar", "document6"], value: null}, 
  {key: ["foobar", "document4"], value: null}, 
  {key: ["foobar", "document1"], value: null}
]

这并不能完全按照您的要求生成输出,但是,我认为您会发现它足够灵活