Mongoose findByIdAndUpdate() with select option [duplicate] - node.js

Using Mongoose in Nodejs you can return some fields using find.
eg.
User.findOne({_id:'132324'}, {first_name:1, last_name:1}).exec...
but I can't seem to figure out how to return certain fields using findOneAndUpdate.
User.findOneAndUpdate({_id:'132324'}, {$set : {..bla bla}}, {first_name:1, last_name:1}).exec....
Has anyone achieved this before?
I can't find it in the documentation.

From the manual, the options argument needs a "fields" key in it since there are other details such as "upsert" and "new" where this applies. In your case you also want the "new" option:
User.findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{
"fields": { "first_name":1, "last_name": 1 },
"new": true
}
).exec(...)
Alternately you may use .select()
User.select({ "first_name": 1, "last_name": 1 }).findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{ "new": true }
).exec(...)
Noting that without "new": true the document returned is in the state before the modification of the update was processed. Some times this is what you mean, but most of the time you really want the modified document.

It seems the syntax for findByIdAndUpdate has changed a little.
Its takes the form of Model.findByIdAndUpdate(query, update, options, (optional callback))
According to the docs, to specify which fields are returned you have to specify them in the options parameter. so, using the above example it would be:
User.findOneAndUpdate(
{id}, //query
{ $set: { "fieldToBeChanged": "update" } }, //update
{new:true, select: "fieldIWant anotherFieldIWant"}, //options
})
The new:true options is not necessary. If omitted mongoose defaults to returning the document before the updates were applied. Set to true and it will return the document after the updates have been made.

We can exclude any field while using mongoose update function findByIdAndUpdate with the help of select function,please have a look at the following code it will exclude password and __v from the Update response
User.findByIdAndUpdate({ _id: req.userData.UserId },req.body,{new: true}).select({Password: 0, __v: 0 })
.exec()
.then(result => {})

Try this:
User.findOneAndUpdate({_id:'132324'}, {update: '1235'}, {new: true}).select({ _id: 0 })
The select() will exclude the _id field from the result.

For MongoDb version 3.2+
User.findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{
"projected": { "first_name":1, "last_name": 1 },
"returnNewDocument": true
}
)...

