问题
Let's say I have this schema:
var UserSchema = new Schema({
name : {
firstName : String,
lastName : String
}
});
And I create this user:
User.create({
name : {
firstName : Bob,
lastName : Marley
}
}, function (err, user) {
if(err) {
return console.error(err);
} else {
return console.log(user);
}
});
I noticed that if I do this:
User.findOneAndUpdate({_id: userId}, { name: { firstName : "Damian" } }, function (err, user) {
if(err) {
return console.error(err);
} else {
return console.log(user);
}
});
My user now is:
user = {
name : {
firstName : "Damian"
}
}
However, if I do this:
User.findOneAndUpdate({_id: userId}, { "name.firstName" : "Damian" }, function (err, user) {
if(err) {
return console.error(err);
} else {
return console.log(user);
}
});
My user is:
user = {
name : {
firstName : "Damian",
lastName : "Marley"
}
}
Is there a way to pass an Object with not all of its attributes filled out to findOneAndUpdate
, and keep the attributes that were there before, without getting rid of them? (same functionality as $set
in Mongo). This is really annoying...
回答1:
Flatten your incomplete nested object with flat, like this:
var flatten = require('flat')
flatten({
name : {
firstName : "Damian"
}
})
// {
// 'name.firstName': 'Damian'
// }
And now you can call findOneAndUpdate
exactly as you did in your second example.
回答2:
If you pass actual full nested object to findOneAndUpdate()
(or any other mongoose
update method):
Model.findOneAndUpdate({ ... }, {
name: {
firstName : "Damian"
}
})
You will overwrite the whole (nested) object, therefore removing all of its other properties from the document (in this case: lastName
).
To update only specific properties of the nested object, not the whole object, you need to use full path to the nested properties.
{ "name.firstName" : "Damian" }
(this will leave lastName
in the nested name
object untouched)
But it can be annoying to take care of that manually, whenever you update nested object's properties.
Luckily, it's not a problem at all to flatten the update object you pass to findOneAndUpdate()
- so it never ends up being more than one level deep.
There are many ways to do this, debated upon in this question: Fastest way to flatten / un-flatten nested JSON objects .
The quickest solution seems to be to use ready-made flat Node.js library.
var flatten = require('flat')
User.findOneAndUpdate(
{ _id: userId },
// same as passing '{ "name.firstName": "Damian" }':
flatten({
name: {
firstName : "Damian"
}
}), function (err, user) {
if(err) {
return console.error(err);
} else {
return console.log(user);
}
});
来源:https://stackoverflow.com/questions/30278445/findoneandupdate-overwriting-attributes-in-2-level-deep-object-passed-as-doc