Preventing duplicating entries with Mongoose and Node.Js - node.js

So I have a student grades object
[
{
_id: '5bf43c42a09e1129b8f0cd4c',
user: '5bc89dec5f6e1103f808671b',
StudentGrades: [
{
_id: '5bf43daf58f0f803d4e9760b',
classCode: 'ENG1A0',
gradeLevel: 12,
credit: 1,
mark: 67
}
],
__v: 0
}
];
I use the following backend code to make entries into the database
router.put('/:user_id', function(req, res) {
let id = req.params.user_id;
const gradeFields = {
classCode: req.body.classCode,
gradeLevel: req.body.gradeLevel,
credit: req.body.credit,
mark: req.body.mark
};
if (gradeFields)
passport.authenticate('jwt', { session: false }),
UserGrades.findOneAndUpdate(
{ user: id },
{ $push: { StudentGrades: gradeFields } },
{ new: true },
{ unique: true },
function(err) {
if (err) {
res.send(err);
} else {
res.send(gradeFields);
}
}
);
});
Everything is working but at this time, a person can have duplicate classes.
In the express code i tried using {unique: true} and tried setting the classCode mongoose model to unique as well, but it didn't work. Help would be appreciated

Validate that the user_id maps to an existing user.
Validate that this user does not already have a StudentGrade with the supplied classCode.
Update the document and return the updated user.
router.put('/:user_id', async (req, res) => {
const { user_id } = req.params;
const gradeFields = {
classCode: req.body.classCode,
gradeLevel: req.body.gradeLevel,
credit: req.body.credit,
mark: req.body.mark
};
try {
// Authenticate with Passport
await passport.authenticate('jwt', { session: false });
// Grab user with this user_id
const existingUser = await UserGrades.findOne({ user: user_id });
if(!existingUser) {
// If user does not exist, throw 404
res.status(404).send("User with this ID does not exist");
}
// Check if user has classCode already on an existing StudentGrade
if(existingUser.StudentGrades.some(sg => sg.classCode === req.body.classCode)) {
res.status(409).send("Student already has grade with this class code.");
}
// Update user record with new StudentGrade and return updates document
const updatedUser = await UserGrades.findOneAndUpdate(
{ user: user_id },
{ $push: { StudentGrades: gradeFields } },
{ new: true }
);
res.status(200).send(updatedUser);
} catch (e) {
console.log('Failed to update user grades', e);
// Unknown server error, send 500
res.status(500).send(e)
}
});

Related

Node express check mongo object id exists in array if not exist then update

I have a list of array in mongo what I need to do is push Id in list of array and check that if Id exist then it will not push
Right now I push like this
const eventUserGoing = async (req, res) => {
try {
const updateuserGoinginEvent = await Events.findByIdAndUpdate(
req.body.eventid,
{
$push: {
userGoing: req.user.user_id,
},
},
{
new: true,
}
);
res
.status(200)
.json({
success: true,
message: 'Event saved successfully',
data: updateuserGoinginEvent,
});
} catch (err) {}
};
I think if its possible by aggregate but don't get what's best what to do this.
Try to use findOneAndUpdate if the event with _id does not already contain the user.
If that is the case, $push the new user:
const eventUserGoing = async (req, res) => {
try {
const updateuserGoinginEvent = await Events.findOneAndUpdate(
{ _id: req.body.eventid, userGoing: { $ne: req.user.user_id } },
{
$push: {
userGoing: req.user.user_id,
},
},
{
new: true,
}
);
res.status(200).json({
success: true,
message: 'Event saved successfully',
data: updateuserGoinginEvent,
});
} catch (err) {}
};

Mongoose How to await updating documents?

