2010-06-16 6 views
0

Donc, en utilisant la bibliothèque MongoDB régulière Ruby je la requête suivante pour trouver filesize moyenne sur un ensemble de 5001 documents:Dans MongoDB, comment puis-je répliquer cette requête simple en utilisant map/reduce dans ruby?

avg = 0 
    total = collection.count() 
    Rails.logger.info "#{total} asset creation stats in the system" 
    collection.find().each {|row| avg += (row["filesize"] * (1/total.to_f)) if row["filesize"]} 

Son assez simple, donc je suis en train de faire la même chose en utilisant map/reduce comme un exercice d'apprentissage. Voici ce que j'ai trouvé:

map = 'function(){emit("filesizes", {size: this.filesize, num: 1});}' 
    reduce = 'function(k, vals){ 
      var result = {size: 0, num: 0}; 
      for(var x in vals) { 
       var new_total = result.num + vals[x].num; 
       result.num = new_total 
       result.size = result.size + (vals[x].size * (vals[x].num/new_total)); 
      } 
      return result; 
    }' 
    @results = collection.map_reduce(map, reduce) 

Cependant les deux requêtes reviennent avec deux résultats différents!

Qu'est-ce que je fais mal?

Répondre

1

Vous pondérez les résultats en effectuant la division dans chaque fonction de réduction.

Supposons que vous ayez [{size : 5, num : 1}, {size : 5, num : 1}, {size : 5, num : 1}]. Votre réduction calculerait:

result.size = 0 + (5*(1/1)) = 5 
result.size = 5 + (5*(1/2)) = 7.25 
result.size = 7.25 + (5*(1/3)) = 8.9 

Comme vous pouvez le voir, cela pondère les résultats vers les premiers éléments.

Heureusement, il existe une solution simple. Ajoutez simplement une fonction de finalisation, qui sera exécutée une fois que l'étape de réduction est terminée.