假设我有一个像这样的猫鼬模式:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var testSchema = new Schema({
name: {type: String, required: true},
nickName: {type: String}
});
var Test = module.exports = mongoose.model('Test', testSchema);
我使用变量Test声明了CRUD操作的方法。从那一个这样的方法是更新,其定义如下:
module.exports.updateTest = function(updatedValues, callback) {
console.log(updatedValues); //this output is shown below
Test.update(
{ "_id": updatedValues.id },
{ "$set" : { "name" : updatedValues.name, "nickName" : updatedValues.nickName } },
{ multi: false },
callback
);
};
现在,我在节点路由器中使用此方法,如下所示:
router.put('/:id', function(req, res, next) {
var id = req.params.id,
var name = req.body.name,
var nickName = req.body.nickName
req.checkBody("name", "Name is required").notEmpty();
var errors = req.validationErrors();
if(errors) { ........ }
else {
var testToUpdate = new Test({
_id: id,
name: name,
nickName: nickName || undefined
});
Test.updateTest(testToUpdate, function(err, result) {
if(err) { throw(err); }
else { res.status(200).json({"success": "Test updated successfully"}); }
});
}
});
现在,如果我在数据库中保存一条新记录然后在数据库中看到它,那么它看起来像是:
{
"_id" : ObjectId("ns8f9yyuo32hru0fu23oh"), //some automatically generated id
"name" : "firstTest",
"__v" : 0
}
现在,如果我在不更改任何内容的情况下更新同一文档,那么如果我查看数据库中的相同记录,那么我得到:
{
"_id" : ObjectId("ns8f9yyuo32hru0fu23oh"), //some automatically generated id
"name" : "firstTest",
"__v" : 0,
"nickName" : null
}
你能看到nickName被设置为null吗?我不希望它像这样工作。我希望如果我的属性为null,那么该属性不应该包含在记录中。
如果你还记得,我有控制台在更新之前记录了updatedValues。 (参见第二个代码块)。所以,这是记录的值:
{
"_id" : ObjectId("ns8f9yyuo32hru0fu23oh"), //some automatically generated id
"name" : "firstTest"
}
我不知道为什么,但是在记录的值中没有nickName,然后在更新后我得到nickName: null
。我认为,问题在于第二个代码块。你能检查一下吗?
注意:
实际上我的架构中的字段比我指定的字段多得多。某些字段也引用其他记录。
答案 0 :(得分:2)
您可以通过在 update 方法中将 runValidators 选项设置为 true 来阻止此类文档在 MongoDB 中更新。
例如:
module.exports.updateTest = function(updatedValues, callback) {
Test.update(
{ "_id": updatedValues.id },
{ "$set" : {
"name" : updatedValues.name, "nickName" : updatedValues.nickName } ,
},
{ multi: false, runValidators: true },
callback
);
};
此外,您还可以将选项 omitUndefined 设置为 true 以防止未定义的值被反映。
例如:
module.exports.updateTest = function(updatedValues, callback) {
Test.update(
{ "_id": updatedValues.id },
{ "$set" : {
"name" : updatedValues.name, "nickName" : updatedValues.nickName } ,
},
{ multi: false, runValidators: true, omitUndefined: true },
callback
);
};
答案 1 :(得分:1)
查看您的代码,您的更新方法中存在一个问题,它无法帮助您获得所需的结果。
这是您更新代码:
module.exports.updateTest = function(updatedValues, callback) {
console.log(updatedValues); //this output is shown below
Test.update(
{ "_id": updatedValues.id },
{ "$set" : { "name" : updatedValues.name, "nickName" : updatedValues.nickName } },
{ multi: false },
callback
);
};
问题出在$set
部分。
在mongodb中,您只需为其分配undefined
即可取消设置文档中的字段。您应该使用$unset
运算符。
所以你的更新代码应该是这样的:
module.exports.updateTest = function(updatedValues, callback) {
console.log(updatedValues); //this output is shown below
const operators = {$set: {name: updatedValues.name}};
if (updatedValues.nickName) {
operators.$set.nickName = updatedValues.nickName;
} else {
operators.$unset = {nickName: 1};
}
Test.update(
{ "_id": updatedValues.id },
operators,
{ multi: false },
callback
);
};
请注意,如果字段中已存在该字段,则使用$unset
是删除字段的基础。
答案 2 :(得分:1)
我不会这样写,但我会告诉你代码失败的原因。
问题是你的$ set block
您选择将值专门设置为传入的更新对象。如果值为undefined
,则强制mongo将其设置为null。
这是问题
例如,在DB中:
{
"_id" : ObjectId("ns8f9yyuo32hru0fu23oh"),
"name" : "firstTest",
"nickname": "jack",
"__v" : 0
}
如果你传入testToUpdate = { name: 'foo' }
,你最终会得到
Test.update({ ... }, { $set: { name: 'foo', nickname: undefined }}
因为您从参数中获取updatedValues.nickname
并且没有定义
Test.update({ ... }, { $set: updatedValues }
,转换为Test.update({ ... }, { $set: { name: 'foo' } }
。
您不再为昵称提供密钥,因此不会将其设置为undefined / null。
我会使用mongoose插件而不用担心手动将字段一直传递给模型
请参阅https://github.com/autolotto/mongoose-model-update
您可以定义可更新字段,然后就可以执行
model.update(req.body)
并且不用担心这一切
即使您不想使用该插件,您仍然可以执行
Test.findByIdAndUpdate(id, { name, nickname }, callback)
答案 3 :(得分:1)
正如其他答案中所指出的那样,问题确实是您的 $set
部分,但是使用 if 条件并不是做到这一点的最佳方法。如果有多个字段,您可能需要多个 if 条件,甚至需要一个单独的函数来处理这个问题。
Mongoose 提供了一个非常好的解决方案:{omitUndefined: 1}
,它不会更新查询中的所有未定义值。
答案 4 :(得分:0)
问题是您仍在文档中添加nickname
属性 - 您只需将其设置为undefined
,但与您认为的相反,不是同样没有设置它。
如果未定义昵称属性,则根本不包括昵称属性,您可以这样做:
module.exports.updateTest = function(updatedValues, callback) {
const set = { "name": updatedValues.name };
if (updatedValues.nickName) {
set["nickName"] = updatedValues.nickName;
}
Test.update({ "_id": updatedValues.id }, { "$set": set}, { multi: false },
callback
);
};
答案 5 :(得分:0)
正如您在文档
中所述的查询中使用$set
一样
$ set运算符用指定的值替换字段的值。
因此,您强制将此处的null
未定义值设置为字段。您可以将required : true
设置为架构中的nickName
字段,或尝试传递JS对象而不是编写原始查询,如:
而不是:
Test.update(
{ "_id": updatedValues.id },
{ "$set" : { "name" : updatedValues.name, "nickName" : updatedValues.nickName } },
{ multi: false },
callback
);
尝试做:
var data = { name : updatedValues.name };
if(updatedValues.nickName){
data.nickName = updatedValues.nickName;
}
Model.update({ "_id": updatedValues.id }, data ,options, callback)
检查Mongoose documentation中的此链接,了解有关该方法的更多信息。
答案 6 :(得分:0)
我找到的最简单的方法是使用lodash.pickby
,您只需传递body
(或与此相关的任何对象),它将删除所有的undefined
和null
字段和键