Всъщност, с изключение на факта, че mongoose всъщност "забърква" с актуализацията под завивките, това всъщност е действието по подразбиране при изпращането ви на обикновена функция на MongoDB.
Така че mongoose смята, че е "мъдро" като удобен метод да "предполагаме", че сте имали предвид да издадете $set инструкция тук. Тъй като всъщност не искате да правите това в този случай, изключвате това поведение чрез { overwrite: true } в опциите, предадени на всеки .update() метод:
Като пълен пример:
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();
}
})()
Произвежда:
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"
}
Показването на документа е "презаписано", защото потиснахме $set операция, която иначе би била интерполирана. Двете проби се показват първи без overwrite опция, която прилага $set модификатор, а след това "с" overwrite опция, където обектът, който сте предали за "актуализация", се спазва и няма такъв $set се прилага модификатор.
Забележете, така драйверът на MongoDB Node прави това "по подразбиране". Така че поведението на добавяне в "неявния" $set се извършва от мангуста, освен ако не му кажете да не го прави.
ЗАБЕЛЕЖКА Истинският начин за „замяна“ всъщност би бил да използвате
replaceOne, или като API метод наreplaceOne()или чрезbulkWrite().overwriteе наследство на начина, по който mongoose иска да приложи$setкакто е описано и демонстрирано по-горе, обаче официалният API на MongoDB въвеждаreplaceOneкато „специален“ крал наupdate()операция, коятоне позволявата използването на атомни оператори като$setв изявлението и ще грешка, ако опитате.Това е много по-ясно семантично, тъй като замени чете много ясно за какво всъщност се използва методът. В рамките на стандартните API извиквания към
update()вариантите, разбира се, все още ви позволяват да пропуснете атомните оператори и просто ще заменят съдържание така или иначе. Но трябва да се очакват предупреждения.