La meilleure façon de le faire est en version 4.2+ qui permet l' utilisation d' un pipeline d'agrégation dans le document de mise à jour et updateOne
, updateMany
ou la update
méthode de collecte. Notez que ce dernier est obsolète dans la plupart des pilotes de langues, sinon dans toutes.
MongoDB 4.2+
La version 4.2 a également introduit l' $set
opérateur d'étape de pipeline qui est un alias pour $addFields
. Je vais l'utiliser $set
ici car il correspond à ce que nous essayons de réaliser.
db.collection.<update method>(
{},
[
{"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
]
)
MongoDB 3.4+
Dans la version 3.4+, vous pouvez utiliser $addFields
les $out
opérateurs de pipeline d'agrégation.
db.collection.aggregate(
[
{ "$addFields": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}},
{ "$out": "collection" }
]
)
Notez que cela ne met pas à jour votre collection mais remplace plutôt la collection existante ou en crée une nouvelle. De plus, pour les opérations de mise à jour qui nécessitent une «conversion de type», vous aurez besoin d'un traitement côté client et, selon l'opération, vous devrez peut-être utiliser la find()
méthode au lieu de la .aggreate()
méthode.
MongoDB 3.2 et 3.0
Pour ce faire, nous utilisons $project
nos documents et utilisons l' $concat
opérateur d'agrégation de chaînes pour renvoyer la chaîne concaténée. À partir de là, vous itérez ensuite le curseur et utilisez l' $set
opérateur de mise à jour pour ajouter le nouveau champ à vos documents à l'aide d' opérations en bloc 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 bulkWrite
méthode.
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
À partir de cette version, vous devez utiliser l' Bulk
API 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 } }
);
})
Vous devriez parcourir. Pour votre cas spécifique:
la source
save()
remplace complètement le document. Devrait utiliser à laupdate()
place.db.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } );
create_guid
qui ne produisait un GUID unique par document que lors de l'itération deforEach
cette manière (c.-à-d. Que l'utilisationcreate_guid
d'uneupdate
instruction avecmutli=true
le même GUID était générée pour tous les documents). Cette réponse a parfaitement fonctionné pour moi. +1Apparemment, il existe un moyen de le faire efficacement depuis MongoDB 3.4, voir la réponse de styvane .
Réponse obsolète ci-dessous
Vous ne pouvez pas (encore) faire référence au document lui-même dans une mise à jour. Vous devrez parcourir les documents et mettre à jour chaque document à l'aide d'une fonction. Voir cette réponse pour un exemple, ou celle-ci pour le côté serveur
eval()
.la source
update
opération. Cette demande de fonctionnalité associée n'est toujours pas résolue également.OPEN
.Pour une base de données avec une activité élevée, vous pouvez rencontrer des problèmes où vos mises à jour affectent activement les enregistrements changeants et pour cette raison, je recommande d'utiliser snapshot ()
http://docs.mongodb.org/manual/reference/method/cursor.snapshot/
la source
snapshot()
:Deprecated in the mongo Shell since v3.2. Starting in v3.2, the $snapshot operator is deprecated in the mongo shell. In the mongo shell, use cursor.snapshot() instead.
lienConcernant cette réponse , la fonction snapshot est déconseillée dans la version 3.6, selon cette mise à jour . Ainsi, sur la version 3.6 et supérieure, il est possible d'effectuer l'opération de cette façon:
la source
Au départ
Mongo 4.2
,db.collection.update()
peut accepter un pipeline d'agrégation, permettant enfin la mise à jour / création d'un champ basé sur un autre champ:La première partie
{}
est la requête de correspondance, filtrant les documents à mettre à jour (dans notre cas tous les documents).La deuxième partie
[{ $set: { name: { ... } }]
est le pipeline d'agrégation de mise à jour (notez les crochets au carré signifiant l'utilisation d'un pipeline d'agrégation).$set
est un nouvel opérateur d'agrégation et un alias de$addFields
.N'oubliez pas
{ multi: true }
, sinon seul le premier document correspondant sera mis à jour.la source
J'ai essayé la solution ci-dessus mais je l'ai trouvée inappropriée pour de grandes quantités de données. J'ai ensuite découvert la fonctionnalité de flux:
la source
Voici ce que nous avons trouvé pour copier un champ dans un autre pour ~ 150_000 enregistrements. Cela a pris environ 6 minutes, mais est toujours beaucoup moins gourmand en ressources qu'il ne l'aurait été pour instancier et itérer sur le même nombre d'objets rubis.
la source
Avec MongoDB version 4.2+ , les mises à jour sont plus flexibles car elles permettent d'utiliser le pipeline d'agrégation dans ses
update
,updateOne
etupdateMany
. Vous pouvez maintenant transformer vos documents en utilisant les opérateurs d'agrégation puis les mettre à jour sans avoir besoin d'expliciter la$set
commande (à la place nous utilisons$replaceRoot: {newRoot: "$$ROOT"}
)Ici, nous utilisons la requête agrégée pour extraire l'horodatage du champ ObjectID "_id" de MongoDB et mettre à jour les documents (je ne suis pas un expert en SQL, mais je pense que SQL ne fournit aucun ObjectID généré automatiquement avec horodatage, vous devez créer automatiquement cette date)
la source
{ $replaceRoot: { newRoot: "$$ROOT" } }
; cela signifie remplacer le document par lui-même, ce qui est inutile. Si vous remplacez$addFields
par son alias$set
etupdateMany
qui est l'un des alias deupdate
, alors vous obtenez exactement la même réponse que celle ci- dessus.