I was looking on stack overflow on how to retrieve an array element and I found so by looking at this link here:
Retrieve only the queried element in an object array in MongoDB collection
Below is the query I created from following this question:
exports.confirmFriend = function(req, res){
if(req.body._id) {delete req.body._id}
User.findOne({'username': req.params.username}, {'friends': {$elemMatch: {'username': req.body.username}}}, function (err, user){
if(err) { return handleError(res, err); }
if(!user) { return res.send(404); }
//This is because $elemMatch returns only the first element so only 1 element will be populated
user.friends[0] = req.body;
user.save();
//Here I want to UPDATE or SAVE the document that I queried for now
});
Now that I have queried for my user.friends element and I have assigned req.body to it properly with the correct contents, I was wondering, is there a way to update or save the document I have performed this assignment/query on? when I call user.save()
at the end of the assignment, I get an error saying: TypeError: Object #<Object> has no method 'save'
. Any ideas on how to fix this? Thanks!
Yes, so this is no longer a mongoose document. It cannot be one because you use "projection" and that means that not necessarily all of the fields are returned. In fact that are not as you only return the "friends" array out of all the possible fields.
This means that .save()
would be completely invalid even if this was a mongoose document, since what would happen if you tried to "cast" is that you end up overwriting all of the existing document content with just one field.
So your approaches here boil down to:
Don't use projection and identify the array element in code the use .save()
User.findOne({'username': req.params.username},function(err,user) {
user.friends.map(function(friend) {
if (friend.username == req.body.username)
friend = req.body;
return friend;
});
user.save(function(err,user) {
// do whatever
});
});
Just to this with an atomic update to modify the document. Probably much safer anyway as you cannot guarantee something else has not modified the document since you read it, but of course removes all mongoose validation and hooks:
User.findOneAndUpdate(
{
"username": req.params.username,
"friends.username": req.body.username
},
{
"$set": { "friends.$": req.body }
},
function(err,user) {
// do something with the now modified user
}
);
So realize that there are certain situations where it is not possible to return a full document object in the response and you would otherwise need to manually cast as one, but then it would not likely be the same as the source.
The second form basically exists to make sure that no changes are issued between the two operations. It's generally better even if you need to implement some other logic, but also better if you design around not needing to have other calculations and hooks on the model.