根据某些条件从 CouchDB 中提取一组文档 ID 时如何使用 MapReduce

Nee*_*eek 5 couchdb mapreduce

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

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

我想查询我的文档列表:

{ _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' } }
Run Code Online (Sandbox Code Playgroud)

我想要结果:

'foobar': [ 'document1', 'document4', 'document6' ]
'xyz': [ 'document2', 'document3' ]
'wibble': [ 'document5' ]
Run Code Online (Sandbox Code Playgroud)

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

在 SQL 中,我们可以根据 TheID 字段进行排序或分组,并适当地迭代结果集。我怀疑这种想法对于 CouchDB 查询是否有任何用处。

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

function (doc) {
  emit(doc.Message.TheID, 1);
}
Run Code Online (Sandbox Code Playgroud)

也许

function (doc) {
  emit(doc._id, doc.Message.TheID);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

那么,映射/归约对在这里做正确的事情吗?我应该考虑使用“显示”还是“列表”?我打算围绕结果构建一个基于 Mustache 的 HTML 模板引擎,因此“列表”似乎是错误的方法。

预先感谢您的任何指导。

编辑我已经做了一些本地开发并提出了我认为是一个损坏的解决方案。希望这能为您指明我正在尝试的方向。请参阅我在https://neek.iriscouch.com/_utils/database.html?test/_design/test/_view/collectByTheID创建的基于公共云的 CouchDB

这是公开的。如果您想玩,请将其复制到新视图,不要污染此视图,以防其他人进来想看原版。

地图功能:

function(doc) {
  emit(doc.Message.TheID, doc._id);
}
Run Code Online (Sandbox Code Playgroud)

减少功能:

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

结果:

"foobar"   ["document6", "document4", "document1"]
"wibble"   ["document5"]
"xyz"      ["document3", "document2"]
Run Code Online (Sandbox Code Playgroud)

reduce 函数首先保留值数组,然后在第二遍将它们连接在一起。然而,当我在 5000 多个文档数据库上运行它时,它会出现一些带有空文档 ID 数组的 TheID 值。我相信这遇到了我之前提到的问题,其中传递给reduce的值数组是根据从中提取它们的映射的btree结构构建的,并且不能保证包含给定键的完整值集。

Mat*_*ngs 2

利用 group_level 功能:

地图:

emit([doc.message.TheID, doc._id], null)
Run Code Online (Sandbox Code Playgroud)

减少:

您必须包含一个reduce才能使用group_level,它可以为空,如下所示或其他内容,即_count

function(keys, values){
   return null;
}
Run Code Online (Sandbox Code Playgroud)

group_level=1 的查询将返回:

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

[
 {key: ["foobar"], value: null}, 
 {key: ["xyz"], value: null}, 
 {key: ["wibble"], value: null}
]
Run Code Online (Sandbox Code Playgroud)

您将使用此查询来填充分组 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}
]
Run Code Online (Sandbox Code Playgroud)

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