2013-04-23 14 views
10

Ho un set di documenti in Mongo. Di ':Mongo: conta il numero di occorrenze di parole in un insieme di documenti

[ 
    { summary:"This is good" }, 
    { summary:"This is bad" }, 
    { summary:"Something that is neither good nor bad" } 
] 

mi piacerebbe contare il numero di occorrenze di ogni parola (case insensitive), quindi ordinare in ordine decrescente. Il risultato dovrebbe essere qualcosa del tipo:

[ 
    "is": 3, 
    "bad": 2, 
    "good": 2, 
    "this": 2, 
    "neither": 1, 
    "nor": 1, 
    "something": 1, 
    "that": 1 
] 

Qualche idea su come procedere? La struttura di aggregazione sarebbe preferibile, poiché già la comprendo in qualche modo :)

risposta

18

MapReduce potrebbe essere una buona soluzione che può elaborare i documenti sul server senza fare manipolazioni sul client (in quanto non esiste una funzionalità da dividere una stringa sul server DB (open issue).

Inizia con la funzione map. nell'esempio che segue (che probabilmente ha bisogno di essere più robusto), ogni documento viene passato alla funzione map (come this). l'aspetto di codice per il campo summary e, se è presente, lo mette in minuscolo, si divide in uno spazio e quindi emette un valore per ogni parola trovata.

var map = function() { 
    var summary = this.summary; 
    if (summary) { 
     // quick lowercase to normalize per your requirements 
     summary = summary.toLowerCase().split(" "); 
     for (var i = summary.length - 1; i >= 0; i--) { 
      // might want to remove punctuation, etc. here 
      if (summary[i]) {  // make sure there's something 
       emit(summary[i], 1); // store a 1 for each word 
      } 
     } 
    } 
}; 

Poi, nella funzione reduce, le somme tutti i risultati trovati dalla funzione map e restituisce un totale discreto per ogni parola che era emit ta sopra.

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

Infine, eseguire il MapReduce:

> db.so.mapReduce(map, reduce, {out: "word_count"}) 

I risultati con i dati di esempio:

> db.word_count.find().sort({value:-1}) 
{ "_id" : "is", "value" : 3 } 
{ "_id" : "bad", "value" : 2 } 
{ "_id" : "good", "value" : 2 } 
{ "_id" : "this", "value" : 2 } 
{ "_id" : "neither", "value" : 1 } 
{ "_id" : "or", "value" : 1 } 
{ "_id" : "something", "value" : 1 } 
{ "_id" : "that", "value" : 1 } 
+0

Wow ... la mappa/ridurre parte di Mongo è molto più facile da capire di quanto temessi. Grazie per aver fornito una così grande risposta! –

5

Un MapReduce di base esempio

var m = function() { 
    var words = this.summary.split(" "); 
    if (words) { 
     for(var i=0; i<words.length; i++) { 
      emit(words[i].toLowerCase(), 1); 
     } 
    } 
} 

var r = function(k, v) { 
    return v.length; 
}; 

db.collection.mapReduce(
    m, r, { out: { merge: "words_count" } } 
) 

Questo inserirà conteggio delle parole nel nome di una raccolta di parole_count che è possibile ordinare (e l'indice)

Si noti che non fa uso di arginare, omettere la punteggiatura, maniglie smettere di parole ecc

noti inoltre è possibile ottimizzare la funzione mappa accumulando ripetendo parola (e) chiave occorrenze e emettere il conteggio, non solo 1

+0

Grazie per l'eccellente risposta. Sono nella difficile posizione di dover scegliere una risposta "giusta", e anche se le tue funzionano perfettamente, la risposta di WiredPrairie ha commenti e un po 'più di spiegazione. Grazie per aver dedicato del tempo per aiutarmi! –

Problemi correlati