Well, I am trying to do autoincrement in node.
So I have a simple function that I found here How to add auto increment to existing collection in mongodb/node.js?
// Increment DB Value
exports.getNextSequenceValue = async function (sequenceName) {
try {
const seqNum = await db.collection('counters').findOneAndUpdate({
_id: sequenceName
}, {
$inc: {
seq: 1
}
}, {
returnNewDocument: true
});
// Check if response has seqNum.seq exists
console.log(seqNum.value.seq);
if (seqNum && seqNum.value.seq) {
return seqNum.value.seq
} else {
return 'what ever you want '
}
} catch (err) {
console.error(err)
}
}
and my controller where i used it.
const user = new User({
profile: {
firstname: req.body.firstname,
lastname: req.body.lastname,
location: req.body.location,
status: 1,
avatar: "/uploads/avatars/defaultProfilePic.jpg"
},
email: req.body.email,
password: req.body.password,
xid: core.getNextSequenceValue('userid') // ----HERE ITS FUNCTION-----
});
console.log(core.getNextSequenceValue('userid')) //---- [object Promise]X ---
User.findOne({
email: req.body.email
}, (err, existingUser) => {
if (err) {
return next(err);
}
if (existingUser) {
req.flash('errors', {
msg: 'Account with that email address already exists.'
});
return res.redirect('/signup');
}
user.save((err) => {
if (err) {
return next(err);
}
req.logIn(user, (err) => {
if (err) {
return next(err);
}
res.redirect('/');
});
});
});
};
But now i get error.
ValidationError: User validation failed: xid: Cast to Number failed for value "Promise { <pending> }" at path "xid"
Have no idea how to fix it.
Thanks for any help.
Ok your issue seems to be with new : true when you're using findOneAndUpdate(), it has to be returnNewDocument: true more over you need to do await on function which takes sometime to finish but not on the response of it. Remember to always wrap your async & await with try-catch block.
exports.getNextSequenceValue = async function (sequenceName) {
try {
const seqNum = await db.collection('counters').findOneAndUpdate({
_id: sequenceName
}, {
$inc: {
seq: 1
}
},
{ returnNewDocument: true }
);
// Check if response has seqNum.seq exists
console.log(seqNum.seq);
if (seqNum && seqNum.seq) { return seqNum.seq } else {
return 'what ever you want '
}
} catch (err) {
console.error(err)
}
}
Related
I am trying to create a login functionality for my Reactjs Webiste using Nodejs express backend.
I want to set a JWT token when the user tries to log in and update that token in my mongoDB database and then verify the token on the frontend and save it to localStorage.
However, when the user tries to log in after registration, it returns back the result without the token, and thus not allowing the user to log in, unless he clicks the login button again, then my code would generate and update the user with the JWT token.
Why is this behavior happening? Why is the first response only returning the found user from the findOne() operation when i am resolving the result from the findOneAndUpdate operation?
Here is my code:
Auth Controller:
login(params) {
params.email = params.email.toLowerCase();
return new Promise((resolve, reject) => {
db.collection("Users").findOne({ email: params.email }).then((response) => {
console.log(response)
if(response) {
bcrypt.compare(params.password, response.password, (err, success) => {
if(success) {
let token = jwt.sign({
name: response.name,
id: response._id
}, proccess.env.JWT_SECRET);
db.collection("Users").findOneAndUpdate({
email: params.email
}, {
$set: { token: token, lastLogin: new Date() },
}, function (e, s) {
if(e) {
console.log(e)
reject(e)
} else {
console.log("updated")
resolve(s)
}
})
} else {
reject({msg: 'Incorrect email or password.'})
}
})
} else {
reject({msg: 'cannot log in user'});
}
})
})
}
Auth Router:
router.post('/login', (req, res) => {
let User = new models.User()
let processes = [];
processes.push(function (callback) {
User.login(req.body).then(function (response) {
callback(null, response);
}, function (error) {
console.log(error)
callback(error);
});
});
async.waterfall(processes, function (error, data) {
if (!error) {
return res.json({
statusCode: 200,
msg: 'User logged in successfully.',
result: data
});
} else {
return res.json({
statusCode: 401,
msg: 'Cannot login user.',
error: error
});
}
});
})
React Login.js:
const login = () => {
axios.post('/login', data).then(async (response) => {
console.log(response)
if(response && response.data.result.value.token ) {
localStorage.setItem("authUser", JSON.stringify(response.data.result.value.token))
history.push("/")
console.log(response.data.result)
} else {
console.log("ERROR")
}
})
}
MongoDBs method findOneAndUpdate does return the old document by default.
In order to return the updated document pass returnNewDocument: true as option:
https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/
In your case:
db.collection("Users").findOneAndUpdate({
email: params.email
}, {
$set: { token: token, lastLogin: new Date() },
}, {
returnNewDocument: true
}, function (e, s) {
if(e) {
console.log(e)
reject(e)
} else {
console.log("updated")
resolve(s)
}
})
PS: You might should use async functions with await. This could make your code way more readable (at least within the User Model) :)
This can help you.
In your model
async login(params) {
params.email = params.email.toLowerCase();
try {
const user = await db.collection("Users").findOne({ email: params.email });
if(!user) {
throw {message: "Incorrect email"}
}
const vaild = await bcrypt.compare(params.password, user.password);
if(!valid) {
throw {msg: 'Incorrect email or password.'}
}
let token = jwt.sign({
name: user.name,
id: user._id
}, proccess.env.JWT_SECRET);
return db.collection("Users").findOneAndUpdate({
email: params.email
}, {
$set: { token: token, lastLogin: new Date() },
}, {new: true}); //FOR THE RETRIEVE NEW UPDATEs FROM MONGODB
} catch(e) {
throw e
}
}
I have this Schema here
Consider the likedTours which is an Array of Objects (Tours) (ignore position 0).
I want to pull any Objects where the _id of a Tour matches the critiria.
Adding a new Tour upon liking a tour is okay, but on unlike I don't know how to pull that item out.
Here is my function in the Controller in the Node.JS backend
const unlikeTour = async (req, res) => {
try {
TourDB.Tour.findOneAndUpdate(
{ _id: req.params.tourid },
{
$pull: { likedUsers: req.userID },
$inc: { likes: -1 },
}
).exec(async (err, docs) => {
if (!err) {
try {
await UserDB.User.findOneAndUpdate(
{ _id: req.userID },
{ $pull: { 'likedTours._id': docs._id } } //Here I need help
).exec()
return res.status(200).send({ successMessage: 'Tour successfully unliked' })
} catch (err) {
return res.status(500).send({ errorMessage: 'User not found' })
}
} else {
return res.status(500).send({ errorMessage: 'Tour not found' })
}
})
} catch (err) {
return res.status(500).send({ errorMessage: err })
}
}
This method looks for a tour and update it by pulling out the userID and decrement the likes count by -1.
And then I try to find in the UserDB that tour in the likedTours and tried to pull but it doesn't not work like that.
Thanks in advance
you can update as
await UserDB.User.findOneAndUpdate(
{ _id: req.userID },
{ $pull: { likedTours: { _id: docs._id } } } //Here I need help
).exec();
reference: https://docs.mongodb.com/manual/reference/operator/update/pull/
I have created a function that is looking for any reservation document with matching _id, if exists is deleted, and next the Book(findById) function is performed which is looking for any books using function(findOneAndUpdate), if exists is added value to quantity +1.But i don't know why but the function does not want to be done.
router.post('/cancel-reservation', (req, res) => {
var reservation = req.body.reservation;
Reservation.findByIdAndRemove(reservation._id, function (err) {
if (err) throw err;
else {
Book.findById(reservation.idBook, (err, book) => {
if (err) throw err;
if (book == null) {
//////////// //*WORKING *////////////////
let bookObj = {
name: reservation.nameBook,
description: reservation.description,
publishingHouse: reservation.publishingHouse,
quantity: 1,
year: reservation.year,
sites: reservation.sites
};
var book = new Book(bookObj);
book.save()
.then(function (book) {
res.json(book)
})
.catch((err) => {
res.json('not saved')
})
//////////// //*WORKING *////////////////
} else if (book) {
// findOneAndUpdate
//////////// //*HERE IS PROBLEM *////////////////
book.quantity = book.quantity+1;
console.log(book._id)
Book.findOneAndUpdate({ _id: book._id },
{ $set: { quantity : book.quantity } }, { upsert: true }),
((err, complete) => {
if(err) console.log('errrrrr');
else {
console.log(complete)
console.log('complete')
res.json(complete)
}
})
}
});
}
});
});
this problem may be related to the fact that the findOneAndUpdate function is nested in findById?
I think that you have an extra parentheses on the findOneAndUpdate
Book.findOneAndUpdate({ _id: book._id },
{ $set: { quantity : book.quantity } }, { upsert: true },
(err, complete) => {
if(err) console.log('errrrrr');
else {
console.log(complete)
console.log('complete')
res.json(complete)
}
})
I'm using async each to iterate over objects and perform a query to populate their children. Each site has locations which have floors which have areas. My sites and locations populate just fine, however, that is where it stops. Sails outputs that it is looking for floors and areas though but they are never assigned. Any ideas?
gg: function (req, res) {
var userID = req.param('id');
User.findOne({ id: userID }).populate('sites').exec(function afterFind(err, foundUser) {
if (err) return res.json({ status: 'failure', message: 'Server error' });
if (foundUser === undefined) return res.json({ status: 'failure', message: 'User was not found' });
var resultToJson = foundUser.toJSON();
var sites = resultToJson.sites;
async.each(sites, function (site, siteCb) {
sails.log.debug('Finding locations for ' + site.name);
Locations.find({ site: site.id }).exec(function afterFind(err, foundLocations) {
if (err) {
siteCb(err);
} else {
site['locations'] = foundLocations;
async.each(site['locations'], function (location, locCb) {
sails.log.debug('Finding floors for ' + location.name);
Floor.find({ location: location.id }).exec(function afterFind(err, foundFloors) {
if (err) {
locCb(err);
} else {
location['floors'] = foundFloors;
async.each(location['floors'], function (floor, floorCb) {
sails.log.debug('Finding areas for ' + floor.name);
Area.find({ floor: floor.id }).exec(function afterFind(err, foundAreas) {
if (err) {
floorCb(err);
} else {
floor['areas'] = foundAreas;
floorCb();
}
});
}, function (floorError) {
if (floorError) {
locCb(floorError);
}
else {
locCb();
}
});
}
});
}, function (locError) {
if (locError) {
siteCb(locError);
} else {
siteCb();
}
});
}
});
}, function (siteError) {
if (siteError) {
sails.log.debug(siteError);
return res.json({ status: 'failure', message: 'Server error' });
} else {
return res.json({ status: 'success', message: 'Sites for user retrieved', sites: sites });
}
});
});
}
This code should be fixed with use of toJSON() on foundLocations. Anytime when you're overriding a defined attribute with populated one (or something else) it will not work when using a ORM returned object, use toJSON() or something and assign to plain JS object.
Ref: https://stackoverflow.com/a/43500017/1435132
Also, any reason to not use populate with Locations.find?
I have two problems with the code below. It is the beginning of a node.js module for registering users that are stored in a mysql database.
The first problem is that in the callback from the first query the variable this.connection is undefined. The second problem is that the variable user is also undefined in the same callback. Hence the code crashes on the following line:
this.connection.query('INSERT INTO users{{values values}}', {
values: {
user_name: user.username,
user_email: user.email,
user_password: user.password
}
})
Please let me know how to solve this in a propper way.
Many thanks!
Full code is below:
module.exports = usermodel;
//connection is a mysql-wrapper connection
function usermodel(connection)
{
this.connection = connection;
}
playermodel.prototype = {
registerByEmail: function (user, callback) {
//check firts if the user already exists
this.connection.query('SELECT user_id FROM users {{where data}}', {
data: {
user_email: user.email
}
},
function (err, result) {
if (err) {
...
} else if (result.length > 0) {
...
} else {
// this.connection is undefined ????????????
this.connection.query('INSERT INTO users {{values values}}', {
values: {
user_name: user.username,
user_email: user.email,
user_password: user.password
}
}),
function (err, result) {
...
}
}
});
}
}
this.connection is undefined because this.connection is on the instance of playermodel the callback function passed to the first query does not know about the context of the playermodel instance.
user should be defined though. You can get around the this.connection being undefined by either binding the function or setting a reference to this in a variable the the callback will have access to.
//Bind
playermodel.prototype = {
registerByEmail: function (user, callback) {
//check firts if the user already exists
this.connection.query('SELECT user_id FROM users {{where data}}', {
data: {
user_email: user.email
}
},
function (err, result) {
if (err) {
...
} else if (result.length > 0) {
...
} else {
// this.connection is undefined ????????????
this.connection.query('INSERT INTO users {{values values}}', {
values: {
user_name: user.username,
user_email: user.email,
user_password: user.password
}
}),
function (err, result) {
...
}
}
//HERE
}.bind(this));
}
}
or
//me reference to this
playermodel.prototype = {
registerByEmail: function (user, callback) {
//HERE
var me = this;
//check firts if the user already exists
this.connection.query('SELECT user_id FROM users {{where data}}', {
data: {
user_email: user.email
}
},
function (err, result) {
if (err) {
...
} else if (result.length > 0) {
...
} else {
//HERE
me.connection.query('INSERT INTO users {{values values}}', {
values: {
user_name: user.username,
user_email: user.email,
user_password: user.password
}
}),
function (err, result) {
...
}
}
});
}
}