如何使用MapReduce从CouchDB中按某些条件提取一组文档id

如何使用MapReduce从CouchDB中按某些条件提取一组文档id,mapreduce,couchdb,Mapreduce,Couchdb,我正在进行CouchDB实验的第一周,试图停止使用SQL进行思考。我有一个文档集合5000个事件文件,这些文件都有一些ID值,这些ID值对于文档组来说是通用的。所以可能有10个都有“foobar” 如果有人问-TheID不是关系数据库中的自动增量值-它是我们的合作伙伴公司分配的唯一id。我不能重新设计我的源数据来以其他方式识别自己,我必须使用这个TheID字段来识别文档组 我想查询我的文档列表: { _id: 'document1', Message: { TheID: 'foobar' } }

我正在进行CouchDB实验的第一周,试图停止使用SQL进行思考。我有一个文档集合5000个事件文件,这些文件都有一些ID值,这些ID值对于文档组来说是通用的。所以可能有10个都有“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列表

表示新CouchDB用户犯的一个常见错误是试图用reduce函数构造复杂的聚合值。完全缩减应该产生一个标量值,比如5,而不是一个包含一组唯一键和每个键的计数的JSON哈希

我想我可以使用reduce来扫描地图的结果,并以某种方式将所有具有公共TheID值的结果收集到单个结果对象中。我在阅读reduce文档时看到的是,它将获得包含相当不可预测的集合的键和值数组,这些集合是由映射结果基础的btree结构驱动的。它不能保证数组包含我可以扫描的所有类似的TheID值。这种方法似乎完全失败了

那么,在这里使用map/reduce对正确吗?我应该用“show”或“list”来代替吗?我打算围绕结果构建一个基于胡须的HTML模板引擎,所以“列表”似乎是错误的选择

提前感谢您的指导

编辑我已经做了一些本地开发,并提出了我认为是一个失败的解决方案。希望这能告诉你我努力的方向。请参阅我在上创建的基于公共云的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;
  }
}
减少功能:

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结构,并且不保证包含给定键的完整值集。

利用组级功能:

地图:

减少:

您必须包括一个reduce to use group_级别,它可以是空的,如下所示或其他内容,即_count

function(keys, values){
   return null;
}
组级别为1的查询将返回:

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

[
 {key: ["foobar"], value: null}, 
 {key: ["xyz"], value: null}, 
 {key: ["wibble"], value: null}
]
您可以使用此查询填充分组UI中的顶层。当用户展开一个类别时,您将使用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}
]

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

谢谢Matt!尝试在普通地图视图上使用group_level时出错:->{error:query_parse_错误,原因:在地图视图上使用分组无效。}。。分组不是只有在使用reduce函数时才起作用吗?忘了包括它,是的,您需要reduce,但它可以是空的。我会修改我的回答谢谢马特。我们在这方面的工作暂时搁置了。我们的实际查询更为复杂,它列出了按每个文档中找到的最新日期值排序的唯一ID值,我在上面的问题陈述中没有包括这些值。这增加了复杂性,使得MapReduce查询很难解决,我们不得不优先考虑其他工作。我们会在可能的时候再谈这个。