2012-10-25 4 views
0

J'ai une base de données MongoDB avec des documents qui contiennent un champ de filtre. Les documents ressemblent à ceci:mongodb compte l'objet égal

{ 
    "_id": ObjectId("503208f5b5db0135387d9249"), 
    "name": "name1", 
    "filter": "facebook" 
}   { 
    "_id": ObjectId("503208f5b5db0135387d9249"), 
    "name": "name2", 
    "filter": "twitter" 
} 
{ 
    "_id": ObjectId("503208f5b5db0135387d9249"), 
    "name": "name3", 
    "filter": "twitter" 
}  

Et je veux compter par type. Cet exemple devrait ressembler à ceci:

facebook => 1, twitter => 2 

Avec le code rubis, le comptage est très lent.

Le filtre est une chaîne qui peut être n'importe quoi. E.g:

{facebook : 1203, twitter : 201, wherever : 200, othertype : 400} 

J'utilise mongo mapper (driver ruby).

============== EDIT 3

Enfin, il travaille. voici le code pour le pilote ruby:

def map 
    'function(){ 
     emit(this.plataforma, 1); 
    }' 
end 
def reduce 
    ' 
    function(prev, current) { 
     var n = 0; 
     current.forEach(function(v){ 
      n+=v; 
     }); 
     return n; 
    }' 
end 

def build 
    Mention.collection.map_reduce(map, reduce, :query => {}) 
end 
+0

quelle version de mongo utilisez-vous? – soulcheck

+0

J'utilise mongo 1.6.3. –

+0

Vous pouvez utiliser le framework d'agrégation avec '$ match' et' $ sum' ou un MR ou une pré-agrégation ici – Sammaye

Répondre

0

Ceci est similaire à:

Où par cela montre comment vous pourriez exécuter un MR comme ça:

var map = function(){ 
    emit(this.filter,1); 
} 

var reduce = function(previous,current){ 
    var count = 0; 

    for (index in current) { 
     count += current[index]; 
    } 

    return count; 
} 

qui sera ensuite sortie, à une autre collection (ou en ligne si vous voulez) les documents du format:

{ 
    _id: facebook, 
    value: 1, 
    _id: twitter, 
    value: 2 
} 

Ce qui est écrit dans le langage de la console MongoDBs qui est JS. MongoDB a un analyseur JavaScript intégré (spidermonkey) qui peut analyser les fonctions MR. De Ruby vous intégrerait vos fonctions comme ceci:

map = BSON::Code.new "function() { emit(this.filter, 1); }" 

Et puis exécutez le MR comme ceci:

col.map_reduce(map, reduce, 
     { 
      :out => 'summary_collection', 
      :raw => true 
     } 
    ) 

Et puis vous simplement interroger la collecte sommaire pour ce filtre en fournissant la représentation de chaîne comme _id ou faites un simple find() pour obtenir tous les résultats.

+0

quel est ce code? Je ne comprends pas. cette requête se déroulera à plus de 200.000 documents, ce qui serait lent? –

+0

@RodrigoDias Ce code ne fonctionnera pas en temps réel à la différence d'une requête SQL, le framework d'agrégation pourrait le faire mais pas de carte réduite. Vous auriez besoin de sortir ceci à une collection de résumé que vous stockeriez comme cache pour lire de mettre à jour le MR (peut-être incrémentalement) une fois toutes les 5 minutes ou ainsi. Bien sûr, vous pouvez toujours effectuer une pré-agrégation qui résoudra le problème en temps réel, mais cela signifie que vous devez modifier votre schéma pour contenir un champ de somme pour chaque filtre. – Sammaye

+0

@RodrigoDias Ce code est JavaScript. C'est le code sur lequel la console mongo s'exécute. Dans Ruby, vous enveloppez vos blocs de code pour chacune des fonctions dans 'BSON :: Code.new', puis appelez le MR actuel comme suit:' col.map_reduce (map, reduce, { : out => {: inline = > true}, : raw => true } ) ' – Sammaye

0

J'ai posé la question, mais sans aucune réponse. Donc, si vous avez seulement deux possibilités de filtre: Facebook et Twitter, vous pouvez le faire avec deux requêtes:

db.yourcollection.count({ 
    'filter' : 'facebook' 
}) 

cela vous retourner 1

db.yourcollection.count({ 
    'filter' : 'twitter' 
}) 

cela vous revenir 2

+0

le filtre peut être n'importe quoi, c'est une chaîne –

+0

oui. il pourrait retourner quelque chose comme: {facebook: 1203, twitter: 201, où: 200, othertype: 400} –

0

Si vous dites que le filtre peut être quelque chose - que vous devriez utiliser mapreduce. http://www.mongodb.org/display/DOCS/MapReduce

Voici un code de travail, mettez-le dans la console mongo, et que de le traduire en ruby.

d'abord créer la fonction carte:

var map = function(){ 
    emit(this.filter, 1); 
} 

réduire la fonction:

var reduce = function(key, value){ 
    var n = 0; 
    value.forEach(function(v){ 
    n+=v; 
    }); 
    return n; 
}; 

parce que vous utilisez ancienne version de mongo, vous devez faire ce qui suit:

res = db.runCommand({ 
    'mapreduce' : 'YOURCOLLECTION', 
    'map' : map, 
    'reduce' : reduce, 
    'out' : 'tmp' 
}); 
db.tmp.find(); 

cette vous renverra

{'_id' : 'facebook', 'value' : 1}, 
{'_id' : 'twitter', 'value' : 2} 
Questions connexes