How to replace string in all documents in Mongo

By : lzap
Source: Stackoverflow.com
Question!

I need to replace a string in certain documents. I have googled this code, but it unfortunately does not change anything. I am not sure about the syntax on the line bellow:

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);
}

I would like to add some debug prints to see what the value is, but I have no experiences with MongoDB Shell. I just need to replace this:

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

with

{ "source": { "url": "http://bbb/xxx/yyy" } }
By : lzap


Answers

MongoDB can do string search/replace via mapreduce. Yes, you need to have a very special data structure for it -- you can't have anything in the top keys but you need to store everything under a subdocument under value. Like this:

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

Once you have this neatly set up you can do:

$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],
]);
By : chx


The best way to do this if you are on MongoDB 2.6 or newer is looping over the cursor object using the .forEach method and update each document usin "bulk" operations for maximum efficiency.

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();

From MongoDB 3.2 the Bulk() API and its associated methods are deprecated you will need to use the db.collection.bulkWrite() method.

You will need loop over the cursor, build your query dynamically and $push each operation to an 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);
By : Styvane


This video can help you solving your question :)
By: admin