27 votes

Comment remplacer une chaîne dans tous les documents dans Mongo

Je dois remplacer une chaîne de caractères dans certains documents. J'ai cherché ce code sur Google, mais il ne change malheureusement rien. Je ne suis pas sûr de la syntaxe de la ligne ci-dessous :

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

J'aimerais ajouter des impressions de débogage pour voir quelle est la valeur, mais je n'ai aucune expérience du shell MongoDB. J'ai juste besoin de remplacer ceci :

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

avec

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

38voto

om-nom-nom Points 33691

Ce n'est pas correct en général : si vous avez une chaîne de caractères http://aaa/xxx/aaa ( yyy est égal à aaa ), vous vous retrouverez avec http://bbb/xxx/bbb . Mais si vous êtes d'accord avec cela, le code fonctionnera.

Pour ajouter des informations de débogage, utilisez print fonction :

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

(Et d'ailleurs, si vous voulez imprimer des objets, il y a aussi printjson fonction)

4voto

user3100115 Points 15249

La meilleure façon de procéder si vous utilisez MongoDB 2.6 ou une version plus récente est de boucler sur l'objet curseur en utilisant la fonction .forEach et mettre à jour chaque document en utilisant "bulk" les opérations pour une efficacité maximale.

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

De MongoDB 3.2 le site Bulk() et l'API associée méthodes sont dépréciées, vous devrez utiliser la fonction db.collection.bulkWrite() méthode.

Vous devrez boucler sur le curseur, construire votre requête de manière dynamique et $push chaque opération à un tableau.

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

2voto

Xavier Guihot Points 6414

De nos jours,

  • démarrage Mongo 4.2 , db.collection.updateMany (alias de db.collection.update ) peut accepter un pipeline d'agrégation, permettant finalement la mise à jour d'un champ sur la base de sa propre valeur.

  • démarrage Mongo 4.4 le nouvel opérateur d'agrégation $replaceOne permet de remplacer très facilement une partie d'une chaîne de caractères.

    // { "source" : { "url" : "http://aaa/xxx/yyy" } } // { "source" : { "url" : "http://eee/xxx/yyy" } } db.collection.updateMany( { "source.url": { $regex: /aaa/ } }, [{ $set: { "source.url": { $replaceOne: { input: "$source.url", find: "aaa", replacement: "bbb" } }} }] ) // { "source" : { "url" : "http://bbb/xxx/yyy" } } // { "source" : { "url" : "http://eee/xxx/yyy" } }

  • La première partie ( { "source.url": { $regex: /aaa/ } } ) est la requête de correspondance, filtrant les documents à mettre à jour (ceux contenant "aaa" )

  • La deuxième partie ( $set: { "source.url": {... ) est le pipeline d'agrégation des mises à jour (notez les crochets qui indiquent l'utilisation d'un pipeline d'agrégation) :

    • $set est un nouvel opérateur d'agrégation ( Mongo 4.2 ) qui, dans ce cas, remplace la valeur d'un champ.
    • La nouvelle valeur est calculée avec la nouvelle $replaceOne opérateur. Notez comment source.url est modifié directement sur la base de sa propre valeur ( $source.url ).

Notez que cette opération est entièrement gérée côté serveur, ce qui ne vous permettra pas d'effectuer la partie impression de débogage de votre question.

1voto

chx Points 5381

MongoDB peut effectuer des recherches/remplacements de chaînes de caractères via mapreduce. Oui, vous devez avoir une structure de données très spéciale pour cela - vous ne pouvez pas avoir n'importe quoi dans les clés supérieures mais vous devez tout stocker sous un sous-document sous value . Comme ça :

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

Une fois que tout cela est bien mis en place, vous pouvez le faire :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X