MapReduceは低速ですが、非常に大きなデータセットを処理できます。一方、アグリゲーションフレームワークは少し高速ですが、大量のデータに苦労します。
示されている構造の問題は、データをクラックして開くために配列を「$unwind」する必要があることです。これは、すべての配列アイテムに対して新しいドキュメントを作成することを意味し、集約フレームワークを使用して、メモリ内でこれを行う必要があります。したがって、100個の配列要素を持つ1000個のドキュメントがある場合、groupByしてそれらをカウントするには、100,000個のドキュメントのストリームを構築する必要があります。
クエリをより適切にサーバー化するスキーマレイアウトがあるかどうかを確認することを検討することもできますが、Aggregationフレームワークを使用して実行する場合は、次の方法で実行できます(サンプルデータを使用して、スクリプト全体をシェルにドロップします);
db.so.remove();
db.so.ensureIndex({ "items.sku": 1}, {unique:false});
db.so.insert([
{
_id: 42,
last_modified: ISODate("2012-03-09T20:55:36Z"),
status: 'active',
items: [
{ sku: '00e8da9b', qty: 1, item_details: {} },
{ sku: '0ab42f88', qty: 4, item_details: {} },
{ sku: '0ab42f88', qty: 4, item_details: {} },
{ sku: '0ab42f88', qty: 4, item_details: {} },
]
},
{
_id: 43,
last_modified: ISODate("2012-03-09T20:55:36Z"),
status: 'active',
items: [
{ sku: '00e8da9b', qty: 1, item_details: {} },
{ sku: '0ab42f88', qty: 4, item_details: {} },
]
},
]);
db.so.runCommand("aggregate", {
pipeline: [
{ // optional filter to exclude inactive elements - can be removed
// you'll want an index on this if you use it too
$match: { status: "active" }
},
// unwind creates a doc for every array element
{ $unwind: "$items" },
{
$group: {
// group by unique SKU, but you only wanted to count a SKU once per doc id
_id: { _id: "$_id", sku: "$items.sku" },
}
},
{
$group: {
// group by unique SKU, and count them
_id: { sku:"$_id.sku" },
doc_count: { $sum: 1 },
}
}
]
//,explain:true
})
SKUはドキュメントごとに1回しかカウントできないとおっしゃっていたため、$ groupを2回行ったことに注意してください。そのため、最初に一意のdoc / skuペアを分類してから、カウントアップする必要があります。
出力を少し変えたい場合(つまり、サンプルとまったく同じように)、それらを$projectすることができます。