For MongoDB version: 4.0.2, mongoose version: 5.13.7:
userModel.findOneAndUpdate({_id:'132324'},{$set:
{"first_name":"user","last_name":"one"}},
{new:true,select:{first_name:1, last_name:1}},(err,data)=>{}

Use option {new: true} in findOneAndUpdate (default: false)

Related

using updateOne inside a map, setting the filter to the unique id , $set is to the document and upsert is true. Even then new document is created

here is my code in the controller from where i am getting the records from my google calendar API and then passing that data to this function and the code inside the function which inserts the document (records) looks like this as below:
Holiday.bulkWrite(
holidays.map((holiday) => ({
updateOne: {
filter: { holidayId: holiday.id },
update: { $set: holiday },
upsert: true,
},
}))
)
It's hard to tell what exactly the issue is because it is not Mongo related but code related, from what it seems you are just using the wrong field for the filter.
holiday.id is null, and we can see that the "inserted" documents do not have such field. You are basically executing the following update:
db.collection.update({
holidayId: null
},
{
"$set": {
holidayId: "123"
... other fields
}
},
{
"upsert": true
})
I believe this simple fix would solve your issue, change .id To .holidayId:
Holiday.bulkWrite(
holidays.map((holiday) => ({
updateOne: {
filter: { holidayId: holiday.holidayId },
update: { $set: holiday },
upsert: true,
},
}))
)

Updating an array of objects iteratively in mongodb nodejs

I am trying to update a couple of objects in the MongoDB in nodejs. Here's the problem I'm having: I have an array of objects kind of looks like this:
[{
"_id": {
"$oid": "5ed611265828aa77c978afb4"
},
"advert_id": "5ec2e4a8bda562e21b8c5052",
"isCategory": false,
"name": "2+1 Ev Taşıma",
"value": "2200 TL"
},
{
"_id": "40", // this object is recently added so it doesn't
// have an oid it needs to be inserted instead of updating.
"advert_id": "5ec2e4a8bda562e21b8c5052",
"isCategory": false,
"name": "4+1 Ev Taşıma",
"value": "7000 TL"
},
]
I'm trying to update every one of these objects using collection.updateMany with upsert: true
Here's the code I wrote:
mongodb.collection('prices').updateMany({}, {$set: post.prices}, {upsert: true}, (error, result) => {
if(error) throw error;
res.json(response);
})
Here's the error:
MongoError: Modifiers operate on fields but we found type array instead. For example: {$mod: {<field>: ...}} not
{$set: [ { _id: "5ed611265828aa77c978afb4", advert_id: "5ec2e4a8bda562e21b8c5052", isCategory: false, name: "2+1
Ev Taşıma", value: "2000 TL", isVisible: false } ]}
The problem seems that I'm trying to pass prices array directly into the $set pipe. What's a field type and how can I translate my array into that.
Appreciate the help, had some difficult time searching through the documentation but couldn't find any related chapter. I can really appreciate it if you can also link the documentation's related part in your answer.
Database document:
{
"_id": {
"$oid": "5ed611265828aa77c978afb4"
},
"advert_id": "5ec2e4a8bda562e21b8c5052",
"isCategory": false,
"name": "2+1 Ev Taşıma",
"value": "2000 TL",
"isVisible": false
}
I see a problem here {$set: post.prices}
It should be {$set: {'prices':post.prices}} - You should provide a document to $set
Refer
db.col.find({
'_id.$oid': '5ed611265828aa77c978afb4'
},
{
$set: {
"advert_id": "5ec2e4a8bda562e21b8c5052",
"isCategory": false
},
{
upsert: true
}
}

mongodb remove document if array count zero after $pull in a single query

I have a requirement where my comments schema looks like the following
{
"_id": 1,
"comments": [
{ "userId": "123", "comment": "nice" },
{ "userId": "124", "comment": "super"}
]
}
I would like to pull the elements based on the userId field.
I am doing the following query
comments.update({},{$pull:{comments:{userId:"123"}}})
My requirement is that if the array length became zero after the pull operator I need to remove the entire document for some reason.Is there a away to do this in a single query?
PS:I am using the mongodb driver.Not the mongoose
If I'm reading your question right, after the $pull, if the comments array is empty (zero length), then remove the document ({ _id: '', comments: [] }).
This should remove all documents where the comments array exists and is empty:
comments.remove({ comments: { $exists: true, $size: 0 } })
I had a similar requirement and used this (using mongoose though):
await Attributes.update({}, { $pull: { values: { id: { $in: valueIds } } } }, { multi: true })
await Attributes.remove({ values: { $exists: true, $size: 0 } })
Not sure if it's possible to do this in one operation or not.
You can use middlewares for this.
http://mongoosejs.com/docs/middleware.html
Write a pre/post update method in mongodb to check your condition.

Mongoose select fields to return from findOneAndUpdate

Using Mongoose in Nodejs you can return some fields using find.
eg.
User.findOne({_id:'132324'}, {first_name:1, last_name:1}).exec...
but I can't seem to figure out how to return certain fields using findOneAndUpdate.
User.findOneAndUpdate({_id:'132324'}, {$set : {..bla bla}}, {first_name:1, last_name:1}).exec....
Has anyone achieved this before?
I can't find it in the documentation.
From the manual, the options argument needs a "fields" key in it since there are other details such as "upsert" and "new" where this applies. In your case you also want the "new" option:
User.findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{
"fields": { "first_name":1, "last_name": 1 },
"new": true
}
).exec(...)
Alternately you may use .select()
User.select({ "first_name": 1, "last_name": 1 }).findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{ "new": true }
).exec(...)
Noting that without "new": true the document returned is in the state before the modification of the update was processed. Some times this is what you mean, but most of the time you really want the modified document.
It seems the syntax for findByIdAndUpdate has changed a little.
Its takes the form of Model.findByIdAndUpdate(query, update, options, (optional callback))
According to the docs, to specify which fields are returned you have to specify them in the options parameter. so, using the above example it would be:
User.findOneAndUpdate(
{id}, //query
{ $set: { "fieldToBeChanged": "update" } }, //update
{new:true, select: "fieldIWant anotherFieldIWant"}, //options
})
The new:true options is not necessary. If omitted mongoose defaults to returning the document before the updates were applied. Set to true and it will return the document after the updates have been made.
We can exclude any field while using mongoose update function findByIdAndUpdate with the help of select function,please have a look at the following code it will exclude password and __v from the Update response
User.findByIdAndUpdate({ _id: req.userData.UserId },req.body,{new: true}).select({Password: 0, __v: 0 })
.exec()
.then(result => {})
Try this:
User.findOneAndUpdate({_id:'132324'}, {update: '1235'}, {new: true}).select({ _id: 0 })
The select() will exclude the _id field from the result.
For MongoDb version 3.2+
User.findOneAndUpdate(
{ "_id": "132324" },
{ "$set": { "hair_color": "yellow" } },
{
"projected": { "first_name":1, "last_name": 1 },
"returnNewDocument": true
}
)...
For MongoDB version: 4.0.2, mongoose version: 5.13.7:
userModel.findOneAndUpdate({_id:'132324'},{$set:
{"first_name":"user","last_name":"one"}},
{new:true,select:{first_name:1, last_name:1}},(err,data)=>{}
Use option {new: true} in findOneAndUpdate (default: false)

How to keep an array member count

I'm using nodeJS + Express + Mongoose + mongoDB
Here's my mongoDB User Schema:
{
friends: [ObjectId]
friends_count: Number
}
Whenever user adds a friend, a friendId will be pushed into friends array, and friends_count will be increased by 1.
Maybe there are a lot of actions will change the friends array, and maybe I will forgot to increase the friends_count. So I want to make sure that friends_count always equal to friends.length
Is there a good way or framework to make sure all of that?
P.S
I know how to update friends_count. What I mean is what if I forgot to?
Is there a way to automatically keep these two attributes sync?
Use the $ne operator as a "query" argument to .update() and the $inc operator to apply when that "friend" did not exist within the array as you $push the new member:
User.update(
{ "_id": docId, "friends": { "$ne": friendId } },
{
"$push": { "friends": friendId },
"$inc": { "friends_count": 1 }
},
function(err,numberAffected) {
}
)
Or to "remove" a friend from the list, do the reverse case with $pull:
User.update(
{ "_id": docId, "friends": friendId },
{
"$pull": { "friends": friendId },
"$inc": { "friends_count": -1 }
},
function(err,numberAffected) {
}
)
That way your friends_count stays in sync with the number of array elements present.
All you need to do is to update friends_count in both add and remove functions. For example:
User.findById(userId, function (err, user) {
if (user) {
user.friends.push(friendId);
user.friends_count++;
user.save();
}
});
FYI, I don't think it is necessary to add friends_count while you can get total numbers of friends by friends.length.

Resources