【问题标题】:Best way to do one-to-many "JOIN" in CouchDB在 CouchDB 中进行一对多“JOIN”的最佳方式
【发布时间】:2011-03-03 06:49:27
【问题描述】:

我正在寻找相当于“SQL 连接”的 CouchDB。

在我的示例中,CouchDB 文档是列表元素:

{ "type" : "el", "id" : "1", "content" : "first" } 
{ "type" : "el", "id" : "2", "content" : "second" } 
{ "type" : "el", "id" : "3", "content" : "third" } 

有一个文档定义了列表:

{ "type" : "list", "elements" : ["2","1"] , "id" : "abc123" }

如您所见,第三个元素已被删除,它不再是列表的一部分。所以它不能是结果的一部分。现在我想要一个返回内容元素的视图,包括正确的顺序。

结果可能是:

{ "content" : ["second", "first"] }

在这种情况下,元素的顺序已经是应该的了。另一个可能的结果:

{ "content" : [{"content" : "first", "order" : 2},{"content" : "second", "order" : 1}] }

我开始写地图功能:

map = function (doc) {
  if (doc.type === 'el') {
    emit(doc.id, {"content" : doc.content}); //emit the id and the content
    exit;
  }
  if (doc.type === 'list') {
    for ( var i=0, l=doc.elements.length; i<l; ++i ){
      emit(doc.elements[i], { "order" : i }); //emit the id and the order
    }
  }
}

这是我所能得到的。你能纠正我的错误并写一个reduce函数吗?请记住,第三个文档不能是结果的一部分。

当然你也可以写一个不同的地图函数。但是文档的结构(一个定义元素文档和每个条目的一个条目文档)不能更改。


编辑:不要错过 JasonSmith 对他的回答的评论,他在其中描述了如何更短地做到这一点。

【问题讨论】:

    标签: couchdb mapreduce


    【解决方案1】:

    谢谢!这是炫耀CouchDB 0.11's new features的好例子!

    您必须使用 fetch-related-data 功能来引用文档 (可选)为了更方便的 JSON,使用 _list 函数 清理结果。详情请见Couchio's writeup on "JOIN"s

    计划如下:

    1. 首先,您的el 文档具有唯一性约束。如果两个 他们有 id=2,这是个问题。有必要使用 _id 字段而不是 id。 CouchDB 将保证唯一性,而且, 该计划的其余部分需要_id 才能按 ID 获取文档。

      { "type" : "el", "_id" : "1", "content" : "first" } 
      { "type" : "el", "_id" : "2", "content" : "second" } 
      { "type" : "el", "_id" : "3", "content" : "third" } 
      

      如果更改文档以使用_id 绝对不可能,您可以 为emit(doc.id, doc) 创建一个简单视图,然后将其重新插入到 临时数据库。这会将id 转换为_id,但会增加一些复杂性。

    2. 视图发出{"_id": content_id} 键控数据 [list_id, sort_number],将列表与其内容“组合”起来。

      function(doc) {
        if(doc.type == 'list') {
          for (var i in doc.elements) {
            // Link to the el document's id.
            var id = doc.elements[i];
            emit([doc.id, i], {'_id': id});
          }
        }
      }
      

      现在有一个简单的el 文档列表,按正确的顺序排列。你可以 如果您只想查看特定列表,请使用 startkeyendkey

      curl localhost:5984/x/_design/myapp/_view/els
      {"total_rows":2,"offset":0,"rows":[
      {"id":"036f3614aeee05344cdfb66fa1002db6","key":["abc123","0"],"value":{"_id":"2"}},
      {"id":"036f3614aeee05344cdfb66fa1002db6","key":["abc123","1"],"value":{"_id":"1"}}
      ]}
      
    3. 要获取el 内容,请使用include_docs=true 进行查询。通过魔法 _id,将加载 el 文档。

      curl localhost:5984/x/_design/myapp/_view/els?include_docs=true
      {"total_rows":2,"offset":0,"rows":[
      {"id":"036f3614aeee05344cdfb66fa1002db6","key":["abc123","0"],"value":{"_id":"2"},"doc":{"_id":"2","_rev":"1-4530dc6946d78f1e97f56568de5a85d9","type":"el","content":"second"}},
      {"id":"036f3614aeee05344cdfb66fa1002db6","key":["abc123","1"],"value":{"_id":"1"},"doc":{"_id":"1","_rev":"1-852badd683f22ad4705ed9fcdea5b814","type":"el","content":"first"}}
      ]}
      

      注意,这已经是您需要的所有信息了。如果您的客户是 灵活,您可以从这个 JSON 中解析出信息。下一个可选 step 只是将其重新格式化以匹配您需要的内容。

    4. 使用_list 函数,它只是重新格式化视图输出。人们使用它们来输出 XML 或 HTML,但是我们将制作 JSON更方便。

      function(head, req) {
        var headers = {'Content-Type': 'application/json'};
        var result;
        if(req.query.include_docs != 'true') {
          start({'code': 400, headers: headers});
          result = {'error': 'I require include_docs=true'};
        } else {
          start({'headers': headers});
          result = {'content': []};
          while(row = getRow()) {
            result.content.push(row.doc.content);
          }
        }
        send(JSON.stringify(result));
      }
      

      结果匹配。当然在生产中你需要startkeyendkey 来指定你想要的列表。

      curl -g 'localhost:5984/x/_design/myapp/_list/pretty/els?include_docs=true&startkey=["abc123",""]&endkey=["abc123",{}]'
      {"content":["second","first"]}
      

    【讨论】:

    • 字,这是一个彻底的。干得好 jhs!
    • 这是误导性的长。实际上,第 2 步是一个完整的解决方案,但是,是的,我详细说明了背景要求和在客户端增加便利的可选方法。
    • CouchIO URL 已更改,这是同一篇文章的新 URL:couchio.tumblr.com/post/446015664/…
    • 我有一个类似的用例,我需要一个具有一对一映射的连接,但是我需要两个文档。按照这个答案,我可以发出两个文档,但在单独的行中使用发出两次,我可以使用发出一次吗?
    猜你喜欢
    • 2013-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    • 2014-09-16
    相关资源
    最近更新 更多