Delete an element from array by update method - javascript

I'm trying to delete one element from an array in one of the mongoose schema I made. But the pull method doesn't seem to work well for me
I manage to add an element and I'm trying to make it basically the same but in reverse. But the pull method doesn't seem to work for me.
this is my schema
userName: {type: String, required: true, unique: true},
password: {type: String, required: true},
favorite: [{type: Schema.Types.ObjectId, ref:'programs'}]
and this is how I'm adding an element to the array
router.put('/favorite/:id', (req, res) => {
User.findOne({ _id: req.params.id }, (err, user) => {
if(err) return console.error(err);
user.favorite.push(req.body.favorite);
user.save((err, user) => {
if(err) return console.error(err);
console.log(user);
});
res.json(user);
})
});
here I tried to make it in similar way to remove one element but didn't work, in postman it showed just that it cannot put
router.put('favorite/delete/:id', (req, res) => {
User.findOne({_id: req.params.id}, (err, user) =>{
if(err) return console.error(err);
user.favorite.pull(req.body.favorite);
user.save((err,user) => {
if (err) return console.error(err);
console.log(user);
});
res.json(user);
})
});
Basically, I just need to be able to remove the element by its id. I feel like the id should be mentioned somewhere, but I'm not sure where or how. Thanks for all the tips

I highly recommend using async await insted of callbacks, mongoose supports Promises.
You can also use mongoose queries to update fields instead of JavaScript, this will reduce the amount of queries to the database.
Also when an error occurs don't forget to send a response to the user to inform that something went wrong.
You tried to use MongoDB $pull operator in JavaScript instead of using it in the query, you cant use MongoDB operators in JavaScript
You can also destructure properties from req.params to make it more readable
Note the id have to be the user document id and favorite have to be the favorite document id
Now you can add to favorites using $push
// to use the await keyword we have to set the callback to an async function
router.put('/favorite/:id', async (req, res) => {
// destruct id from the req.params object and favorite from req.body
const { id } = req.params;
const { favorite } = req.body;
try {
const updatedUser = await User.findByIdAndUpdate(id,
{ $push: { favorites: favorite } },
// new: true means return the updated document
{ new: true },
);
res.send(updatedUser);
} catch (e) {
console.error(e);
res.status(500).send('Something went wrong');
}
});
And remove from favorites using $pull
router.put('/favorite/delete/:id', async (req, res) => {
const { id } = req.params;
const { favorite } = req.body;
try {
const updatedUser = await User.findByIdAndUpdate(id,
{ $pull: { favorites: favorite } },
{ new: true },
);
res.send(updatedUser);
} catch (e) {
console.error(e);
res.status(500).send('Something went wrong');
}
});

Related

JS file not pulling model.findByPk() using "/:id"?

I'm trying to pull items from my database using each item's id, but am receiving an empty object when running it through Insomnia. For example, in the code below, I would like to pull a category by ID, but also include any associated Products.
Any idea what I might be doing wrong? Thank you in advance!
router.get('/:id', async (req, res) => {
try {
const oneCategory = await Category.findByPk({
include: [{ model: Product }]
});
// console.log(oneCategory);
if (!oneCategory) {
res.status(404).json({ message: 'No category found with that id!' });
return;
}
res.status(200).json(oneCategory);
} catch (error) {
res.status(500).json(error);
}
});
When calling the findByPk method, you need to pass the key you are looking for. In this particular case, the code should look like this:
router.get('/:id', async (req, res) => {
try {
const oneCategory = await Category.findByPk(req.params.id, {
include: [{ model: Product }]
});
if (!oneCategory) {
res.status(404).json({ message: 'No category found with that id!' });
return;
}
res.status(200).json(oneCategory);
} catch (error) {
res.status(500).json(error);
}
});
So just grab the id from the URL with req.params.id and pass it to findByPk. However, it could be a good idea to check so that the id is in fact an integer before doing so :)

How to check if key exists on mongoose document?

I have a User Model and recently added some key to it. This means that existing users will not have this key initially and new users do. Now I have a route where I want to check if the particular key exists on the user object so that I can add it if it returns false.
This is my route currently:
router.post("/new-application", verifyUser, (req, res) => {
const { application } = req.body;
User.findById(req.userId)
.then((user) => {
if (user.hasOwnProperty("applications")) {
console.log("has applications");
} else {
console.log("has not applications");
user["applications"] = initialApplications;
}
user.save().then((updatedUser) => {
// console.log(updatedUser);
});
})
.catch((err) => {
console.log("err fetching user: ", err);
res.end();
});
});
The problem is that if (user.hasOwnProperty("applications")) always returns false even after I added it to the user. I also tried if("applications" in user). That also does not work.
So how can I check if a key or field exists on a Mongoose object.
A simple way of checking if the field exists or not can be done by $exist.
router.post("/new-application", verifyUser, (req, res) => {
const { application } = req.body;
User.findById({$and: [{_id: req.userId}, {applications: {$exists:false}}]})
.then((user) => {
// it will return the user only when
// applications doesn't exist
})
.catch((err) => {
console.log("err fetching user: ", err);
res.end();
});
});
Note: the reason your old user doesn't show the applications is they don't have it when you saved them and changing the model now won't add this property to old models. So, we can use the $exists operator to check it.

Node.js. Filtering through a collection of referenced objects

