在CouchDB中进行一对多“JOIN”的最佳方法

时间:2010-06-13 18:43:04

标签: couchdb mapreduce

我正在寻找与“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函数吗?请记住,第三个文档不得是结果的一部分。

当然你也可以写一个不同的地图功能。但是文档的结构(一个definig元素文档和每个条目的条目文档)不能改变。


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

1 个答案:

答案 0 :(得分:51)

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

您必须使用与提取相关的数据功能来引用文档 在视图中。可选地,为了更方便的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),然后重新插入到。{1}} 临时数据库。这会将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进行查询。通过魔术 _idel文档将加载。

    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的信息。下一个可选 步骤只需重新格式化,以符合您的需要。

  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"]}