How to avoid multiple finds of same document in mongoose? - node.js

I'm new to mongoose so please forgive me if this sounds stupid.
For every edit, I want to store the historic values and also modify the existing value of my collection.
So here's the code
collection.findById(type_id).select({ "history": 0 })
.then(function(data){
var changes = { data : data, by : user_id }
return collection.findOneAndUpdateAsync({_id:type_id},
{$push: {"history": changes }})
})
.then(function(data){
return collection.findOneAndUpdateAsync({_id:type_id}, info)
})
.then(function(res){
resolve(res) })
This piece of code works just fine, but I don't want to do multiple find for the same collection.
Would be great if you can suggest something better and efficient.
Thanks in advance. :)

I don't think all that's possible within an atomic update. The best you can do is reduce the number of calls to the server down to two: your first call is the update using findByIdAndUpdate() with the new option set to false (default), this will allow you to access
the data before the update. You can then use the second call to update history with this data. For example:
collection.findByIdAndUpdateAsync(type_id, info /*, { "new": false } */)
.then(function(data){
var changes = {
"data": data,
"by": user_id
};
return collection.findByIdAndUpdateAsync(
type_id,
{ "$push": { "history": changes } },
{ "new": true }
);
})
.then(function(res){ resolve(res) });

Related

Mongoose subdocument update: dot notation assignment OR .findByIdAndUpdate() [duplicate]

