Actualizar campo MongoDB usando el valor de otro campo


En MongoDB, ¿es posible actualizar el valor de un campo usando el valor de otro campo? El SQL equivalente sería algo así como:

UPDATE Person SET Name = FirstName + ' ' + LastName

Y el pseudo-código MongoDB sería:

db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );
Author: styvane, 2010-10-20

6 answers

No puede hacer referencia al documento en sí en una actualización (todavía). Tendrá que iterar a través de los documentos y actualizar cada documento utilizando una función. Vea esta respuesta para un ejemplo, o esta para el lado del servidor eval().

 101
Author: Niels van der Rest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:47

Usted debe iterar a través. Para su caso específico:

db.person.find().snapshot().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);
 216
Author: Carlos Barcelona,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-28 05:24:44

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 usando el $addFields y el $out operadores de tuberías de agregación.

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

Tenga en cuenta que este 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 "type casting" necesitará el lado del cliente processing, y dependiendo de la operación, es posible que necesite usar el método find() en lugar del método .aggreate().

MongoDB 3.2 y 3.0

La forma en que hacemos esto es por $projecting nuestros documentos y utilizar el $concat operador de agregación de cadenas para devolver la cadena concatenada. a partir de ahí, a continuación, iterar el cursor y utilizar el $set actualizar operador para agregar el nuevo campo a sus documentos usando operaciones masivas para máxima eficiencia.

Consulta de agregación:

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

MongoDB 3.2 o posterior

A partir de esto, es necesario utilizar el bulkWrite método.

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

A partir de esta versión es necesario utilizar el ahora obsoleto Bulk API 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 } }
    );
})
 110
Author: styvane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-24 18:29:10

Para una base de datos con alta actividad, puede encontrarse con problemas en los que sus actualizaciones afectan a los registros que cambian activamente y, por esta razón, recomiendo usar 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 /

 38
Author: Eric Kigathi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-11 16:58:51

Probé la solución anterior, pero me pareció inadecuada para grandes cantidades de datos. Luego descubrí la función 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
    })
})
 9
Author: Chris Gibb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-03 11:44:17

Esto es lo que se nos ocurrió para copiar un campo a otro para ~150_000 registros. Tomó alrededor de 6 minutos, pero todavía es significativamente menos intensivo de recursos de lo que habría sido para instanciar e iterar sobre el mismo número 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)
 2
Author: Chris Bloom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 15:07:08