Если значение свойства имеет значение 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);

Объявляю методы работы CRUD с помощью переменной Test. Из этого один такой метод является обновлением, которое определяется следующим образом:

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
    );

};

Теперь я использую этот метод внутри моего маршрутизатора node следующим образом:

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, это свойство не должно быть включено в запись.

Если вы помните, у меня консоль зарегистрировала обновленные версии перед ее обновлением. (см. второй блок кода). Итак, вот зарегистрированные значения:

{
    "_id" : ObjectId("ns8f9yyuo32hru0fu23oh"), //some automatically generated id 
    "name" : "firstTest"
}

Я не знаю почему, но nickName не присутствует в зарегистрированных значениях, а затем после обновления я получаю nickName: null. Я думаю, проблема заключается во втором блоке кода. Можете ли вы проверить его?

Примечание:

На самом деле у меня в моей схеме больше полей, чем указано в вопросе. Некоторые поля также относятся к другим записям.

Ответ 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)

Ответ 2

Проблема заключается в том, что вы все еще добавляете в документ свойство nickname - вы просто устанавливаете его на undefined, но вопреки тому, что вы считали не то же самое, что не устанавливаете его вообще.

То, что вы хотите, - это вообще не включать свойство 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
    );

};

Ответ 3

Взглянув на ваш код, в вашем методе обновления есть проблема, которая не поможет вам получить желаемый результат.

Это код обновления:

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 является основополагающим для удаления поля, если оно уже существует в вашем документе.

Ответ 4

Как вы используете $set в запросе, который указан в документации

Оператор $set заменяет значение поля указанным значением.

Итак, вы вынуждаете устанавливать значение undefined, которое 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 для получения дополнительной информации об этом подходе.