2011-01-27 2 views
7

Я пытаюсь вычислить среднее значение из коллекции, используя драйвер MongoDB Java, например:как вычислить среднее с MongoDB и 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); 

«длительность» является NumberLong (в Java, это является Long, вероятно, драйвер java преобразует его). я понял, после некоторых поисков, что для того, чтобы извлечь номер, используя .floatApprox был один путь, и это также работает в MongoDB консоли:

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

Однако запуск приведенный выше код Java не будет вычислить среднее значение, но возвращает это вместо

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

Я попробовал несколько вариантов, и без .floatApprox, но только не в состоянии получить некоторые странные строки конкатенации до сих пор.

Мой вопрос: что я делаю неправильно/как я должен вычислить среднее значение одного столбца NumberLong?

+1

Является ли ключевым здесь, что вы хотите сделать mongo, сделать усреднение, а не вытягивать данные столбца в Java? Уверены ли вы, что у вас не случайно есть нечисловые данные среди ваших длительностей? –

+0

Да, это мое намерение делать вычисления в базе данных, а не в памяти (потому что, когда у меня будет много записей, я думаю, что у моей JVM не хватит памяти). И да, действительно, могут быть нечисловые данные, поскольку могут быть «нулевые» средние записи - я проверю, что –

+2

Если все, что вы хотите, является средним, то, я думаю, это не должно занимать огромное количество памяти, потому что вы не нужно хранить каждую запись в памяти, всего сумму и счет. Я недостаточно хорошо знаком с Mongo, чтобы точно сказать, как это сделать, но если вы можете выполнить пакетный результат из запроса, вы можете обрабатывать подмножество за раз. –

ответ

6

Если у вас возникли проблемы с картой/уменьшением, вы должны, вероятно, спуститься в консоль mongodb, проработать там, а затем перевести это в свой драйвер.

Возьмем, к примеру, следующие документы:

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

Вы должны написать MapReduce для расчета средней продолжительности StartProcess следующим образом:

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

Тогда, предполагая, что вы используете MongoDB 1,7 или выше:

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

Сообщите мне об этом:

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

Если это не поможет, можете ли вы опубликовать свою функцию карты и структуру документа.

+0

спасибо! наконец, было время вернуться к этому коду и попробовать его! –