im trying to figure out why my code isnt awaiting the update method in mongoose.
First I have tried to find the user
//Find user
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password');
//Update the user property
let newuser;
if (remember) {
if (!user.rememberAcct) {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
}
} else if (user.rememberAcct) {
await user.updateOne({
rememberAcct: false,
});
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
document always updating asyncrounously, but await seems not applying to update. newuser returns the user with old property
Update:
with options {returnDocument: 'after'} or {new: true} is still returning old document.
Refers: https://mongoosejs.com/docs/api.html#model_Model-findOneAndUpdate
What Im missing here?
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
}, {returnDocument: 'after'});
In your snippet, there are 3 mistakes
1st
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password') // <-- missing `rememberAcct` to select, without it `user.rememberAcct` will always be `undefined`
2nd
await user.updateOne({ // <-- this should be `User` not `user`
rememberAcct: false, // <-- this filter will select a random user & update query is missing
})
3rd :- Not using user.save() to update user.
here is final solution.
const user = await User.findOne(
{
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
},
{ password: 1, rememberAcct: 1 }
)
if (!user) throw new Error('user not found')
console.log('user.rememberAcct (before)', user.rememberAcct)
if (remember) {
if (!user.rememberAcct) {
user.rememberAcct = true
await user.save()
}
} else if (user.rememberAcct) {
user.rememberAcct = false
await user.save()
}
// NOTE: above `if/else` statements can be boiled down into single `if/else` statement, no need to check more than once.
// I leave it you figure it out.
console.log('user.rememberAcct (after)', user.rememberAcct)
Your should either use findByIdAndUpdate, or pass a proper query:
// Either:
await User.findByIdAndUpdate(
user._id,
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
// Or:
await User.findOneAndUpdate(
{
_id : user._id,
},
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
I believe you need to place them into a try catches, let me know if the below works
this ads some error handling as well so you know why it may have failed.
async function yourfunctionorrequest() {
let password
try {
const user = await User.findOne({
username,
active: {
$ne: false
},
accessBanExpires: {
$lt: Date.now()
},
}, function (err, docs) {
// set the specific item you were after to the variable
password = docs.password
})
} catch (e) {
console.log(e)
}
let newuser;
if (remember) {
if (!user.rememberAcct) {
try {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
} catch (e) {
console.log(e)
}
} else if (user.rememberAcct) {
try {
await user.updateOne({
rememberAcct: false,
});
} catch (e) {
console.log(e)
}
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
console.log("error message", e)
}
}

How to update user's profile in express?

I want to update a particular user's financial records which is an array.
<-- This is my user model -->
const FinanceSchema = new mongoose.Schema({
moneyToBePaid: {
type: Number,
},
moneyPaid: {
type: Number,
},
moneyToBeReceived: {
type: Number,
},
moneyReceived: {
type: Number,
},
});
const UserSchema = new mongoose.Schema({
financialInformation: [FinanceSchema],
});
module.exports = mongoose.model("user", UserSchema);
<-- This is my post route -->
router.post("/users/:id/profile", async (req, res) => {
const _id = req.params.id;
const {
moneyToBePaid,
moneyPaid,
moneyToBeReceived,
moneyReceived,
} = req.body;
const finance = {
moneyToBePaid,
moneyPaid,
moneyToBeReceived,
moneyReceived,
};
try {
const user = await User.findById(_id);
user.financialInformation.push(finance);
await user.save();
res.status(200).json(user);
}
<-- This is my update route -->
router.patch("/users/:user_id/profile/:profile_id", async (req, res) => {
const user_id=req.params.user_id;
const profile_id=req.params.profile_id;
try {
}
I am confused how to update a particular user's particular finance record.
Assuming you want to update the moneyPaid property of specific finance array element:
try {
const user_id=req.params.user_id;
const profile_id=req.params.profile_id;
await User.findOneAndUpdate(
{ "_id": user_id, "financialInformation._id": profile_id },
{
"$set": {
"financialInformation.$.moneyPaid": "2258" // the data you want to update
}
});
res.status(200).send('user updated');
} catch(err) {
console.log(err);
res.send(err);
}

How to use mongoose findAndUpdateOne()

i am trying to update mongoDB via mongoose using the findOneAndUpdate() method,
i destructure my fields from req.body but if i updated only a single value others are set to null, how do i fix that
CODE
const { name, email, phone, type } = req.body;
await Contact.findOneAndUpdate(
{ _id: req.params.id },
{ $set: { name, email, type, phone } },
{ upsert: true },
(err, updatedContact) => {
if (err) {
console.error(err.message);
res.status(400).send('Could not updat');
} else {
res.json(updatedContact);
}
}
);
});
******************************************
This gives me the desirable result i expected please not that i have not implemented error checking you can do that using the 'express-validator'
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, phone, type } = req.body;
// Build contact object
const updatedContact = {};
if (name) updatedContact.name = name;
if (email) updatedContact.email = email;
if (phone) updatedContact.phone = phone;
if (type) updatedContact.type = type;
try {
await Contact.findOneAndUpdate(
{ _id: req.params.id },
{ $set: updatedContact },
{ new: true }
);
res.json(updatedContact);
} catch (err) {
console.error(err.message);
res.status(400).send('Could not update');
}
});

find an id in embedded document node mongoose

I have a model of courses with the following structure:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const user_shortid = require('shortid');
// Create Course schema
const CourseSchema = new Schema({
courseDetail: {
type: String
},
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
enrolledUsers: [{
type: Schema.Types.ObjectId,
ref: 'users'
}],
currentStatus: {
type: String,
default: 'Planned'
}
});
mongoose.model('courses', CourseSchema);
I have created a post request for adding a signed in user to the array of enrolledUsers, the problem is, I want to check first if the req.user.id exists in the enrolledUsers array. Following is my post request:
router.post('/joincourse', [ensureAuthenticated], (req, res) => {
Course.findByIdAndUpdate({ _id: req.body.coursecode },
{ $push: { enrolledUsers: req.user.id } },
{ safe: true, upsert: true },
function (err, doc) {
if (err) {
req.flash('error_msg', 'Could not enroll in the course');
res.redirect('/dashboard');
} else {
req.flash('success_msg', 'You are now enrolled in the course');
res.redirect('/dashboard');
}
}
);
});
Right now the behavior is that a user can enroll again and again in the same course.
Is there some way I can check for the req.user.id in the enrolledUsers array before it is added?
you can do find the user first by using find() and then if a user exists, update it , else
give an error like this
router.post('/joincourse', [ensureAuthenticated], (req, res) => {
Course.findById({ _id: req.body.coursecode },
function (err, doc) {
if (err) {
req.flash('error_msg', 'Could not enroll in the course');
res.redirect('/dashboard');
} else {
if(doc){
if(!doc.enrolledUsers.includes(req.user.id)){ // here is the checking
doc.enrolledUsers.push(req.user.id);
doc.save();
req.flash('success_msg', 'You are now enrolled in the course');
res.redirect('/dashboard');
}
}else{
// show error msg
}
}
}
);
});

Resources