リストをフィルタリングし、集計に基づいて並べ替えたいと思います。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」のみが選択されている場合) のようになります。これはクエリが簡単で、高速です。しかし、インデックスのディスク サイズは指数関数的に増加するようです (フィルター処理された個別のフィールドの数に応じて)。また、日付範囲などの制限のないデータをフィルター処理することは、まったく受け入れられないようです。
最後に、必要なフィルターをその場で処理するビューを動的に生成し、必要に応じてのみ使用し、それらが使用されなくなったら破棄することができます (ディスク容量を節約するため)。ここでの欠点は、コードが非常に複雑になることと、新しいフィルターが選択されるたびに大きな初期費用がかかることです。
より良い方法はありますか?