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