Javascript 如果更新时某个属性的值为null,则不应将该属性添加到记录中
假设我有这样一个猫鼬模式:Javascript 如果更新时某个属性的值为null,则不应将该属性添加到记录中,javascript,node.js,mongodb,mongoose,Javascript,Node.js,Mongodb,Mongoose,假设我有这样一个猫鼬模式: 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);
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);
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
);
};
我使用变量测试声明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"}); }
});
}
});
现在,我在节点路由器中使用此方法,如下所示:
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
}
你能看到昵称被设置为空吗?我不希望它像这样工作。我希望如果我的属性为null,那么该属性不应包含在记录中
如果您还记得的话,我已经在更新之前记录了updatedValues。(参见第二个有问题的代码块)。因此,以下是记录的值:
{
"_id" : ObjectId("ns8f9yyuo32hru0fu23oh"), //some automatically generated id
"name" : "firstTest"
}
我不知道为什么,但昵称不存在于记录的值中,更新后我得到昵称:null
。我认为,问题在于第二个代码块。你能检查一下吗
注意:
实际上,我的模式中的字段比我在讨论中指定的要多得多。一些字段也引用了其他记录。问题是,您仍然在文档上添加
昵称
属性-您只是将其设置为未定义
,但与您认为的相反,这与根本不设置它不一样
如果昵称属性未定义,则您希望根本不包括它,您可以这样做:
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);
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
);
};
看看你的代码,你的更新方法中有一个问题,它不能帮助你获得你想要的结果 这是您的更新代码:
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
是删除该字段的基础。在文档中所述的查询中使用$set
$set运算符用指定的值替换字段的值
因此,您强制将未定义的值设置为字段的null
。您可以针对模式中的昵称
字段设置required:true
,或者尝试传递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)
有关该方法的更多信息,请查看此链接。我不会这样写,但我会告诉您代码失败的原因 问题在于$set块 您选择专门为传入的更新对象设置值。如果值
未定义
,则强制mongo将其设置为null
问题出在这里
例如,以DB为单位:
{
“_id”:ObjectId(“ns8f9yyuo32hru0fu23oh”),
“名称”:“firstTest”,
“昵称”:“杰克”,
“_v”:0
}
如果您传入testToUpdate={name:'foo'}
您将得到
Test.update({…},{$set:{name:'foo',昵称:undefined}}
因为您从参数中获得了updatedValues.nickname
,而这是未定义的
你想要的是
Test.update({…},{$set:updatedValues}
翻译为Test.update({…},{$set:{name:'foo'}}
您不再为昵称提供密钥,因此不会将其设置为undefined/null
我会使用mongoose插件,不用担心手动将字段一直传递到您的模型 看 您可以定义可更新字段,然后只需
model.update(req.body)
不用担心这一切
即使你不想使用这个插件,你也可以这样做
Test.findbyiandupdate(id,{name,昵称},callback)
我发现的最简单的方法是使用lodash.pickby
只需传递body
(或任何相关对象)它删除了所有未定义的
和空的
字段和键正如其他答案中指出的那样,问题是你的$set
部分,但使用if条件并不是最好的方法。如果有多个字段,你可能需要多个if条件,甚至需要一个单独的函数来处理这个问题
Mongoose提供了一个很好的选项来处理这个问题:
{omitUndefined:1}
,它不会更新查询中所有未定义的值。您可以通过在update方法中将runValidators选项设置为true来防止在MongoDB中更新这些文档
例:
此外,还可以将选项“忽略未定义”设置为true,以防止未定义的值被反映
例:
实际上,我的模式中的字段比我在问题中指定的要多得多。一些字段也引用了其他记录。尽管如此,我还是尝试了您的答案,但在更新时出现了一个错误,即:强制转换错误:值“{………}”的强制转换到字符串失败,在花括号中显示我的对象。我的模式中还有一些层次结构对象,如下所示:详细信息:{mailing:{name:String,address:String},联系人:{mobile:String,email:email}。上面的对象显示为示例。我如何更新它?请忽略我的第一条注释,因为它被解决了,这是我的键入错误。@Vishal ok,那么这是否意味着我的答案有帮助?我不知道你的答案是否行得通,因为我得到了一个错误,即无法设置未定义的地址。实际上,我的架构中的字段比我在qu中指定的字段多得多估计。一些字段也引用了其他记录。不过我尝试了一下你的答案,更新时出现了一个错误,上面写着:Cast error:Cast to stri