現在の処理はmapReduceです
サーバーでこれを実行し、上位の結果を並べ替えて上位100を保持する必要がある場合は、次のようにmapReduceを使用できます。
db.test.mapReduce(
function() {
var input = [0.1,0.3,0.4];
var value = Array.sum(this.vals.map(function(el,idx) {
return Math.abs( el - input[idx] )
}));
emit(null,{ "output": [{ "_id": this._id, "value": value }]});
},
function(key,values) {
var output = [];
values.forEach(function(value) {
value.output.forEach(function(item) {
output.push(item);
});
});
output.sort(function(a,b) {
return a.value < b.value;
});
return { "output": output.slice(0,100) };
},
{ "out": { "inline": 1 } }
)
したがって、マッパー関数はすべて同じキーで計算と出力を行うため、すべての結果がレデューサーに送信されます。最終出力は単一の出力ドキュメントの配列に含まれるため、すべての結果が同じキー値で出力されることと、mapReduceが正しく機能できるように、各出力自体が配列であることが重要です。
並べ替えと縮小は、リリースされた各ドキュメントが検査されるときにレデューサー自体で実行されます。要素は単一の一時配列に入れられ、並べ替えられ、上位の結果が返されます。
これは重要であり、最初は単一の要素であっても、エミッターがこれを配列として生成する理由です。 MapReduceは結果を「チャンク」で処理することで機能するため、発行されたすべてのドキュメントが同じキーを持っていても、すべてが一度に処理されるわけではありません。むしろ、レデューサーはその結果を発行された結果のキューに戻し、その特定のキーのドキュメントが1つだけ残るまで削減します。
ここでは、リストを簡潔にするために「スライス」出力を10に制限し、この10000サンプルで呼び出された100回の削減サイクルを確認できるように統計を含めています。
{
"results" : [
{
"_id" : null,
"value" : {
"output" : [
{
"_id" : ObjectId("56558d93138303848b496cd4"),
"value" : 2.2
},
{
"_id" : ObjectId("56558d96138303848b49906e"),
"value" : 2.2
},
{
"_id" : ObjectId("56558d93138303848b496d9a"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d93138303848b496ef2"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d94138303848b497861"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d94138303848b497b58"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d94138303848b497ba5"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d94138303848b497c43"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d95138303848b49842b"),
"value" : 2.1
},
{
"_id" : ObjectId("56558d96138303848b498db4"),
"value" : 2.1
}
]
}
}
],
"timeMillis" : 1758,
"counts" : {
"input" : 10000,
"emit" : 10000,
"reduce" : 100,
"output" : 1
},
"ok" : 1
}
したがって、これは特定のmapReduce形式の単一のドキュメント出力であり、「値」には、並べ替えられ制限された結果の配列である要素が含まれます。
将来の処理は集約的です
執筆時点で、MongoDBの現在の最新の安定したリリースは3.0であり、これには操作を可能にする機能がありません。しかし、次の3.2リリースでは、これを可能にする新しい演算子が導入されています。
db.test.aggregate([
{ "$unwind": { "path": "$vals", "includeArrayIndex": "index" }},
{ "$group": {
"_id": "$_id",
"result": {
"$sum": {
"$abs": {
"$subtract": [
"$vals",
{ "$arrayElemAt": [ { "$literal": [0.1,0.3,0.4] }, "$index" ] }
]
}
}
}
}},
{ "$sort": { "result": -1 } },
{ "$limit": 100 }
])
また、簡潔にするために同じ10個の結果に制限すると、次のような出力が得られます。
{ "_id" : ObjectId("56558d96138303848b49906e"), "result" : 2.2 }
{ "_id" : ObjectId("56558d93138303848b496cd4"), "result" : 2.2 }
{ "_id" : ObjectId("56558d96138303848b498e31"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497c43"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497861"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499037"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b498db4"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496ef2"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496d9a"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499182"), "result" : 2.1 }
これは主に、 $ unwind
によって可能になります。
配列インデックスを含む結果にフィールドを投影するように変更されているほか、 $ arrayElemAt
これは、指定されたインデックスから配列要素を特異値として抽出できる新しい演算子です。
これにより、各要素に計算を適用するために、入力配列からのインデックス位置による値の「ルックアップ」が可能になります。入力配列は、既存の $literal
>
演算子なので$arrayElemAt
は文句を言わず、配列として認識し(他の配列関数は直接入力に問題がないため、現時点では小さなバグのようです)、によって生成された「インデックス」フィールドを使用して適切な一致するインデックス値を取得します。 $ unwind
比較のために。
計算は $ extract
によって行われます。
そしてもちろん、 $ abs
の別の新しい演算子
あなたの機能を満たすために。また、最初に配列を巻き戻す必要があったため、これはすべて $ group
ドキュメントごとにすべてのアレイメンバーを蓄積し、<を介してエントリの追加を適用するステージcode> $ sum
アキュムレータ。
最後に、すべての結果ドキュメントは $ sort
で処理されます。
次に、 $ limit
上位の結果を返すためだけに適用されます。
概要
MongoDBの集約フレームワークで利用できるようになろうとしている新しい機能があっても、どちらのアプローチが実際に結果に対してより効率的であるかは議論の余地があります。これは主に、 $ unwind
の必要性がまだあるためです。 配列コンテンツ。処理されるパイプラインの配列メンバーごとに各ドキュメントのコピーを効果的に生成します。これにより、通常、オーバーヘッドが発生します。
したがって、mapReduceは新しいリリースまでこれを行う唯一の現在の方法ですが、処理されるデータの量によっては、実際には集約ステートメントよりもパフォーマンスが優れている可能性があります。集約フレームワークは、翻訳されたJavaScriptではなくネイティブのコード化された演算子で機能します。操作。
すべてのものと同様に、どのケースが目的に適しているか、どのケースが期待される処理に最高のパフォーマンスをもたらすかを確認するために、常にテストを行うことをお勧めします。
サンプル
もちろん、質問で提供されたサンプルドキュメントの期待される結果は 0.9
です。 適用された数学によって。ただし、テストの目的で、少なくともmapReduceコードが正常に機能していることを確認したいサンプルデータを生成するために使用した短いリストを次に示します。
var bulk = db.test.initializeUnorderedBulkOp();
var x = 10000;
while ( x-- ) {
var vals = [0,0,0];
vals = vals.map(function(val) {
return Math.round((Math.random()*10),1)/10;
});
bulk.insert({ "vals": vals });
if ( x % 1000 == 0) {
bulk.execute();
bulk = db.test.initializeUnorderedBulkOp();
}
}
配列は完全にランダムな小数点以下1桁の値であるため、サンプル出力として示したリストの結果には多くの分布がありません。