2011-01-27 11 views
7

Sto cercando di calcolare un valore medio da una raccolta utilizzando il driver MongoDB Java, in questo modo:come calcolare la media con MongoDB e NumberLong

DBObject condition = 
    new BasicDBObject("pluginIdentifier", plugin.getIdentifier()); 

DBObject initial = new BasicDBObject(); 

initial.put("count", 0); 
initial.put("totalDuration", 0); 
String reduce = "function(duration, out) { out.count++; 
    out.totalDuration+=duration.floatApprox; }"; 
String finalize = "function(out) { out.avg = out.totalDuration.floatApprox/
    out.count; }"; 

DBObject avg = durationEntries.group(
    new BasicDBObject("pluginIdentifier", true), 
    condition, initial, reduce, finalize); 

System.out.println(avg); 

"durata" è una NumberLong (in Java, è un lungo, probabilmente il driver java lo converte). Ho capito dopo alcune ricerche che, al fine di estrarre il numero, utilizzando .floatApprox era un modo per andare, e questo funziona anche nella console MongoDB:

> db.DurationEntries.findOne().duration.floatApprox 
5 

Tuttavia, l'esecuzione del codice Java di cui sopra non lo farà calcolare una media, ma restituisce questo invece

[{"pluginIdentifier":"dummy", "count":7.0, "totalDuration":NaN, "avg":NaN}] 

ho provato diverse varianti, con e senza .floatApprox, ma sono stati solo in grado di ottenere delle concatenazioni di stringhe strani finora.

La mia domanda è: cosa sto facendo male/come dovrei andare a calcolare la media di una colonna NumberLong?

+1

È la chiave in questo caso che si desidera che Mongo esegua la media anziché tirare i dati della colonna in Java? Sei sicuro di non aver accidentalmente dati non numerici tra le tue durate? –

+0

Sì, questo è il mio intento, fare il calcolo nel database piuttosto che nella memoria (perché quando avrò molte voci, penso che la mia JVM finirà la memoria). E sì, ci possono essere dati non numerici nella misura in cui potrebbero esserci voci "nulle" nella media - Verificherò che –

+2

Se tutto ciò che vuoi è una media, allora non dovrebbe richiedere un'enorme quantità di memoria, penso, perché tu non è necessario conservare ciascuna voce in memoria, solo una somma totale e un conteggio. Non sono abbastanza familiare con Mongo per dire esattamente come, ma se riesci a mettere in batch il set di risultati dalla query, potresti elaborare un sottoinsieme alla volta. –

risposta

6

Se si riscontrano problemi con la mappa/riduzione, è probabile che si debba scendere nella console di mongodb, elaborarla e quindi tradurla nel driver.

Prendiamo, per esempio, i seguenti documenti:

db.tasks.find() 
{ "_id" : ObjectId("4dd51c0a3f42cc01ab0e6506"), "duration" : 10, "name" : "StartProcess", "date" : "20110501" } 
{ "_id" : ObjectId("4dd51c0e3f42cc01ab0e6507"), "duration" : 11, "name" : "StartProcess", "date" : "20110502" } 
{ "_id" : ObjectId("4dd51c113f42cc01ab0e6508"), "duration" : 12, "name" : "StartProcess", "date" : "20110503" } 

Si potrebbe scrivere il MapReduce per calcolare la durata media dei StartProcess come segue:

m = function(){ 
    emit(this.name , { totalDuration : this.duration , num : 1 }); 
}; 

r = function (name, values){ 
    var n = {totalDuration : 0, num : 0}; 
    for (var i=0; i<values.length; i++){ 
    n.totalDuration += values[i].totalDuration; 
    n.num += values[i].num; 
    } 
    return n; 
}; 

f = function(who, res){ 
    res.avg = res.totalDuration/res.num; 
    return res; 
}; 

Quindi, supponendo che si sta usando MongoDB 1.7 o successiva:

db.tasks.mapReduce(m, r, { finalize : f, out : {inline : 1} }); 

Vi darebbe la seguente risposta:

"results" : [ 
    { 
    "_id" : "StartProcess", 
     "value" : { 
     "totalDuration" : 33, 
     "num" : 3, 
     "avg" : 11 
     } 
    } 
] 

Se ciò non aiuta, è possibile pubblicare la funzione della mappa e la struttura del documento.

+0

grazie! finalmente ho avuto il tempo di tornare a quel codice e provarlo! –

Problemi correlati