Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/43.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如果更新时某个属性的值为null,则不应将该属性添加到记录中_Javascript_Node.js_Mongodb_Mongoose - Fatal编程技术网

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