I'm having issues filtering through an array of ObjectId's, which are a reference to another model. And to return an error if one or more with that ObjectId already exists within the array.
This is what the model looks like in the database
And this is my code
// #route PUT api/profile/favorites/:id
// #desc Add recipe to favorites
// #access Private
router.put('/favorites/:id', auth, async (req, res) => {
const post = await Post.findById(req.params.id);
try {
const profile = await Profile.findOne({ user: req.user.id });
const newFav = {
_id: post._id,
by: post.name,
nameOfDish: post.nameOfDish
};
console.log(profile.favorites.valueOf());
// Check if the post has already been added to favorites
if (profile.favorites.filter((favs) => favs.valueOf().toString() === post._id).length > 0) {
return res.status(400).json({ msg: 'Post already been favorited' });
}
profile.favorites.unshift(newFav);
await profile.save();
res.json(newFav);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}); ```
Your code should still work. Are you sure the value and type of fav.valueOf().toString() and post._id are the same?
On a different note, why not use .find() method? It'll make the code easier to read.
Replace
profile.favorites.filter((favs) => favs.valueOf().toString() === post._id).length > 0
with
profile.favorites.find(fav => fav.valueOf().toString() === post._id)

Elegant way to update many properties with mongoose

I have a model with 7 properties and want to update them all when there is an edit request from front-end. Is there any elegant way to do so, or do I have to type all of them manually like in my code bellow (whitch by the way works fine for me, but looks really ugly).
exports.saveDish = (req, res, next) => {
const {
name,
description,
price,
category,
vegetarian,
hot,
menuPosition,
} = req.body;
Dish.findById(req.body._id)
.then(oldDish => {
if (oldDish) {
oldDish.name = name;
oldDish.description = description;
oldDish.price = price;
oldDish.category = category;
oldDish.vegetarian = vegetarian;
oldDish.hot = hot;
oldDish.menuPosition = menuPosition;
oldDish.save();
return res.status(204).json({ message: 'Dish data properly updated' });
}
const newDish = new Dish(req.body);
newDish.save();
return res.status(201).json({ message: 'New dish properly saved' });
})
.catch(err => console.log(err));
};
This will update an existing record and return the updated value. If no matching record is found, it will return a falsey value to the callback or promise (can't remember if it's null or something else).
Dish.findByIdAndUpdate(req.body._id, updates, {new: true}, cb)
You can try something like this :
exports.saveDish = (req, res, next) => {
/**
*
* upsert: true --> helps to insert new document if no matching doc exists
* new: true --> returns new document in output
* rawResult: true --> helps to find out whether update or insert operation is done
*
* Dish is a mongoose schema where findByIdAndUpdate is only from mongoose,
* which internally converts a string from it's first parameter into {_id : ObjectId('req.body._id')}, also uses $set operation on req.body
*
* Print data to check what's being returned, you might see entire document(data.value) being returned with some other information
*
* */
Dish.findByIdAndUpdate(req.body._id, req.body, { upsert: true, new: true, rawResult: true }, (err, data) => {
if (err) { console.log(err); res.status(200).json({ message: 'Operation Failed' }) }
if (data.lastErrorObject.updatedExisting) return res.status(204).json({ message: 'Dish data properly updated' });
return res.status(201).json({ message: 'New dish properly saved' });
})
};
Here you're updating existing document (adding new fields or updating the existing fields w.r.t. what's there is req.body) or inserting an entire new document if no matching _id is found in database, this way you avoid multiple DB calls. Here I've made it in callbacks, but earlier I've actually done it in async await, it does work either way, this should work for all of your cases listed above !!
Ref : Mongoose findByIdAndUpdate
#EddieDean, your way worked almost fine, it turns out that you have to pass any id to findByIdAndUpdate() method, so I edited it a little bit to work with unique, new dishes too.
Working code just in case:
exports.saveDish = (req, res, next) => {
if (req.body._id) {
Dish.findByIdAndUpdate(
{ _id: req.body._id },
{ ...req.body },
{ useFindAndModify: false }
)
.then(oldDish => {
if (oldDish) {
oldDish.save();
return res
.status(204)
.json({ message: 'Dish data properly updated' });
}
})
.catch(err => console.log(err));
} else {
const newDish = new Dish(req.body);
newDish
.save()
.then(result => {
return res.status(201).json({ message: 'New dish properly saved' });
})
.catch(err => console.log(err));
}
};

mongoose "Find" with multiple conditions

I am trying to get data from my mongoDB database by using mongoose filters. The scenario is that each user object in the database has certain fields like "Region" or "Sector".
Currently I am getting all the users that contain the keyword "region" in there object like so:
// Filter all healthcare bios by region
app.get('/user',function(req, res) {
// use mongoose to get all users in the database
User.find({region: "NA"}, function(err, user)
{
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
{
res.send(err);
}
// return all todos in JSON format
console.log(user);
res.json(user);
});
});
How can put some conditions in mongoose that it return users that contain both "region" && "Sector" in their objects. Currently its only returning the user which have the region keyword in them.
I have tried using $and operator but I couldn't get it to work.
app.get('/user',function(req, res) {
User.find({region: "NA",sector:"Some Sector"}, function(err, user)
{
if (err)
{
res.send(err);
}
console.log(user);
res.json(user);
});
});
If you want data with either region:"NA" or sector:"Some Sector". you can use $or operator.
User.find({$or:[{region: "NA"},{sector:"Some Sector"}]}, function(err, user)
{
if (err)
{
res.send(err);
}
console.log(user);
res.json(user);
});
If you want results that contain any region or sector as long as both are present at the same time you need the following query in your User.find:
{region: {$exists:true},sector: {$exists:true}}
, is the equivalent of $and as long as you are searching different fields.
const dateBetweenDates = await Model.find({
$or: [
{
$and: [
{ From: { $gte: DateFrom } },
{ To: { $lte: DateTo } },
], // and operator body finishes
},
{ _id: req.user.id},
], //Or operator body finishes
})
For anyone else trying to find with multiple conditions using mongoose, here is the code using async/await.
app.get('/user', async (req, res) {
const user = await User.find({region: "NA",sector:"Some Sector"});
if (user) {
// DO YOUR THING
}
});

Categories

Resources