update - Actualizar el campo MongoDB usando el valor de otro campo
save en mongodb (6)
En MongoDB, ¿es posible actualizar el valor de un campo utilizando el valor de otro campo? El SQL equivalente sería algo así como:
UPDATE Person SET Name = FirstName + '' '' + LastName
Y el pseudocódigo de MongoDB sería:
db.person.update( {}, { $set : { name : firstName + '' '' + lastName } );
Aparentemente, hay una forma de hacerlo de manera eficiente desde MongoDB 3.4, vea la respuesta de styvane .
Respuesta obsoleta a continuación
No puede consultar el documento en una actualización (todavía). Deberá recorrer los documentos y actualizar cada documento utilizando una función. Vea esta respuesta para un ejemplo, o esta para eval()
lado del servidor.
Deberías recorrerlo. Para su caso específico:
db.person.find().snapshot().forEach(
function (elem) {
db.person.update(
{
_id: elem._id
},
{
$set: {
name: elem.firstname + '' '' + elem.lastname
}
}
);
}
);
Esto es lo que se nos ocurrió para copiar un campo a otro para ~ 150_000 registros. Tomó alrededor de 6 minutos, pero aún así es mucho menos intensivo en recursos de lo que habría sido crear instancias e iterar sobre la misma cantidad de objetos 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)
La mejor manera de hacer esto es usar el marco de agregación para calcular nuestro nuevo campo.
MongoDB 3.4
La solución más eficiente está en MongoDB 3.4 utilizando los $addFields
y $out
$addFields
pipeline.
db.collection.aggregate(
[
{ "$addFields": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}},
{ "$out": "collection" }
]
)
Tenga en cuenta que esto no actualiza su colección, sino que reemplaza la colección existente o crea una nueva. También para las operaciones de actualización que requieren "conversión de tipo", necesitará el procesamiento del lado del cliente y , dependiendo de la operación, es posible que deba usar el método find()
lugar del método .aggreate()
.
MongoDB 3.2 y 3.0
La forma en que lo hacemos es $project
nuestros documentos y usando el operador de agregación de cadenas $concat
para devolver la cadena concatenada. a partir de ahí, luego se itera el cursor y se usa el operador de actualización $set
para agregar el nuevo campo a sus documentos utilizando operaciones masivas para lograr la máxima eficiencia.
Consulta de agregación:
var cursor = db.collection.aggregate([
{ "$project": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}}
])
MongoDB 3.2 o más nuevo
a partir de esto, necesita utilizar el método 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 y 3.0
Desde esta versión, debe utilizar la API Bulk
ahora en desuso y sus métodos asociados .
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 } }
);
})
Para una base de datos con alta actividad, es posible que tenga problemas en los que sus actualizaciones afecten a los registros que cambian activamente y, por este motivo, recomiendo el uso de instantáneas ()
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/
Probé la solución anterior pero no la encontré adecuada para grandes cantidades de datos. Entonces descubrí la función de transmisión:
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
})
})