2013-10-11 8 views
6

In MongoDB, sto provando a scrivere le funzioni di Riduzione mappa che salva solo i dati se soddisfa determinati criteri.Come faccio a non salvare i dati nella mia funzione reduce() in MongoDB?

Non riesco a capire come non emit() dal mio riduttore. Salva sempre i dati, in un modo o nell'altro.

Ecco un esempio generico. Ignora il contesto dei dati: ho creato questi dati e codice esclusivamente per lo scopo di questa domanda.

Data Set:

{ "_id" : ObjectId("52583b3a58da9769dda48853"), "date" : "01-01-2013", "count" : 1 } 
{ "_id" : ObjectId("52583b3d58da9769dda48854"), "date" : "01-01-2013", "count" : 1 } 
{ "_id" : ObjectId("52583b4258da9769dda48855"), "date" : "01-02-2013", "count" : 1 } 
{ "_id" : ObjectId("52583b4f58da9769dda48856"), "date" : "01-03-2013", "count" : 4 } 

Mappa Funzione:

// Map all data by (date, count) 
var map = function() { 
    var key = this.date; 
    var value = this.count; 
    emit(key, value); 
} 

Reducer che semplicemente ignora i dati indesiderati.

// Only save dates which have count > 2 
var reducer = function(date, counts) { 
    var sum = Array.sum(counts); 
    if (sum > 2) { 
     return sum; 
    } 
} 

Risultati (valore di 1 non è stato ignorato):

{ "_id" : "01-01-2013", "value" : null } 
{ "_id" : "01-02-2013", "value" : 1 } 
{ "_id" : "01-03-2013", "value" : 4 } 

Ho anche aggiunto in una dichiarazione di ritorno vuoto, ma ho ottenuto gli stessi risultati:

// Only save dates which have count > 2 
var reducer = function(date, counts) { 
    var sum = Array.sum(counts); 
    if (sum > 2) { 
     return sum; 
    } 
    else return; 
} 

Quello che vorrei Per avere successo sono solo i seguenti dati esistere nella mia collezione di output dopo l'esecuzione di Map-Reduce. Come posso realizzare questo?

{ "_id" : "01-03-2013", "value" : 4 } 

risposta

3

è possibile eseguire un'operazione MapReduce supplementare, con le seguenti funzioni:

var second_map = function() { 
    if(this.value > 2) { 
     emit(this._id, this.value); 
    } 
} 

e

var second_reduce = function() {} 

La funzione di ridurre può essere vuoto, perché not having multiple values per key will cause it to not even be called in questo caso.

Così, correndo il MapReduce in questo modo:

db.map_reduce_example.mapReduce(
    second_map, second_reduce, {out: 'final_mapreduce_result'}); 

produrrà della collezione:

> db.final_mapreduce_result.find() 
{ "_id" : "01-03-2013", "value" : 4 } 

Si noti che se si decide di utilizzare questo approccio, è possibile rimuovere la condizione if (sum > 2) dal prima riduci la funzione.

+0

Questo è intelligente! Dovrò fare una corsa per vedere quanto è veloce quando ho eseguito i miei dati reali, ma è un trucco abbastanza buono per raggiungere l'obiettivo. Grazie. – Kurtis

+3

Non farei una mappa/riduci se stai solo cercando di filtrare alcuni risultati. Basta fare una rimozione dopo la prima mappa/riduci: db.mroutput.remove ({value: {$ lte: 2}}). Remove() verrà eseguito molto più velocemente di una mappa/riduci. –

+0

@RobMoore Hai ragione; questa è una soluzione più semplice e potenzialmente più efficiente. Penso che dovresti postarlo come una risposta separata. – GolfWolf

2

Abbiamo bisogno di ricordare che un riduttore può essere saltata se c'è solo 1 valore emessa (dalla mappa()) per la chiave. Non dovremmo nemmeno provare a filtrare i risultati nella riduzione poiché la riduzione può essere chiamata più volte per la stessa chiave (ogni volta con un sottoinsieme dei valori emessi).

L'unica altra opzione è il metodo finalize ma che risulterà nei valori null non nella rimozione delle voci dal risultato.

Penso che l'unico modo per ottenere il risultato desiderato sia utilizzare la struttura di aggregazione anziché ridurre la mappa. Il gasdotto sarà simile:

db.test.aggregate( 
    { 
    "$project" : { 
     "_id" : 0, 
     "date" : 1, 
     "count" : 1 
    } 
    }, 
    { 
    "$group" : { 
     "_id" : "$date", 
     "value" : { "$sum" : "$count" } 
    } 
    }, 
    { 
    "$match" : { 
     "value" : { "$gt" : 2 } 
    } 
    } 
); 
{ "result" : [ { "_id" : "01-03-2013", "value" : 4 } ], "ok" : 1 } 

L'unica grande lato negativo di questo approccio è il risultato devono tornare in linea, che limita la dimensione dei risultati a 16 MB. Che sarà risolto/rimediato nella versione 2.6: https://jira.mongodb.org/browse/SERVER-10097

HTH, Rob.

+0

La cosa principale che non mi piace del framework Aggregation è il formato di output. Prende tutti i risultati e li memorizza in un singolo oggetto. Apprezzo sicuramente il suggerimento, però! E sapere che il limite di 16 MB sarà risolto presto è anche una grande notizia. Grazie! – Kurtis

+2

2.5.2 (versione di sviluppo) ha già implementato questo - il quadro di aggregazione può restituire un cursore, oppure è possibile specificare l'output andare a una raccolta. Considerando che il quadro di aggregazione è MOLTO più veloce della riduzione della mappa, ti incoraggio ad usarlo quando puoi. –

+1

Inoltre, se necessario per MapReduce, votare https://jira.mongodb.org/browse/SERVER-2340 che richiede questa funzionalità. Ci sono cose che il framework agg non può fare che la mappa possa ridurre, quindi questo è ancora un utile miglioramento. –

Problemi correlati