Möglicherweise die einfachste Lösung ist, dies mit zwei separaten Aggregationsoperationen zu tun und die Ergebnisse in Ihrer Anwendung zu kombinieren.
Alternativ Sie mit einer Karte verkleinern Betrieb tun könnte: suchen
Die folgende Karte und reduzieren Funktionen sollten Sie die Ergebnisse liefern:
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,
}
>
Wenn Sie mit Map Reduce nicht vertraut sind, die Dokumentation kann hier gefunden werden: http://www.mongodb.org/display/DOCS/MapReduce
Zusätzlich gibt einige gute Beispiele Map Reduce im MongoDB-Kochbuch sind: http://cookbook.mongodb.org/
Der Abschnitt "Extras" des Kochbuch-Artikels "Suchen von Max- und Min-Werten mit versionierten Dokumenten" http://cookbook.mongodb.org/patterns/finding_max_and_min/ enthält eine gute Schritt-für-Schritt-Anleitung einer Map Reduce-Operation, die erklärt, wie die Funktionen ausgeführt werden.
Hoffentlich wird dies Ihnen helfen, Ihre gewünschten Ergebnisse zu erreichen. Wenn Sie einen Weg finden, dies mit einer einzigen Aggregationsoperation zu tun, teilen Sie Ihre Lösung bitte mit, damit die Community von Ihrer Erfahrung profitieren kann. Vielen Dank.
Hier sind ein paar Hinweise auf Map Reduce, in Reaktion auf Ihren Kommentar:
MapReduce führt JavaScript auf dem Server. Dies kann dazu führen, dass die Leistung für andere Vorgänge beeinträchtigt wird. Map Reduce eignet sich für einmalige Operationen, die zu einem Zeitpunkt ausgeführt werden können, an dem der Server nicht den höchsten Datenverkehr erreicht. Sie können feststellen, dass die Verwendung von Map Reduce für direkte Statistiken aus einer großen Sammlung nicht optimal ist.
Das Aggregationsframework basiert dagegen auf nativem Code und führt serverseitiges JavaScript nicht aus, wodurch es schneller als Map Reduce ist.
Wenn möglich, empfiehlt es sich, jedem Dokument, das abgefragt werden kann, Felder hinzuzufügen.Dies fügt jedem Einfügevorgang oder Update ein wenig zusätzlichen Aufwand hinzu, aber die Ergebnisse werden viel schneller zurückgegeben, wenn ein Map Reduce-Vorgang vermieden werden kann. Leider ist dies schwierig mit Maximum und Minimum Werten und Durchschnittswerten.
Wenn eine Map Reduce-Operation die einzige Option ist, können einige Maßnahmen ergriffen werden, um die Auswirkungen auf den Server zu verringern. Erstens ist es möglich, eine Map Reduce auf einem Secondary mit SlaveOk auszuführen. Da Daten jedoch nicht auf eine Sekundärseite geschrieben werden können, muss die Ausgabe inline zurückgegeben werden und ist daher auf 16 MB beschränkt. Einige Benutzer nehmen eine sekundäre Kopie aus der Replikatgruppe, starten sie als eigenständigen Mongod-Prozess neu, führen die Map-Reduce-Operation für sie aus, kopieren die Ausgabesammlung dorthin, wo sie hingehört, und verbinden sie erneut mit der Replica-Gruppe.
Eine letzte Sache zu prüfen, ist reduzieren inkrementelle Karte: http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-IncrementalMapreduce Sie eine Anfrage an die Karte Befehl reduzieren passieren kann, dass nur Dokumente gefunden werden, die seit der letzten Karte reduzieren geändert wurden, und führen Sie den Betrieb der Karte reduzieren mit der Reduzieren Sie die Ausgabeoption.
Hoffentlich gibt Ihnen das Obige einen Denkanstoß, wie Sie Ihre Statistiken am besten berechnen können. Es empfiehlt sich, die gewünschten Informationen in die Dokumente einzubeziehen. Wenn dies jedoch nicht möglich ist, ist die Verwendung des Aggregationsframeworks effizienter als Map Reduce.
Hier ist ein Hinweis auf die Aggregation Framework und pymongo, in Reaktion auf den zweiten Kommentar:
Der Aggregations Rahmen in pymongo mit dem Befehl Methode des Datenbankobjekts verwendet werden kann.
Die Dokumentation auf der Kommando Methode finden Sie hier: http://api.mongodb.org/python/current/api/pymongo/database.html#pymongo.database.Database.command
eine Aggregationsoperation auszuführen, um ein Dokument zu dem Befehl Methode mit zwei Schlüsseln übergeben; "Aggregat" und "Pipeline". Der Wert von "aggregate" ist der Name der Auflistung, für die die Operation ausgeführt wird, und der Wert von "pipeline" ist ein Array der auszuführenden Aggregationsoperationen. Pipelines werden in der "Aggregation Framework" Dokumentation erläutern: http://www.mongodb.org/display/DOCS/Aggregation+Framework#AggregationFramework-Pipelines
Hier ist ein Beispiel dafür, wie Sie den $ Abroller Betrieb in pymongo durchführen können:
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}]}
Dies ist ein fantastisches Beispiel. Vielen Dank dafür. Auch Links zu Dokumenten werden sehr geschätzt.In Bezug auf die Aggregation - das Ziel war es, agile Statistiken über ein Dashboard und Pymongo zu aggregieren, aber ich denke, dass sowohl das Map/Reduce als auch die Aggregation mit $ group immer noch relativ langsam ist. Ich suche 60.080 (1 Wochen wert) Datensekunden von 10 Sekunden aus einem Python-Skript, das es bereits in die 10-Sekunden-Statistiken aus Apache-Log-Dateien zusammenfasst. – sam0673
Gerne helfen! Ich habe meine Antwort oben mit einigen Anmerkungen zu Map Reduce aktualisiert. – Marc
Eine wichtige Frage aber ... wie würden Sie db.newStats.aggregate ({$ abwickeln: "$ hitsPerOneSecond"} usw. über pymongo ausführen, da ich keine Dokumentation dafür finden kann? – sam0673