2011-01-27 11 views
7

Próbuję obliczyć średnią wartość z kolekcji za pomocą sterownika MongoDB java, jak poniżej:jak obliczyć średnią z MongoDB i 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); 

„czas” jest NumberLong (w java go, jest długi, prawdopodobnie sterownik java konwertuje go). zorientowali się, że po poszukiwaniach w celu wydobycia numer, używając .floatApprox była jedna droga, i to działa również w konsoli MongoDB:

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

jednak uruchomić powyższy kod Java nie będzie obliczyć średnią, ale wraca to zamiast

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

próbowałem kilka odmian, z lub bez .floatApprox, ale tylko w stanie uzyskać kilka dziwnych powiązań, ciąg aż do teraz.

Moje pytanie brzmi: co robię źle/w jaki sposób powinienem obliczyć średnią jednej kolumny NumberLong?

+1

Czy kluczem jest tutaj, aby mongo robić uśrednianie, zamiast ciągnąć dane kolumn do Java? Czy na pewno przypadkowo nie masz żadnych danych liczbowych w okresie ważności? –

+0

Tak, to jest mój zamiar, aby wykonać obliczenia w bazie danych, a nie w pamięci (ponieważ, gdy będę miał wiele wpisów, myślę, że moja JVM zabraknie pamięci). I rzeczywiście, mogą istnieć dane nieliczbowe, o ile mogą być "puste" średnie wpisy - sprawdzę, czy to jest –

+2

Jeśli wszystko, co chcesz, jest średnią, to nie powinno to zabrać dużej ilości pamięci, jak sądzę, ponieważ nie trzeba przechowywać każdego wpisu w pamięci, tylko sumę całkowitą i liczbę. Nie jestem wystarczająco zaznajomiony z Mongo, aby powiedzieć dokładnie, jak to zrobić, ale jeśli możesz wsadowy zestaw wyników z zapytania, możesz przetworzyć podzbiór na raz. –

Odpowiedz

6

Jeśli masz problemy z mapą/zmniejszeniem, prawdopodobnie powinieneś zejść do konsoli mongodb, przetrenuj ją i przetłumaczyć na sterownik.

Weźmy, na przykład, następujące dokumenty:

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

byłoby piszesz mapreduce obliczyć średni czas trwania StartProcess następująco:

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

Wtedy, przy założeniu, że używasz MongoDB 1.7 lub nowszy:

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

Odpowiedź brzmiałaby następująco:

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

Jeśli to nie pomoże, możesz opublikować funkcję mapy i strukturę dokumentu.

+0

dzięki! wreszcie miał czas, aby wrócić do tego kodu i wypróbować go! –

Powiązane problemy