I am using .pull to remove a record from an array in mongo db and it works fine, but a comment I read somewhere on stack overflow (can't find it again to post the link) is bothering me in that it commented that it was bad to use .save instead of using .findByIdAndUpdate or .updateOne
I just wanted to find out if this is accurate or subjective.
This is how I am doing it currently. I check if the product with that id actually exists, and if so I pull that record from the array.
exports.deleteImg = (req, res, next) => {
const productId = req.params.productId;
const imgId = req.params.imgId;
Product.findById(productId)
.then(product => {
if (!product) {
return res.status(500).json({ message: "Product not found" });
} else {
product.images.pull(imgId);
product.save()
.then(response => {
return res.status(200).json( { message: 'Image deleted'} );
})
}
})
.catch(err => {
console.log(err);
});
};
I think what they were saying though was it should rather be done something like this (an example I found after a google)
users.findByIdAndUpdate(userID,
{$pull: {friends: friend}},
{safe: true, upsert: true},
function(err, doc) {
if(err){
console.log(err);
}else{
//do stuff
}
}
);
The main difference is that when you use findById and save, you first get the object from MongoDB and then update whatever you want to and then save. This is ok when you don't need to worry about parallelism or multiple queries to the same object.
findByIdAndUpdate is atomic. When you execute this multiple times, MongoDB will take care of the parallelism for you. Folllowing your example, if two requests are made at the same time on the same object, passing { $pull: { friends: friendId } }, the result will be the expected: only one friend will be pulled from the array.
But let's say you've a counter on the object, like friendsTotal with starting value at 0. And you hit the endpoint that must increase the counter by one twice, for the same object.
If you use findById, then increase and then save, you'd have some problems because you are setting the whole value. So, you first get the object, increase to 1, and update. But the other request did the same. You'll end up with friendsTotal = 1.
With findByIdAndUpdate you could use { $inc: { friendsTotal: 1 } }. So, even if you execute this query twice, on the same time, on the same object, you would end up with friendsTotal = 2, because MongoDB use these update operators to better handle parallelism, data locking and more.
See more about $inc here: https://docs.mongodb.com/manual/reference/operator/update/inc/

How can i customize a field when i am making a pull request in Mongodb

Below is a code that deletes a sub document inside roomTypes field and it works fine. The problem is that i want to reuse the code instead of writting one. This because apart from having a field roomTypes with sub documents i also have other fields with sub documents. so i would like to write one code that i can use to update them.
So under the field that is being pulled is there way i can use something like a template string the way i have done for { _id: ${req.params.position} } so that i can update a field based on what is coming in the request body. In short i do not want to hard code the roomTypes field. I want it to be dynamic based on what is coming from the request body. Please help on this
exports.deleteRoomType = catchAsync(async (req, res, next) => {
const reqHostel = await Hostel.findByIdAndUpdate(req.params.id, {
$pull: {
roomTypes: { _id: `${req.params.position}` }
}
});
// process.exit();
res.status(204).json({
status: 'success',
data: null
});
});
You can pass another paramer fieldName and build your update expression based on that field's value:
const reqHostel = await Hostel.findByIdAndUpdate(req.params.id, {
$pull: {
[req.params.fieldName]: { _id: `${req.params.position}` }
}
})

Find specific data in MongoDB via NodeJS

I need to find specific data in my MongoDB for the rest API but it's returning null.
app.get('/api/v1/champions/:name', (req, res) => {
db.find({"champions": req.params.name}, (err, champion) => {
res.json(err)
})
})
Here is my MongoDB Schema:
champions: {
champ_name_1: {
dmg: Number,
cost: Number
},
champ_name_2: {
....
}
}
Since you are checking to see if a key exists in the champions object you'll need to write the query differently.
If your data was formatted like this then your query would work. (champions is a String)
{
"champions": "champ_name_1",
"dmg": 123,
"cost": 123
}
To check if a key exists in an object in mongo, use a query like this.
const champKey = 'champions.' + req.params.name;
db.find({ [champKey]: { $exists: true } });
https://docs.mongodb.com/manual/reference/operator/query/exists/
you can use mongoose package
here
and simply use the mongoose's find() methond to find particular data as providing to it.
For Example: find({_id: MY_ID_HERE});

Mongoose updateOne with parameter {new:true} not showing actual updated value

I am struggling for a couple of hours to show the final value of an updated document (via mongoose updateOne). I successfully modify it as I can see "nModified: 1" when I call the endpoint on Postman, but I am not able to output the actual final document - even when using the parameter {new:true}
This is the code for the route:
// 3. We check if blockid is in this project
Block.findById(req.params.blockid)
.then(block => {
if (!block) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
// 4. We found the block, so we modify it
Block.updateOne(
{ _id: req.params.blockid },
{ $set: blockFields }, // data to be updated
{ new: true }, // flag to show the new updated document
(err, block) => {
if (err) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
console.log(block);
res.json(block);
}
);
})
.catch(err => console.error(err));
Instead, this is the output I am getting (Mongoose is on debug mode)
Any ideas?
Many thanks
{ new : true } will return the modified document rather than the original. updateOne doesn't have this option. If you need response as updated document use findOneAndUpdate.
Below are the mongoosejs function where you can use { new : true }
findByIdAndUpdate()
findOneAndUpdate()
findOneAndDelete()
findOneAndRemove()
findOneAndReplace()
Thank you #sivasankar for the answer. Here is the updated working version with findOneAndUpdate
And here the expected result:
you should give second param as object of keys value paris of data,
don't pass as $Set : blockfields, just add like below, if it is object containing parameters,
{ $set: blockFields }
Because code should be like this
Block.updateOne(
{ _id: req.params.blockid },
blockFields, // if blockfields is object containing parameters
{ new: true },
(err, block) => {
// lines of code
}
);
For more detail here is link to updateOne function detail updateOne

using .save() vs findByIdAndUpdate() for removing item from array

I am using .pull to remove a record from an array in mongo db and it works fine, but a comment I read somewhere on stack overflow (can't find it again to post the link) is bothering me in that it commented that it was bad to use .save instead of using .findByIdAndUpdate or .updateOne
I just wanted to find out if this is accurate or subjective.
This is how I am doing it currently. I check if the product with that id actually exists, and if so I pull that record from the array.
exports.deleteImg = (req, res, next) => {
const productId = req.params.productId;
const imgId = req.params.imgId;
Product.findById(productId)
.then(product => {
if (!product) {
return res.status(500).json({ message: "Product not found" });
} else {
product.images.pull(imgId);
product.save()
.then(response => {
return res.status(200).json( { message: 'Image deleted'} );
})
}
})
.catch(err => {
console.log(err);
});
};
I think what they were saying though was it should rather be done something like this (an example I found after a google)
users.findByIdAndUpdate(userID,
{$pull: {friends: friend}},
{safe: true, upsert: true},
function(err, doc) {
if(err){
console.log(err);
}else{
//do stuff
}
}
);
The main difference is that when you use findById and save, you first get the object from MongoDB and then update whatever you want to and then save. This is ok when you don't need to worry about parallelism or multiple queries to the same object.
findByIdAndUpdate is atomic. When you execute this multiple times, MongoDB will take care of the parallelism for you. Folllowing your example, if two requests are made at the same time on the same object, passing { $pull: { friends: friendId } }, the result will be the expected: only one friend will be pulled from the array.
But let's say you've a counter on the object, like friendsTotal with starting value at 0. And you hit the endpoint that must increase the counter by one twice, for the same object.
If you use findById, then increase and then save, you'd have some problems because you are setting the whole value. So, you first get the object, increase to 1, and update. But the other request did the same. You'll end up with friendsTotal = 1.
With findByIdAndUpdate you could use { $inc: { friendsTotal: 1 } }. So, even if you execute this query twice, on the same time, on the same object, you would end up with friendsTotal = 2, because MongoDB use these update operators to better handle parallelism, data locking and more.
See more about $inc here: https://docs.mongodb.com/manual/reference/operator/update/inc/

Resources