说,我有一份文件:
{
_id: 'some_mongodb_id',
name: 'john doe',
phone: '+12345678901',
}
我想更新此文档:
.findOneAndUpdate({_id: 'some_mongodb_id'}, {name: 'Dan smith'})
结果应该是这样的:
{
_id: 'some_mongodb_id',
name: 'Dan smith',
}
应删除未指定的属性。
我该怎么做?
答案 0 :(得分:8)
实际上,但是由于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节点驱动程序“默认”执行此操作的方式。因此,添加“隐式”$set
的行为是由mongoose完成的,除非你告诉它不要。
注意“替换”的真正方法实际上是使用
replaceOne
,作为replaceOne()
的API方法或通过bulkWrite()
。overwrite
是mongoose如何应用$set
的遗产,正如上面所描述和演示的那样,但MongoDB官方API引入replaceOne
作为“特殊”王{strong}这在语义上更清晰,因为替换非常清楚地读取了实际使用的方法。在对
update()
变体的标准API调用中,当然仍允许您省略原子操作符,并且无论如何都只是替换内容。但是应该预料到警告。
答案 1 :(得分:2)
您可以传递upsert
选项,它将替换文档:
var collection = db.collection('test');
collection.findOneAndUpdate(
{'_id': 'some_mongodb_id'},
{name: 'Dan smith Only'},
{upsert: true},
function (err, doc) {
console.log(doc);
}
);
但问题在于 - 回调中的doc
是找到文档但未更新。
因此,您需要执行以下操作:
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);
});
}
);