2015-03-04 12 views
6

Ho la seguente query, per trovare i tag   in un campo nome e sostituirli con uno spazio vuoto - per sbarazzarcene.
Le stringhe di nome possono avere da 1 a molti tag  , ad es.Trova e sostituisci stringhe nei documenti in modo efficiente

AA aa 
AA  aa 
AA   aa 
AA    aa 
AA AA aaaaaaaa 

... così.

db.tests.find({'name':/.* .*/}).forEach(function(test){ 
     test.name = test.name.replace(" ",""); 
     db.tests.save(test); 
    }); 

    db.tests.find({'name':/.*  .*/}).forEach(function(test){ 
     test.name = test.name.replace("  ",""); 
     db.tests.save(test); 
    }); 

    db.tests.find({'name':/.*   .*/}).forEach(function(test){ 
     test.name = test.name.replace("   ",""); 
     db.tests.save(test); 
    }); 

Oltre ripetere lo stesso modello di query, c'è una soluzione migliore per gestire questo scenario, in termini di minore duplicazione e prestazioni superiori?

risposta

12

Sicuramente se tutto quello che vogliamo fare è togliere i   entità dal testo poi basta fare una partita e sostituzione globale:

db.tests.find({ "name": /\ /g }).forEach(function(doc) { 
    doc.name = doc.name.replace(/ /g,""); 
    db.tests.update({ "_id": doc._id },{ "$set": { "name": doc.name } }); 
}); 

Quindi non ci dovrebbe essere bisogno di scrivere ogni combinazione, la regex sostituirà molto la corrispondenza con l'opzione /g. È possibile utilizzare anche /m per multi-linea: la stringa "nome" contiene caratteri di nuova riga. Vedere uno regexer example di base.

Si consiglia inoltre di utilizzare $set per modificare solo i campi che si desidera realmente anziché l'intero documento indietro di .save(). C'è meno traffico e meno possibilità di sovrascrivere le modifiche che potrebbero essere state apportate da un altro processo da quando il documento è stato letto.

Idealmente si dovrebbe utilizzare l'API di Bulk Operations con MongoDB versioni 2.6 e successive. Questo permette agli aggiornamenti di "batch" per cui v'è ancora una volta meno traffico tra il client e il server:

var bulk = db.tests.initializeOrderedBulkOp(); 
var count = 0; 

db.tests.find({ "name": /\ /g }).forEach(function(doc) { 
    doc.name = doc.name.replace(/ /g,""); 
    bulk.find({ "_id": doc._id }) 
     .updateOne({ "$set": { "name": doc.name } }); 
    count++; 

    if (count % 1000 == 0) { 
     bulk.execute(); 
     bulk = db.tests.initializeOrderedBulkOp(); 
    } 
}); 

if (count % 1000 != 0) 
    bulk.execute(); 

Quelli sono i tuoi modi principali per migliorare questo. Sfortunatamente non c'è modo per una dichiarazione di aggiornamento di MongoDB di usare un valore esistente come parte della sua espressione di aggiornamento in questo modo, quindi l'unico modo è il loop, ma si può fare molto per ridurre le operazioni come mostrato.

+1

Non è necessario eseguire una query $ set di aggiornamento: è sufficiente eseguire db.tests.save (doc); – alexcasalboni

Problemi correlati