Mettre à jour le champ MongoDB en utilisant la valeur d’un autre champ

Dans MongoDB, est-il possible de mettre à jour la valeur d’un champ en utilisant la valeur d’un autre champ? Le SQL équivalent serait quelque chose comme:

UPDATE Person SET Name = FirstName + ' ' + LastName 

Et le pseudo-code MongoDB serait:

 db.person.update( {}, { $set : { name : firstName + ' ' + lastName } ); 

Vous ne pouvez pas vous référer au document lui-même dans une mise à jour (encore). Vous devrez parcourir les documents et mettre à jour chaque document à l’aide d’une fonction. Voir cette réponse pour un exemple ou celui-ci pour eval() côté serveur eval() .

Vous devriez parcourir à travers. Pour votre cas particulier:

 db.person.find().snapshot().forEach( function (elem) { db.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } ); } ); 

La meilleure façon de faire est d’utiliser la structure d’agrégation pour calculer notre nouveau champ.

MongoDB 3.4

La solution la plus efficace consiste à utiliser MongoDB 3.4 en utilisant les opérateurs de pipeline $addFields et $out agrégation.

 db.collection.aggregate( [ { "$addFields": { "name": { "$concat": [ "$firstName", " ", "$lastName" ] } }}, { "$out": "collection" } ] ) 

Notez que cela ne met pas à jour votre collection mais remplace la collection existante ou en crée une nouvelle. De même, pour les opérations de mise à jour qui requièrent le “type casting”, vous aurez besoin du traitement côté client et, en fonction de l’opération, vous devrez peut-être utiliser la méthode find() au lieu de la méthode .aggreate() .

MongoDB 3.2 et 3.0

Nous $concat nos documents et en utilisant l’opérateur d’agrégation de chaînes $concat pour renvoyer la chaîne concaténée. we De là, vous parcourez ensuite le curseur et utilisez l’opérateur de mise à jour $set pour append le nouveau champ à vos documents en utilisant des opérations en masse pour une efficacité maximale.

Requête d’agrégation:

 var cursor = db.collection.aggregate([ { "$project": { "name": { "$concat": [ "$firstName", " ", "$lastName" ] } }} ]) 

MongoDB 3.2 ou plus récent

à partir de cela, vous devez utiliser la méthode bulkWrite .

 var requests = []; cursor.forEach(document => { requests.push( { 'updateOne': { 'filter': { '_id': document._id }, 'update': { '$set': { 'name': document.name } } } }); if (requests.length === 500) { //Execute per 500 operations and re-init db.collection.bulkWrite(requests); requests = []; } }); if(requests.length > 0) { db.collection.bulkWrite(requests); } 

MongoDB 2.6 et 3.0

A partir de cette version, vous devez utiliser l’API en Bulk désormais obsolète, et ses méthodes associées .

 var bulk = db.collection.initializeUnorderedBulkOp(); var count = 0; cursor.snapshot().forEach(function(document) { bulk.find({ '_id': document._id }).updateOne( { '$set': { 'name': document.name } }); count++; if(count%500 === 0) { // Excecute per 500 operations and re-init bulk.execute(); bulk = db.collection.initializeUnorderedBulkOp(); } }) // clean up queues if(count > 0) { bulk.execute(); } 

MongoDB 2.4

 cursor["result"].forEach(function(document) { db.collection.update( { "_id": document._id }, { "$set": { "name": document.name } } ); }) 

Pour une firebase database à forte activité, vous risquez de rencontrer des problèmes où vos mises à jour affectent la modification active des enregistrements et pour cette raison, je vous recommande d’utiliser snapshot ()

 db.person.find().snapshot().forEach( function (hombre) { hombre.name = hombre.firstName + ' ' + hombre.lastName; db.person.save(hombre); }); 

http://docs.mongodb.org/manual/reference/method/cursor.snapshot/

J’ai essayé la solution ci-dessus mais je l’ai trouvée inadaptée à de grandes quantités de données. J’ai ensuite découvert la fonctionnalité de stream:

 MongoClient.connect("...", function(err, db){ var c = db.collection('yourCollection'); var s = c.find({/* your query */}).stream(); s.on('data', function(doc){ c.update({_id: doc._id}, {$set: {name : doc.firstName + ' ' + doc.lastName}}, function(err, result) { /* result == true? */} } }); s.on('end', function(){ // stream can end before all your updates do if you have a lot }) }) 

Voici ce que nous avons proposé pour copier un champ dans un autre pour environ 150 000 enregistrements. Il a fallu environ 6 minutes, mais cela nécessite encore beaucoup moins de ressources qu’il n’aurait été possible d’instancier et d’itérer le même nombre d’objects Ruby.

 js_query = %({ $or : [ { 'settings.mobile_notifications' : { $exists : false }, 'settings.mobile_admin_notifications' : { $exists : false } } ] }) js_for_each = %(function(user) { if (!user.settings.hasOwnProperty('mobile_notifications')) { user.settings.mobile_notifications = user.settings.email_notifications; } if (!user.settings.hasOwnProperty('mobile_admin_notifications')) { user.settings.mobile_admin_notifications = user.settings.email_admin_notifications; } db.users.save(user); }) js = "db.users.find(#{js_query}).forEach(#{js_for_each});" Mongoid::Sessions.default.command('$eval' => js)