web-dev-qa-db-ja.com

CouchDBで1対多の「JOIN」を実行するための最良の方法

「SQL結合」に相当するCouchDBを探しています。

私の例では、リスト要素であるCouchDBドキュメントがあります。

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

リストを定義するドキュメントが1つあります。

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

3番目の要素が削除されたことがわかるように、それはリストの一部ではなくなりました。したがって、結果の一部であってはなりません。ここで、正しい順序を含むコンテンツ要素を返すビューが必要です。

結果は次のようになります。

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

この場合、要素の順序はすでにあるべきものです。別の可能な結果:

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

Map関数を書き始めました:

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関数を書いてくれませんか? 3番目のドキュメントは結果の一部であってはならないことに注意してください。

もちろん、別のマップ関数を作成することもできます。ただし、ドキュメントの構造(エントリごとに1つのdefinig要素ドキュメントとエントリドキュメント)は変更できません。


編集:彼の答えに対するJasonSmithのコメントをお見逃しなく。彼はこれをより短くする方法を説明しています。

34
mit

ありがとうございました!これは自慢できる素晴らしい例です CouchDB 0.11の新機能

ビュー内のドキュメントを参照するには、fetch-related-data機能を使用する必要があります。オプションで、より便利なJSONとして、__list_関数を使用します。結果をクリーンアップします。詳細については、 「JOIN」に関するCouchioの記事 を参照してください。

計画は次のとおりです。

  1. まず、elドキュメントに一意性の制約があります。それらのうちの2つが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ドキュメントの簡単なリストが正しい順序で表示されます。特定のリストのみを表示する場合は、startkeyおよびendkeyを使用できます。

    _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から情報を解析できます。次のoptionalステップは、必要なものに一致するように再フォーマットするだけです。

  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"]}
    _
52
JasonSmith