MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongoose überschreibt das Dokument anstatt `$set`-Felder

Eigentlich, aber für die Tatsache, dass Mongoose tatsächlich unter der Decke mit dem Update "herumspielt", ist dies tatsächlich die Standardaktion Ihrer Übermittlung an eine reguläre MongoDB-Funktion.

Mongoose hält es daher für „klug“, bequem davon auszugehen, dass Sie ein $set ausgeben wollten Anleitung hier. Da Sie das in diesem Fall eigentlich nicht wollen, schalten Sie dieses Verhalten per { overwrite: true } aus in den Optionen, die an .update() übergeben werden Methode:

Als vollständiges Beispiel:

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

})()

Erzeugt:

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

Das Anzeigen des Dokuments wird "überschrieben", da wir den $set unterdrückt haben Operation, die sonst interpoliert worden wäre. Die beiden Beispiele werden zuerst ohne overwrite angezeigt Option, die den $set anwendet Modifikator, und dann "mit" dem overwrite Option, bei der das Objekt, das Sie für das "Update" übergeben haben, berücksichtigt wird und kein solches $set Modifikator wird angewendet.

Beachten Sie, dass der MongoDB-Knotentreiber dies "standardmäßig" so macht. Also das Verhalten beim Hinzufügen des "impliziten" $set wird von Mongoose ausgeführt, es sei denn, Sie sagen ihm, dass er es nicht tun soll.

HINWEIS Der wahre Weg zum "Ersetzen" wäre tatsächlich die Verwendung von replaceOne , entweder als API-Methode von replaceOne() oder durch bulkWrite() . Das overwrite ist ein Erbe davon, wie Mongoose $set anwenden möchte wie oben beschrieben und demonstriert, jedoch führt die offizielle API von MongoDB replaceOne ein als "besonderes" König von update() Vorgang, der nicht erlaubt ist die Verwendung von atomaren Operatoren wie $set innerhalb der Anweisung und schlägt fehl, wenn Sie es versuchen.

Dies ist seit replace semantisch viel klarer liest sich sehr deutlich, wofür die Methode eigentlich verwendet wird. Innerhalb von Standard-API-Aufrufen an update() Varianten erlauben es Ihnen natürlich immer noch, die atomaren Operatoren wegzulassen und werden nur ersetzen Inhalt sowieso. Aber Warnungen sollten erwartet werden.