MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Mongoose écrase le document plutôt que les champs `$set`

En fait, mais pour le fait que la mangouste est en train de "déconner" la mise à jour sous les couvertures, c'est en fait l'action par défaut de votre soumission à une fonction MongoDB régulière.

Donc, mangouste juge "sage" comme méthode pratique de "présumer" que vous vouliez émettre un $set instruction ici. Puisque vous ne voulez pas faire cela dans ce cas, vous désactivez ce comportement via { overwrite: true } dans les options passées à n'importe quel .update() méthode :

À titre d'exemple complet :

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

})()

Produit :

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"
}

Montrer que le document est "écrasé" car nous avons supprimé le $set opération qui, autrement, aurait été interpolée. Les deux exemples s'affichent en premier sans le overwrite option, qui applique le $set modificateur, puis "avec" le overwrite option, où l'objet que vous avez passé pour la "mise à jour" est respecté et aucun $set modificateur est appliqué.

Notez que c'est ainsi que le pilote MongoDB Node le fait "par défaut". Ainsi, le comportement d'ajout dans le $set "implicite" est fait par la mangouste, sauf si vous lui dites de ne pas le faire.

REMARQUE La vraie façon de "remplacer" serait en fait d'utiliser replaceOne , soit comme méthode API de replaceOne() ou via bulkWrite() . Le overwrite est un héritage de la façon dont la mangouste veut appliquer $set comme décrit et démontré ci-dessus, cependant l'API officielle de MongoDB introduit replaceOne en tant que "spécial" roi de update() opération qui ne permet pas l'utilisation d'opérateurs atomiques comme $set dans la déclaration et vous obtiendrez une erreur si vous essayez.

C'est beaucoup plus clair sémantiquement puisque remplacer indique très clairement à quoi sert réellement la méthode. Dans les appels d'API standard à update() les variantes vous permettent bien sûr toujours d'omettre les opérateurs atomiques et ne feront que remplacer contenu de toute façon. Mais il faut s'attendre à des avertissements.