MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

O Mongoose sobrescreve o documento em vez dos campos `$set`


Na verdade, mas pelo fato de que o mangusto está realmente "mexendo" com a atualização nos bastidores, essa é realmente a ação padrão do seu envio para uma função regular do MongoDB.

Portanto, o mangusto considera "sábio" como um método de conveniência "presumir" que você pretendia emitir um $set instrução aqui. Como você realmente não deseja fazer isso neste caso, desative esse comportamento por meio de { overwrite: true } nas opções passadas para qualquer .update() método:

Como um exemplo 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();
  }

})()

Produz:
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 que o documento foi "substituído" porque suprimimos o $set operação que de outra forma teria sido interpolada. Os dois exemplos são exibidos primeiro sem o overwrite opção, que aplica o $set modificador e, em seguida, "com" o overwrite opção, onde o objeto que você passou para a "atualização" é respeitado e nenhum $set modificador é aplicado.

Observe que é assim que o driver do MongoDB Node faz isso "por padrão". Portanto, o comportamento de adicionar no $set "implícito" está sendo feito pelo mangusto, a menos que você diga que não.

OBSERVAÇÃO A verdadeira maneira de "substituir" seria usar replaceOne , seja como o método de API de replaceOne() ou através de bulkWrite() . O overwrite é um legado de como o mangusto quer aplicar $set conforme descrito e demonstrado acima, no entanto, a API oficial do MongoDB apresenta replaceOne como "especial" rei de update() operação que não permite o uso de operadores atômicos como $set dentro da instrução e dará erro se você tentar.

Isso é muito mais claro semanticamente desde substituir lê muito claramente para que o método é realmente usado. Dentro de chamadas de API padrão para o update() variantes, é claro, ainda permitem que você omita os operadores atômicos e apenas substituir conteúdo de qualquer maneira. Mas os avisos devem ser esperados.