MongoDB seleziona count (distinto x) su una colonna indicizzata: conteggia risultati unici per set di dati di grandi dimensioni

Ho esaminato diversi articoli ed esempi e non ho ancora trovato un modo efficiente per fare questa query SQL in MongoDB (dove ci sono milioni di righe documenti)

Primo tentativo

(es. da questa domanda quasi duplicata – equivalente Mongo di SELECT DISTINCT di SQL? )

db.myCollection.distinct("myIndexedNonUniqueField").length 

Ovviamente ho ricevuto questo errore in quanto il mio set di dati è enorme

 Thu Aug 02 12:55:24 uncaught exception: distinct failed: { "errmsg" : "exception: distinct too big, 16mb cap", "code" : 10044, "ok" : 0 } 

Secondo tentativo

Ho deciso di provare a fare un gruppo

 db.myCollection.group({key: {myIndexedNonUniqueField: 1}, initial: {count: 0}, reduce: function (obj, prev) { prev.count++;} } ); 

Ma ho ricevuto questo messaggio di errore:

 exception: group() can't handle more than 20000 unique keys 

Terzo tentativo

Non ho ancora provato ma ci sono diversi suggerimenti che coinvolgono mapReduce

per esempio

  • questo come fare distinti e raggruppare in mongodb? (non accettato, l’autore della risposta / OP non lo ha testato)
  • questo gruppo MongoDB per funzionalità (sembra simile a Second Attempt)
  • questo http://blog.emmettshear.com/post/2010/02/12/Counting-Uniques-With-MongoDB
  • questo https://groups.google.com/forum/?fromgroups#!topic/mongodb-user/trDn3jJjqtE
  • questo http://cookbook.mongodb.org/patterns/unique_items_map_reduce/

Anche

Sembra che ci sia una richiesta pull su GitHub che fissa il metodo .distinct per menzionarlo dovrebbe restituire solo un conteggio, ma è ancora aperto: https://github.com/mongodb/mongo/pull/34

Ma a questo punto ho pensato che valesse la pena di chiedere qui, qual è l’ultima sull’argomento? Devo passare a SQL o ad un altro NoSQL DB per conteggi distinti? o c’è un modo efficace?

Aggiornare:

Questo commento sui documenti ufficiali di MongoDB non è incoraggiante, è accurato?

http://www.mongodb.org/display/DOCS/Aggregation#comment-430445808

Update2:

Sembra che il nuovo framework di aggregazione risponda al commento sopra … (MongoDB 2.1 / 2.2 e versioni successive, anteprima di sviluppo disponibile, non per la produzione)

http://docs.mongodb.org/manual/applications/aggregation/

1) Il modo più semplice per farlo è tramite il framework di aggregazione. Ciò richiede due comandi “$ group”: il primo raggruppa per valori distinti, il secondo conta tutti i valori distinti

 pipeline = [ { $group: { _id: "$myIndexedNonUniqueField"} }, { $group: { _id: 1, count: { $sum: 1 } } } ]; // // Run the aggregation command // R = db.runCommand( { "aggregate": "myCollection" , "pipeline": pipeline } ); printjson(R); 

2) Se vuoi farlo con Map / Reduce puoi farlo. Anche questo è un processo a due fasi: nella prima fase costruiamo una nuova raccolta con un elenco di ogni valore distinto per la chiave. Nel secondo facciamo un count () sulla nuova collezione.

 var SOURCE = db.myCollection; var DEST = db.distinct DEST.drop(); map = function() { emit( this.myIndexedNonUniqueField , {count: 1}); } reduce = function(key, values) { var count = 0; values.forEach(function(v) { count += v['count']; // count each distinct value for lagniappe }); return {count: count}; }; // // run map/reduce // res = SOURCE.mapReduce( map, reduce, { out: 'distinct', verbose: true } ); print( "distinct count= " + res.counts.output ); print( "distinct count=", DEST.count() ); 

Tieni presente che non è ansible restituire il risultato della mappa / ridurre in linea, poiché questo supererà potenzialmente il limite di dimensioni del documento di 16 MB. È ansible salvare il calcolo in una raccolta e quindi contare () la dimensione della raccolta oppure è ansible ottenere il numero di risultati dal valore restituito da mapReduce ().

 db.myCollection.aggregate( {$group : {_id : "$myIndexedNonUniqueField"} }, {$group: {_id:1, count: {$sum : 1 }}}); 

dritto al risultato:

 db.myCollection.aggregate( {$group : {_id : "$myIndexedNonUniqueField"} }, {$group: {_id:1, count: {$sum : 1 }}}) .result[0].count; 

La seguente soluzione ha funzionato per me

db.test.distinct ( ‘user’); [“alex”, “England”, “France”, “Australia”]

db.countries.distinct (‘country’). lunghezza 4