6

CouchDB で「結合」を行う場合、ビュー照合を使用してレコードをグループ化できます。たとえば、顧客注文の 2 つのドキュメント タイプがあるとします。customer 、その顧客のすべての注文、次の顧客、注文を返すことができるようにします。

問題は、顧客が 10 人、注文が 40 件ある場合、出力が 50 行ではなく 10 行になるように行をマージするにはどうすればよいかということです。基本的に、顧客行にさらに情報を追加します。

a_listまたは aを使用するreduceとこれが解決すると思います。問題は、これをどのように正確に行うかです。

4

2 に答える 2

5

私は 2 番目の jhs の回答ですが、彼の「オプション 2」は危険すぎると思います。私はそれを難し​​い方法で学びました。reduce 関数は、ブログの各ユーザーの最後の投稿を取得するなど、多くの優れた機能に使用できますが、返されるデータの量を減らさないものには使用できません。

事実でそれをサポートするために、私はこの小さなスクリプトを作成して、それぞれ 20 の注文を持つ 200 人の顧客を生成しました。

#!/bin/bash
echo '{"docs":['
for i in $(seq 1 200); do
  id=customer/$i
  echo '{"_id":"'$id'","type":"customer","name":"Customer '$i'"},'
  for o in $(seq 1 20); do
    echo '{"type":"order","_id":"order/'$i'/'$o'", "for":"'$id'", "desc":"Desc '$i$o'"},'
  done
done
echo ']}'

これは非常に可能性の高いシナリオであり、Error: reduce_overflow_error.

私見あなたが持っている2つのオプションは次のとおりです。

オプション 1: 最適化されたリスト機能

ちょっとした作業で JSON 応答を手動で作成できるため、注文を配列に蓄積する必要はありません。

jhs の list 関数を編集して、配列を使用しないようにしました。これにより、任意の数の注文を持つ顧客を持つことができます。

function(head, req) {
  start({'headers':{'Content-Type':'application/json'}});

  var first_customer = true
    , first_order = true
    , row
    ;

  send('{"rows":[');

  while(row = getRow()) {
    if(row.key[1] === 2) {
      // Order for customer
      if (first_order) {
        first_order = false;
      } else {
        send(',');
      }
      send(JSON.stringify(row.value));
    }
    else if (row.key[1] === 1) {
      // New customer
      if (first_customer) {
        first_customer = false;
      } else {
        send(']},');
      }
      send('{"customer":');
      send(JSON.stringify(row.key[0]));
      send(',"orders":[');
      first_order = true;
    }
  }
  if (!first_customer)
    send(']}');

  send('\n]}');
}

オプション 2: ユース ケースに合わせてドキュメントを最適化する

注文を同じドキュメントに格納する必要がある場合は、この方法で保存し、クエリ中の処理を回避できるかどうかを自問してください。

言い換えれば、文書データベースが提供する可能性を最大限に活用しようとすることです。ユースケースに最適なドキュメントを設計し、ドキュメントを使用するために必要な後処理を減らします。

于 2011-05-23T08:16:56.230 に答える
2

CouchDBの主な「意見」の1つは、分散型のクラスター化された設定でも可能なことのみを実行するというものです。実際には、これは最初は不便であり、後でコードを変更せずに大きなスケーラビリティが得られることを意味します。

言い換えれば、「参加」の質問に対する完全な答えはありません。しかし、私は2つのかなり良いオプションがあると思います。

私はこのデータセットを使用しています:

$ curl localhost:5984/so/_bulk_docs -XPOST -Hcontent-type:application/json -d @-
{"docs":[
{"type":"customer","name":"Jason"},
{"type":"customer","name":"Hunter"},
{"type":"customer","name":"Smith"},
{"type":"order", "for":"Jason", "desc":"Hat"},
{"type":"order", "for":"Jason", "desc":"Shoes"},
{"type":"order", "for":"Smith", "desc":"Pan"}
]}
^D

[{"id":"4cb766ebafda06d8a3a7382f74000b46","rev":"1-8769ac2fffb869e795c347e7b8c653bf"},
{"id":"4cb766ebafda06d8a3a7382f74000b7d","rev":"1-094eff3e3a5967d974fcd7b3cfd7e454"},
{"id":"4cb766ebafda06d8a3a7382f740019cb","rev":"1-5cda0b61da4c045ff503b57f614454d5"},
{"id":"4cb766ebafda06d8a3a7382f7400239d","rev":"1-50642a9809f15283a9d938c8fe28ef27"},
{"id":"4cb766ebafda06d8a3a7382f74002778","rev":"1-d03d883fb14a424e3db022350b38c510"},
{"id":"4cb766ebafda06d8a3a7382f74002c5c","rev":"1-e9612f5d267a8442d3fc2ae09e8c800d"}]

そして私のマップ機能は

