2011-01-27 138 views
7

Estoy tratando de calcular un valor promedio de una colección utilizando el controlador mongodb java, así:cómo calcular la media con mongodb y 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); 

"duración" es una NumberLong (en Java, es un Long, probablemente el controlador java lo convierta). me di cuenta de que después de buscar con el fin de extraer el número, usando .floatApprox era un camino a seguir, y esto también funciona en la consola MongoDB:

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

Sin embargo, la ejecución del código de Java anteriores no se calcular un promedio, pero vuelve este lugar

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

he intentado varias variaciones, con y sin .floatApprox, pero sólo han sido capaces de obtener algunas concatenaciones de cadenas extrañas hasta ahora.

Mi pregunta es: ¿qué estoy haciendo mal/cómo debo proceder para calcular el promedio de una columna NumberLong?

+1

¿Está la clave aquí que desea hacer que mongo haga el promedio en lugar de tirar los datos de la columna a Java? ¿Estás seguro de que accidentalmente no tienes datos no numéricos entre tus duraciones? –

+0

Sí, esa es mi intención, hacer el cálculo en la base de datos en lugar de en la memoria (porque cuando tenga muchas entradas, creo que mi JVM se quedará sin memoria). Y sí, puede haber datos no numéricos en la medida en que podría haber entradas promedio "nulas" - Comprobaré que –

+2

Si todo lo que quiero es un promedio, entonces no debería tomar grandes cantidades de memoria, creo, porque no es necesario mantener cada entrada en la memoria, solo una suma total y un conteo. No estoy lo suficientemente familiarizado con Mongo para decir exactamente cómo, pero si puede agrupar el conjunto de resultados de la consulta, entonces podría procesar un subconjunto a la vez. –

Respuesta

6

Si tiene problemas con el mapa/reducir, probablemente debería bajar a la consola de mongodb, resolverlo y luego traducirlo a su controlador.

Tomemos, por ejemplo, los siguientes documentos:

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" } 

usted escribiría la MapReduce para el cálculo de la duración media de StartProcess de la siguiente manera:

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; 
}; 

Entonces, asumiendo que usted está usando MongoDB 1.7 o superior:

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

Le daría la siguiente respuesta:

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

Si esto no ayuda, puede publicar la función del mapa y la estructura del documento.

+0

gracias! ¡finalmente tuvo tiempo de volver a ese código y probarlo! –

Cuestiones relacionadas