おそらく最も簡単な解決策は、2 つの個別の集計操作でこれを行い、アプリケーションで結果を結合することです。
別の方法として、Map Reduce 操作を使用してこれを行うこともできます。
次の map および reduce 関数は、探している結果を提供するはずです。
var map = function() {
var totalHits = this.hitsPerOneSecond.map(function(a,b){return a+b;});
var totalHitsCount = this.hitsPerOneSecond.length;
var avgHit = totalHits / totalHitsCount;
var minHit = Math.min.apply(Math, this.hitsPerOneSecond);
var maxHit = Math.max.apply(Math, this.hitsPerOneSecond);
var totalResponses = pathStats_xxx_api_get_response.map(function(a,b){return a+b;});
var totalResponsesCount = this.pathStats_xxx_api_get_response.length;
var avgResponse = totalResponses / totalResponsesCount;
var minResponse = Math.min.apply(Math, this.pathStats_xxx_api_get_response);
var maxResponse = Math.max.apply(Math, this.pathStats_xxx_api_get_response);
emit(this.startTimeStr, {
"totalHits": totalHits,
"totalHitsCount": totalHitsCount,
"avgHit": avgHit,
"minHit": minHit,
"maxHit": maxHit,
"totalResponses": totalResponses,
"totalResponsesCount": totalResponsesCount,
"avgResponse": avgResponse,
"maxResponse": maxResponse,
"minResponse": minResponse
})
}
var reduce = function(key, values) {
var output = {
"totalHits": 0,
"totalHitsCount": 0,
"avgHit": 0,
"minHit": null,
"maxHit": null,
"totalResponses": 0,
"totalResponsesCount": 0,
"avgResponse": 0,
"maxResponse": null,
"minResponse": null
};
values.forEach(function(v) {
output.totalHits += v.totalHits;
output.totalHitsCount += v.totalHitsCount;
output.avgHit = output.totalHits / output.totalHitsCount;
if (output.minHit == null) {
output.minHit = v.minHit;
} else {
if (v.minHit < output.minHit) {
output.minHit = v.minHit
}
}
if (output.maxHit == null) {
output.maxHit = v.maxHit;
} else {
if (v.maxHit > output.maxHit) {
output.maxHit = v.maxHit
}
}
output.totalResponses += v.totalResponses;
output.totalResponsesCount += v.totalResponsesCount;
output.avgResponse = output.totalResponses / output.totalResponsesCount;
if (output.minResponse == null) {
output.minResponse = v.minResponse;
} else {
if (v.minResponse < output.minResponse) {
output.minResponse = v.minResponse
}
}
if (output.maxResponse == null) {
output.maxResponse = v.maxResponse;
} else {
if (v.maxResponse > output.maxResponse) {
output.maxResponse = v.maxResponse
}
}
});
return output;
}
> db.newStats.mapReduce(map, reduce, {out:{inline:1}})
{
"results" : [
{
"_id" : "07-04-2012:10AM",
"value" : {
"totalHits" : 54,
"totalHitsCount" : 20,
"avgHit" : 2.7,
"minHit" : 1,
"maxHit" : 5,
"totalResponses" : 7.523893102462698,
"totalResponsesCount" : 6,
"avgResponse" : 1.253982183743783,
"maxResponse" : 1.4853219936411421,
"minResponse" : 1.0602539963494662
}
}
],
"timeMillis" : 0,
"counts" : {
"input" : 2,
"emit" : 2,
"reduce" : 1,
"output" : 1
},
"ok" : 1,
}
>
Map Reduce に慣れていない場合は、
http ://www.mongodb.org/display/DOCS/MapReduce のドキュメントを参照してください。
さらに、MongoDB クックブック (
http://cookbook.mongodb.org/ ) には、Map Reduce の優れた例がいくつかあります。
クックブックの記事「バージョニングされたドキュメントで最大値と最小値を見つける」 http://cookbook.mongodb.org/patterns/finding_max_and_min/の「Extras」セクションには、Map Reduce 操作の段階的なウォークスルーが含まれており、その方法が説明されています。関数が実行されます。
うまくいけば、これはあなたが望む結果を達成するのに役立ちます. 単一の集計操作でこれを行う方法を見つけられる場合は、ソリューションを共有して、コミュニティがあなたの経験から利益を得られるようにしてください。ありがとう。
あなたのコメントに応えて、Map Reduce に関するいくつかのメモを以下に示します。
MapReduce はサーバー上で JavaScript を実行します。その結果、他の操作のパフォーマンスが低下することがあります。Map Reduce は、サーバーのトラフィックがピークに達していないときに実行される可能性がある、たまに発生する操作に適しています。大規模なコレクションからオンザフライの統計に Map Reduce を使用するのは最適ではないことに気付くかもしれません。
一方、集約フレームワークはネイティブ コードに依存し、サーバー サイド JavaScript を実行しないため、Map Reduce より高速です。
可能であれば、クエリを実行できる各ドキュメントにフィールドを追加することをお勧めします。これにより、各挿入または更新に少し余分なオーバーヘッドが追加されますが、Map Reduce 操作を回避できれば、結果ははるかに迅速に返されます。残念ながら、これは最大値と最小値、および平均では困難です。
Map Reduce 操作が唯一のオプションである場合、サーバーへの影響を軽減するために実行できることがいくつかあります。まず、SlaveOk を使用してセカンダリで Map Reduce を実行できます。ただし、データをセカンダリに書き込むことはできないため、出力はインラインで返す必要があり、したがって 16MB に制限されます。一部のユーザーは、レプリカ セットからセカンダリを取得し、スタンドアロンの mongod プロセスとして再起動し、map-reduce 操作を実行し、必要な場所に出力コレクションをコピーし、セカンダリをレプリカ セットに再結合します。
最後に考慮すべきことは、インクリメンタル Map Reduce です:
http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-IncrementalMapreduce最後の map reduce を実行し、reduce 出力オプションを指定して map reduce 操作を実行します。
上記が、統計を計算する最良の方法に関する考察の材料になることを願っています。ドキュメントに必要な情報を含めることが望ましいですが、それが不可能な場合は、Aggregation Framework を使用する方が Map Reduce よりも効率的です。
2番目のコメントへの応答として、集約フレームワークとpymongoに関するメモを次に示します。
集計フレームワークは、データベース オブジェクトのコマンド メソッドを使用して pymongo で使用できます。
コマンド メソッドに関するドキュメントは、http:
//api.mongodb.org/python/current/api/pymongo/database.html#pymongo.database.Database.commandにあります。
集計操作を実行するには、2 つのキーを使用してドキュメントをコマンド メソッドに渡します。「集約」と「パイプライン」。「aggregate」の値は操作が実行されるコレクションの名前であり、「pipeline」の値は実行される集計操作の配列になります。パイプラインについては、「集約フレームワーク」のドキュメントで説明されています:
http://www.mongodb.org/display/DOCS/Aggregation+Framework#AggregationFramework-Pipelines
pymongo で $unwind 操作を実行する方法の例を次に示します。
In [1]: import pymongo
In [2]: conn = pymongo.Connection()
In [3]: db = conn.test
In [4]: result = db.command({"aggregate":"newStats", "pipeline":
[{"$unwind": "$hitsPerOneSecond"},
{"$group": {"_id":"$startTimeStr",
"totalHits": {"$sum":
"$hitsPerOneSecond"},
"totalHitsCount": {"$sum": 1},
"avgHit": {"$avg": "$hitsPerOneSecond"},
"minHit": {"$min":"$hitsPerOneSecond"},
"maxHit":{"$max": "$hitsPerOneSecond"}}}]})
In [5]: result
Out[5]:
{u'ok': 1.0,
u'result': [{u'_id': u'07-04-2012:10AM',
u'avgHit': 2.7,
u'maxHit': 5.0,
u'minHit': 1.0,
u'totalHits': 54.0,
u'totalHitsCount': 20}]}