2012-04-06 10 views
23

Ho bisogno di sostituire una stringa in alcuni documenti. Ho cercato su google questo codice, ma sfortunatamente non cambia nulla. Non sono sicuro sulla sintassi sulla linea muggito:Come sostituire la stringa in tutti i documenti in Mongo

pulpdb = db.getSisterDB("pulp_database"); 
var cursor = pulpdb.repos.find(); 
while (cursor.hasNext()) { 
    var x = cursor.next(); 
    x['source']['url'].replace('aaa', 'bbb'); // is this correct? 
    db.foo.update({_id : x._id}, x); 
} 

vorrei aggiungere alcune stampe di debug per vedere che cosa è il valore, ma non ho esperienze con MongoDB Shell. Ho solo bisogno di sostituire questo:

{ "source": { "url": "http://aaa/xxx/yyy" } } 

con

{ "source": { "url": "http://bbb/xxx/yyy" } } 
+0

La shell Mongo esegue un javascript arbitrario che suggerisce che il codice funziona. L'hai semplicemente provato? – Derick

risposta

37

Essa non risolve in genere: se si dispone di stringa http://aaa/xxx/aaa (yyy uguale a aaa) vi ritroverete con http://bbb/xxx/bbb. Ma se tutto va bene, il codice funzionerà.

Per aggiungere informazioni di debug usare print funzione:

var cursor = db.test.find(); 
while (cursor.hasNext()) { 
    var x = cursor.next(); 
    print("Before: "+x['source']['url']); 
    x['source']['url'] = x['source']['url'].replace('aaa', 'bbb'); 
    print("After: "+x['source']['url']); 
    db.test.update({_id : x._id}, x); 
} 

(E, a proposito, se si desidera stampare gli oggetti, c'è anche printjson funzione)

+0

Oh non ho provato "print" :-) Che semplice! Ok, posso vedere che i dati stanno arrivando, suppongo di avere un intoppo nella regexp (il caso reale non è xxx ma https://abc.blablab.com) – lzap

+0

Capito - Ho dovuto fare x ['source '] [' url '] = x [' source '] [' url ']. sostituisci (...) invece. – lzap

+0

Hmm per qualche strana ragione, la variabile viene sostituita ma i dati non vengono memorizzati allora. Devo eseguire un commit o qualcosa del genere? Vedo ancora vecchi dati lì. – lzap

1

MongoDB può fare stringa di ricerca/sostituire tramite mapreduce. Sì, è necessario disporre di una struttura dati molto speciale per questo - non è possibile avere nulla nei tasti superiori ma è necessario memorizzare tutto sotto un documento secondario sotto value. Come questo:

{ 
    "_id" : ObjectId("549dafb0a0d0ca4ed723e37f"), 
    "value" : { 
      "title" : "Top 'access denied' errors", 
      "parent" : "system.admin_reports", 
      "p" : "\u0001\u001a%" 
    } 
} 

Una volta che avete questo ben impostato si può fare:

$map = new \MongoCode("function() { 
    this.value['p'] = this.value['p'].replace('$from', '$to'); 
    emit(this._id, this.value); 
}"); 
$collection = $this->mongoCollection(); 
// This won't be called. 
$reduce = new \MongoCode("function() { }"); 
$collection_name = $collection->getName(); 
$collection->db->command([ 
    'mapreduce' => $collection_name, 
    'map' => $map, 
    'reduce' => $reduce, 
    'out' => ['merge' => $collection_name], 
    'query' => $query, 
    'sort' => ['_id' => 1], 
]); 
+0

Questo non è un approccio corretto al problema - mapReduce può produrre un nuovo set di risultati, non dovrebbe essere usato per "sostituire" "valori esistenti in questo modo. Inoltre, si sta dipendendo da qualcosa di estremamente specifico - formattando la propria collezione in questo modo solo per l'output di _id, coppie di valori sembrano molto più complicate della risposta già data per farlo iterando sui documenti nella shell. –

+0

Non tutte le applicazioni Web hanno i privilegi per eseguire i comandi della shell. Un altro approccio sarebbe quello di recuperare tutto in PHP, sostituire e salvare ma nel server sicuramente è più veloce. Infine, potresti citare qualche documentazione ufficiale sul perché non dovrebbe essere usato in questo modo? Non ho letto nulla dicendo che non dovresti unirmi alla fonte. – chx

+0

non stai né mappando né riducendo :) Fondamentalmente, stai sovrascrivendo e questo non è proprio lo scopo di "mapReduce" - stai facendo letteralmente un aggiornamento di ogni documento. Nella migliore delle ipotesi, questo può essere descritto come un hack (che funziona solo su questo esatto formato specifico del documento) –

1

Il modo migliore per farlo se siete su MongoDB 2.6 o più recente è looping sopra l'oggetto cursore con il .forEach metodo e aggiornare ogni documento usin "bulk" operazioni per la massima efficienza.

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

db.collection.find().forEach(function(doc) { 
    print("Before: "+doc.source.url); 
    bulk.find({ '_id': doc._id }).update({ 
     '$set': { 'source.url': doc.source.url.replace('aaa', 'bbb') } 
    }) 
    count++; 
    if(count % 200 === 0) { 
     bulk.execute(); 
     bulk = db.collection.initializeOrderedBulkOp(); 
    } 

// Clean up queues 
if (count > 0) 
    bulk.execute(); 

Da MongoDB 3.2 il Bulk() API e la sua associata methods sono deprecati è necessario utilizzare il metodo db.collection.bulkWrite().

È necessario eseguire il loop sul cursore, creare dinamicamente la query e $push ogni operazione su un array.

var operations = []; 
db.collection.find().forEach(function(doc) { 
    print("Before: "+doc.source.url); 
    var operation = { 
     updateOne: { 
      filter: { '_id': doc._id }, 
      update: { 
       '$set': { 'source.url': doc.source.url.replace('aaa', 'bbb') } 
      } 
     } 
    }; 
    operations.push(operation); 
}) 
operations.push({ 
    ordered: true, 
    writeConcern: { w: "majority", wtimeout: 5000 } 
}) 

db.collection.bulkWrite(operations); 
Problemi correlati