Elegant way to update many properties with mongoose - javascript

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));
}
};

Related

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.

cut user id mongoose response

I'm trying to response from my backend a piece of user id by using lodash, i tryed with id.slice(2, 9) but i get a response without _id. What i'm doing wrong? thanks in advance.
getUserData: (req, res, next) =>{
User.findById(req.params.userId,
(err, user) => {
if (!user)
return res.status(404).json({ status: false, message: 'User record not found.' });
else
return res.status(200).json({ status: true, user: _.pick(user, ['_id'.slice(2, 9), 'domain', 'store', 'settings']) });
}
);
},
getUserData: (req, res, next) =>{
User.findById(req.params.userId,
(err, user) => {
if (!user)
return res.status(404).json({ status: false, message: 'User record not found.' });
else {
let json = { status: true, user: _.pick(user, ['_id', 'domain', 'store', 'settings']) };
json.user._id = json.user._id.slice(2, 9);
return res.status(200).json(json);
}
}
);
},
Pick the parts you want
Slice the _id to replace it with just the part you want
return the object
Edit:
To cut the ObjectId is necessary first to parse to string, so you need something like this:
var newUserId = user._id.toString().substring(3,user._id.toString().length)
But there is a problem (I think, not tested). If you try to store the cut id into a model object, maybe mongoose don't allow you to add an string (and no valid ObjectId) value instead of ObjectId.
Maybe is neccesary create another object instead of the model with the schema.
Old answer (unrelated) but maybe usefull for somebody:
If you want to hide the result just use select() into your query.
You run a query and then select which fields do you want to get or not into the response.
The proper way to code it is as follows:
query.select({ field1: 1, field2: 1 });
//or
query.select('-field1');
//and many other ways
Docs here

How to get data with just inserted data with Sequelize in PostgreSql?

I want to get updated table values after I add user to my "WOD" table. For instance, I have 2 users in my WOD table and after I add third user , I want to return a response to client with I have just inserted data (third guy). But now , I can only return first 2 users because I can not take updated values. Of course I can make another query to get updated table values after I insert, but is there any better solution ? Here is my codes;
const addUser = async (req, res) => {
try {
const { userId, wodId } = req.body;
if (!userId || !wodId) {
res.status(400).send({ status: false, message: 'need userId and wodId' });
}
const wod = await Wod.findByPk(wodId, {
include: [
{
model: User,
as: 'Participants',
through: { attributes: [] }
}
]
});
//check capacity if full.
if (wod.Participants.length >= wod.capacity) {
res
.status(403)
.send({ status: false, message: 'Capacity of this class is full!' });
}
const result = await wod.addParticipants(userId);
res.status(201).json({ status: !!result, wod });
} catch (error) {
res.status(500).send({ status: result, message: error.message });
console.log(error.message);
}
};
As a result of many-to-many association sequelize.sync will generate some functions for us. You are used addParticipants function and this returns an array that added to the assocation(userwod) table.
In this array you will find some id fields(join table fields) because you just run like this INSERT INTO 'user_wods' ('user_id''wod_id') VALUES (2,1). If you want to return the added user's information then you should run a SELECT * FROM 'user' WHERE 'id'=2.
You must call reload function for fetch the third guy.
await wod.reload()

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)

Delete an element from array by update method

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');
}
});

Categories

Resources