1

リストをフィルタリングし、集計に基づいて並べ替えたいと思います。SQL で表現するのはかなり簡単ですが、反復的な Map Reduce を使用してそれを行う最善の方法について困惑しています。私は具体的に Cloudant の CouchDB への「dbcopy」追加を使用していますが、アプローチは他の map/reduce アーキテクチャと似ていると思います。

疑似コード SQL は次のようになります。

SELECT   grouping_field, aggregate(*)
FROM     data
WHERE    #{filter}
GROUP BY grouping_field
ORDER BY aggregate(*), grouping_field
LIMIT    page_size

フィルターは一致を探しているか、範囲内で検索している可能性があります。例: field in ('foo', 'bar')またはfield between 37 and 42.

具体的な例として、電子メールのデータセットを考えてみましょう。グループ化フィールドは、「List-id」、「Sender」、または「Subject」です。集約関数はcount(*)、 またはmax(date)またはmin(date); また、フィルター句は、フラグ、日付範囲、またはメールボックス ID を考慮する場合があります。ドキュメントは次のようになります。

{
  "id": "foobar", "mailbox": "INBOX", "date": "2013-03-29",
  "sender": "foo@example.com", "subject": "Foo Bar"
}

同じ送信者のメールの数を取得するのは簡単です:

"map": "function (doc) { emit(doc.sender, null) }",
"reduce": "_count"

また、Cloudant には、map reduce の 2 番目のパスで count によってソートする良い例があります。しかし、(メールボックスなどで) フィルター処理も行いたい場合は、すぐに面倒になります。

フィルター{"key": ["INBOX", 1234, "foo@example.com"], "value": null}をビュー キーに追加すると (たとえば、最終結果がこれは、大規模なデータ セットでは遅すぎます。

または、潜在的なフィルター選択ごとにインデックスを作成することもできます。たとえば、最終結果は{"key": [["mbox1", "mbox2"], 1234, "foo@example.com"], "value": null},(「mbox1」と「mbox2」の両方が選択され{"key": [["mbox1"], 1234, "foo@example.com"], "value": {...}},ている場合) または (「mbox1」のみが選択されている場合) のようになります。これはクエリが簡単で、高速です。しかし、インデックスのディスク サイズは指数関数的に増加するようです (フィルター処理された個別のフィールドの数に応じて)。また、日付範囲などの制限のないデータをフィルター処理することは、まったく受け入れられないようです。

最後に、必要なフィルターをその場で処理するビューを動的に生成し、必要に応じてのみ使用し、それらが使用されなくなったら破棄することができます (ディスク容量を節約するため)。ここでの欠点は、コードが非常に複雑になることと、新しいフィルターが選択されるたびに大きな初期費用がかかることです。

より良い方法はありますか?

4

1 に答える 1

0

私はこれについて 1 日近く考えてきましたが、あなたが提案した方法よりも良い方法はないと思います。直面する課題は次のとおりです。

1) 集計作業 (カウント、合計など) は、マテリアライズド ビュー エンジン (mapreduce) を介して CouchDB/Cloudant API でのみ実行できます。

2) group_level API は、クエリ時に可変粒度を指定する柔軟性を提供しますが、任意のブールクエリに対しては十分な柔軟性がありません。

3) lucene ベースの _search API を介して、Cloudant API で任意のブールクエリが可能です。ただし、_search API は集約ポストクエリをサポートしていません。やりたいことに対する限定的なサポートは、Cloudant ではまだサポートされていないファセットを使用する lucene でのみ可能です。それでも、それはサポートするだけで、より複雑な集計をサポートcountしない可能性があると思います。sum

あなたが直面している最善の選択肢は、_search API を使用し、sort、group_by、または group_sort を使用してから、クライアントで集計を行うことだと思います。テストするいくつかのサンプル URL は次のようになります。

GET /db/_design/ddoc/_search/indexname?q=name:mike AND age:[1.2 TO 4.5]&sort=["age","name"]

GET /db/_design/ddoc/_search/indexname?q=name:mike AND group_by="mailbox" AND group_sort=["age","name"]

于 2013-04-04T18:24:21.490 に答える