Mongoose 覆盖文档而不是 `$set` 字段
Posted
技术标签:
【中文标题】Mongoose 覆盖文档而不是 `$set` 字段【英文标题】:Mongoose overwrite the document rather that `$set` fields 【发布时间】:2017-12-24 05:17:44 【问题描述】:说,我有一个文件:
_id: 'some_mongodb_id',
name: 'john doe',
phone: '+12345678901',
我想更新这个文档:
.findOneAndUpdate(_id: 'some_mongodb_id', name: 'Dan smith')
结果应该是这样的:
_id: 'some_mongodb_id',
name: 'Dan smith',
应删除未指定的属性。
我该怎么做?
【问题讨论】:
您是否尝试使用更新功能(而不是 findOneAndUpdate)? @israel.zinc 我有,没区别 @israel.zinc 这不会有什么不同。 “猫鼬”通过将$set
添加到“更新对象”中来执行此操作,其中标准驱动程序将“按原样”传递该对象。因此,对于 mongoose,需要“关闭”这种行为,以使其像您实际要求的那样工作。
【参考方案1】:
实际上,但事实上 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 调用中,当然仍然允许您省略原子操作符,并且无论如何都会替换 内容。但应该会收到警告。
【讨论】:
非常感谢,这个覆盖功能完全没有记录:-( 我收到了这个错误:没有重载匹配这个调用。最后一个重载给出了以下错误。 ' upsert: true; 类型的参数覆盖:布尔值; ' 不可分配给“QueryFindOneAndUpdateOptions”类型的参数。对象字面量只能指定已知属性,而 'QueryFindOneAndUpdateOptions'.ts(2769) 类型中不存在 'overwrite' 【参考方案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);
);
);
【讨论】:
您可以使用选项 new:true 返回更新后的文档。以上是关于Mongoose 覆盖文档而不是 `$set` 字段的主要内容,如果未能解决你的问题,请参考以下文章