function(doc) {
  if(doc.type == 'customer')
    emit([doc.name, 1], "");
  if(doc.type == 'order')
    emit([doc.for, 2], doc.desc);
}

フルビューをクエリすると、次のように表示されます。

{"total_rows":6,"offset":0,"rows":[
{"id":"4cb766ebafda06d8a3a7382f74000b7d","key":["Hunter",1],"value":""},
{"id":"4cb766ebafda06d8a3a7382f74000b46","key":["Jason",1],"value":""},
{"id":"4cb766ebafda06d8a3a7382f7400239d","key":["Jason",2],"value":"Hat"},
{"id":"4cb766ebafda06d8a3a7382f74002778","key":["Jason",2],"value":"Shoes"},
{"id":"4cb766ebafda06d8a3a7382f740019cb","key":["Smith",1],"value":""},
{"id":"4cb766ebafda06d8a3a7382f74002c5c","key":["Smith",2],"value":"Pan"}
]}

オプション1:結果を収集するためのリスト機能

利点は、10行を要求した場合、確実に10行になることです(もちろん十分なデータがない場合を除く)。

ただし、コストは、すべてのクエリに対してサーバー側の処理を実行する必要があることです。必要なデータはディスク上にあり、ストリーミングする準備ができていましたが、これでこのボトルネックを乗り越えました。

_listただし、個人的には、パフォーマンスの問題が実証されていない限り、それは素晴らしいことだと感じています。

function(head, req) {
  start({'headers':{'Content-Type':'application/json'}});

  send('{"rows":');

  var customer = null, orders = [], count = 0;

  var prefix = '\n[ ';
  function show_orders() {
    if(customer && orders.length > 0) {
      count += 1;

      send(prefix);
      prefix = '\n, ';

      send(JSON.stringify({'customer':customer, 'orders':orders}));
    }
  }

  function done() {
    send('\n]}');
  }

  var row;
  while(row = getRow()) {
    if(row.key[1] == 2) {
      // Order for customer
      orders.push(row.value);
    }

    if(row.key[1] == 1) {
      // New customer
      show_orders();

      if(req.query.lim && count >= parseInt(req.query.lim)) {
        // Reached the limit
        done();
        return;
      } else {
        // Prepare for this customer.
        customer = row.key[0];
        orders = [];
      }
    }
  }

  // Show the last order set seen and finish.
  show_orders();
  done();
}

この関数は単に行をループし、mapすべての情報が収集された後にのみ完全なcustomer+orders行を出力します。もちろん、出力するJSON形式は変更できます。また、パラメータを使用するとマップクエリが妨害される?lim=Xため、パラメータがあります。limit

危険なのは、この関数がメモリ内に無制限の応答を構築することです。顧客が10,000件の注文をした場合はどうなりますか?または100,000?最終的に、ordersアレイの構築は失敗します。これが、CouchDBがそれらを「トール」リストに保持する理由です。顧客ごとに10,000件の注文を受け取らない場合は、これは問題ではありません。

$ curl 'http://localhost:5984/so/_design/ex/_list/ex/so?reduce=false&lim=2'
{"rows":
[ {"customer":"Jason","orders":["Hat","Shoes"]}
, {"customer":"Smith","orders":["Pan"]}
]}

オプション2:卑劣な削減

関数を使って似たようなことをすることができますreduce。ここで、これはディスクに応答を蓄積するため、技術的にスケーラブルではないことを警告しますが、コードが単純であり、後処理なしでディスクからデータを直接読み取ることがわかっているため、個人的には_listではなくそれを好みます。

function(keys, vals, re) {
  // If all keys are the same, then these are all
  // orders for the same customer, so accumulate
  // them. Otherwise, return something meaningless.
  var a;

  var first_customer = keys[0][0][0];
  for(a = 0; a < keys.length; a++)
    if(keys[a][0][0] !== first_customer)
      return null;

  var result = [];
  for(a = 0; a < vals.length; a++)
    if(vals[a]) {
      // Accumulate an order.
      result.push(vals[a]);
    }
  return result;
}

結果を顧客ごとにセグメント化するこのビューを常に照会?group_level=1します(顧客名がmapキーの最初の項目であったため)。

削減フェーズ中にデータを蓄積することは想定されていないため、これは法律に違反します。それが彼らがそれをreduceと呼ぶ理由です。

ただし、CouchDBはリラックスしており、巨大なリストを作成しない限り、機能するはずであり、はるかにエレガントです。

$ curl 'localhost:5984/so/_design/ex/_view/so?group_level=1&limit=3'
{"rows":[
{"key":["Hunter"],"value":[]},
{"key":["Jason"],"value":["Shoes","Hat"]},
{"key":["Smith"],"value":["Pan"]}
]}

幸運を!

于 2011-05-22T02:54:42.570 に答える