sql >> Base de Datos >  >> NoSQL >> MongoDB

Mongoose sobrescribe el documento en lugar de los campos `$set`

En realidad, excepto por el hecho de que mongoose en realidad está "jugando con" la actualización oculta, esta es en realidad la acción predeterminada de su envío a una función regular de MongoDB.

Así que mongoose lo considera "sabio" como un método de conveniencia para "presumir" que tenía la intención de emitir un $set instrucción aquí. Como en realidad no desea hacer eso en este caso, desactive ese comportamiento a través de { overwrite: true } en las opciones pasadas a cualquier .update() método:

Como ejemplo completo:

const mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

const uri = 'mongodb://localhost/test',
      options = { useMongoClient: true };

const testSchema = new Schema({
  name: String,
  phone: String
});

const Test = mongoose.model('Test', testSchema);

function log(data) {
  console.log(JSON.stringify(data,undefined,2))
}

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    // Clean data
    await Promise.all(
      Object.keys(conn.models).map( m => conn.models[m].remove({}) )
    );

    // Create a document
    let test = await Test.create({
      name: 'john doe',
      phone: '+12345678901'
    });
    log(test);

    // This update will apply using $set for the name
    let notover = await Test.findOneAndUpdate(
      { _id: test._id },
      { name: 'Bill S. Preston' },
      { new: true }
    );
    log(notover);

    // This update will just use the supplied object, and overwrite
    let updated = await Test.findOneAndUpdate(
      { _id: test._id },
      { name: 'Dan Smith' },
      { new: true, overwrite: true }
    );
    log(updated);


  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

Produce:

Mongoose: tests.remove({}, {})
Mongoose: tests.insert({ name: 'john doe', phone: '+12345678901', _id: ObjectId("596efb0ec941ff0ec319ac1e"), __v: 0 })
{
  "__v": 0,
  "name": "john doe",
  "phone": "+12345678901",
  "_id": "596efb0ec941ff0ec319ac1e"
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { '$set': { name: 'Bill S. Preston' } }, { new: true, upsert: false, remove: false, fields: {} })
{
  "_id": "596efb0ec941ff0ec319ac1e",
  "name": "Bill S. Preston",
  "phone": "+12345678901",
  "__v": 0
}
Mongoose: tests.findAndModify({ _id: ObjectId("596efb0ec941ff0ec319ac1e") }, [], { name: 'Dan Smith' }, { new: true, overwrite: true, upsert: false, remove: false, fields: {} })
{
  "_id": "596efb0ec941ff0ec319ac1e",
  "name": "Dan Smith"
}

Mostrar el documento está "sobrescrito" porque suprimimos el $set operación que de otro modo habría sido interpolada. Los dos ejemplos se muestran primero sin overwrite opción, que aplica el $set modificador, y luego "con" el overwrite opción, donde se respeta el objeto que pasó para la "actualización" y no existe tal $set se aplica el modificador.

Tenga en cuenta que así es como el controlador del nodo MongoDB hace esto "de forma predeterminada". Entonces, el comportamiento de agregar el $set "implícito" lo está haciendo la mangosta, a menos que usted le diga que no lo haga.

NOTA La verdadera forma de "reemplazar" sería usar replaceOne , ya sea como el método API de replaceOne() o a través de bulkWrite() . El overwrite es un legado de cómo mangosta quiere aplicar $set como se describió y demostró anteriormente, sin embargo, la API oficial de MongoDB presenta replaceOne como "especial" rey de update() operación que no permite el uso de operadores atómicos como $set dentro de la instrucción y se producirá un error si lo intenta.

Esto es mucho más claro semánticamente desde replace lee muy claramente para qué se usa realmente el método. Dentro de las llamadas API estándar a update() las variantes, por supuesto, aún le permiten omitir los operadores atómicos y simplemente reemplazarán contenido de todos modos. Pero se deben esperar advertencias.