In realtà, ma per il fatto che Mongoose sta effettivamente "ingannando" l'aggiornamento sotto le coperte, questa è in realtà l'azione predefinita del tuo invio a una normale funzione MongoDB.
Quindi Mongoose ritiene "saggio" come metodo di convenienza per "presumere" che intendessi emettere un $set
istruzioni qui. Dal momento che in realtà non vuoi farlo in questo caso, disattivi quel comportamento tramite { overwrite: true }
nelle opzioni passate a qualsiasi .update()
metodo:
Come esempio 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"
}
La visualizzazione del documento viene "sovrascritta" perché abbiamo soppresso il $set
operazione che altrimenti sarebbe stata interpolata. I due esempi vengono visualizzati prima senza overwrite
opzione, che applica il $set
modificatore, quindi "con" il overwrite
opzione, dove l'oggetto che hai passato per l'"aggiornamento" è rispettato e non tale $set
viene applicato il modificatore.
Nota, questo è il modo in cui il driver del nodo MongoDB esegue questa operazione "per impostazione predefinita". Quindi il comportamento di aggiungere il $set
"implicito". viene fatto dalla mangusta, a meno che tu non gli dica di non farlo.
NOTA Il vero modo per "sostituire" sarebbe in realtà usare replaceOne
, sia come metodo API di replaceOne()
o tramite bulkWrite()
. Il overwrite
è un'eredità di come Mongoose vuole applicare $set
come descritto e dimostrato sopra, tuttavia l'API ufficiale di MongoDB introduce replaceOne
come "speciale" re di update()
operazione che non consente l'utilizzo di operatori atomici come $set
all'interno dell'istruzione e si verificherà un errore se ci provi.
Questo è molto più chiaro semanticamente poiché sostituisci legge molto chiaramente per cosa viene effettivamente utilizzato il metodo. All'interno delle chiamate API standard a update()
le varianti ovviamente consentono ancora di omettere gli operatori atomici e si limiteranno a sostituire contenuto comunque. Ma dovrebbero essere previsti avvisi.