17 votes

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

Disons que j'ai un document :

{
  _id: 'some_mongodb_id',
  name: 'john doe',
  phone: '+12345678901',
}

Je veux mettre à jour ce document :

.findOneAndUpdate({_id: 'some_mongodb_id'}, {name: 'Dan smith'})

Et le résultat devrait être le suivant :

{
  _id: 'some_mongodb_id',
  name: 'Dan smith',
}

La propriété, qui n'est pas spécifiée, doit être supprimée.

Comment je fais ça ?

29voto

Neil Lunn Points 1

En fait, à l'exception du fait que la mangouste "s'amuse" en secret avec la mise à jour, il s'agit en fait de l'action par défaut de votre soumission à une fonction MongoDB normale.

Ainsi, la mangouste estime qu'il est "sage", en tant que 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 pouvez désactiver ce comportement via { overwrite: true } dans les options passées à tout .update() méthode :

A 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é" parce que nous avons supprimé le $set qui, sinon, aurait été interpolée. Les deux échantillons montrent le premier sans le overwrite qui applique l'option $set et ensuite "avec" le modificateur overwrite où l'objet que vous avez transmis pour la "mise à jour" est respecté et aucun objet de ce type n'est utilisé. $set est appliqué.

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


NOTE La véritable façon de "remplacer" serait en fait d'utiliser replaceOne soit en tant que méthode API de replaceOne() ou par bulkWrite() . Le site overwrite est un héritage de la façon dont la mangouste veut appliquer $set tel que 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 à l'intérieur de la déclaration et vous obtiendrez une erreur si vous essayez.

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

3voto

Vladimir Points 1

Vous pouvez passer upsert et il remplacera le document :

var collection = db.collection('test');
collection.findOneAndUpdate(
  {'_id': 'some_mongodb_id'},
  {name: 'Dan smith Only'},
  {upsert: true},
  function (err, doc) {
    console.log(doc);
  }
);

Mais le problème ici - est que doc dans le callback est un document trouvé mais non mis à jour. Vous devez donc effectuer quelque chose comme ceci :

var collection = db.collection('test');
collection.update(
  {'_id': 'some_mongodb_id'},
  {name: 'Dan smith Only'},
  {upsert: true},
  function (err, doc) {
    collection.findOne({'_id': 'some_mongodb_id'}, function (err, doc) {
        console.log(doc);
    });
  }
);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X