实际上,但事实上 mongoose 实际上是在“搞乱”更新,这实际上是您提交给常规 MongoDB 函数的默认操作。
所以 mongoose 认为它是“明智的”作为“假定”您打算在这里发出 $set 指令的便捷方法。由于在这种情况下您实际上不想这样做,因此您可以在传递给任何 .update() 方法的选项中通过 { overwrite: true } 关闭该行为:
作为一个完整的例子:
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 节点驱动程序“默认”执行此操作的方式。所以添加“隐式”$set 的行为是由猫鼬完成的,除非你告诉它不要这样做。
注意真正的“替换”方法实际上是使用replaceOne,或者作为replaceOne() 的API 方法,或者通过bulkWrite()。 overwrite 是 mongoose 希望如何应用 $set 的遗产,如上所述和演示,但是 MongoDB 官方 API 引入 replaceOne 作为update() 操作的“特殊”之王不允许在语句中使用 $set 等原子运算符,如果尝试会出错。
这在语义上更加清晰,因为 replace 非常清楚地了解该方法的实际用途。在对update() 变体的标准API 调用中,当然仍然允许您省略原子操作符,并且无论如何都会替换 内容。但应该会收到警告。