Looking up a mongoDB database - node.js

so I have implemented a register page that sends the inputs into a mongoDB atlas. However, I am trying to implement a new page where users are able to login. I am having trouble trying to find the specific key: value pair in order to check my inputs.
My mondoDB atlas shows the following:
_id: 62da20a99df697486c4b12cc
username: "hello"
password: "hello"
__v: 0
My function for logging in is the below:
exports.login = async (req, res) => {
if (await User.find({ username: req.body.username })) {
console.log("do smth");
}}
For now, lets just ignore the console.log() as I just want to see whether its going through or not. It's always evaluating to true and I don't know why. I can type any input and it always goes through but I'm not sure why its always evaluating to true so I know for sure I'm not looking up at the database correctly.
The
User.find()
where
User
is the name of the variable I defined when requiring the file I defined my schema. Any help would be appreciated in terms of how I should look up a key which in this case is username and I want to look up the req.params.username to see if that actually exists in my database.
Updated CODE
exports.login = async (req, res) => {
if (
User.findOne({ username: req.body.username }, function (err, user) {
console.log(user);
})
) {
res.send("YES");
} else {
res.send("NO");
}
If the input is in the database, it will log the user which in this case is
{
_id: new ObjectId("62daeb740c2c6e2b61325151"),
username: '123',
password: '123',
__v: 0
}
However, if its not in the database, it will log null but the if statement still evaluates to true.

User.find returns an array, which may be empty or not, but is never false in the Javascript sense. By contrast User.findOne returns an object, or null if nothing is found, and null does not satisfy the if condition. Probably that's what you want.
If User.findOne returns a Query, you can try invoking it with a callback function:
User.findOne({username: req.body.username}, function(err, user) {
if (err)
console.error(err);
else if (user) {
console.log("do smth");
}
});

Related

Mongoose model methods - create document if unique

I'm new to MongoDB and Mongoose. I'm using it with my Node project (with Express), and I'm trying to keep everything organized and separated. For example, I'm trying to keep all the database queries in each model file. This way all other files could simply use User.createNew({ fields }) and a new user will be created.
I need each user to be unique (based on their usernames), and I'm not sure exactly where to keep this functionality. I set unique: true in the Schema but upon reading Mongoose's documentation, they stated that unique is not real validation (or something about how validation should happen beforehand). So my main problem is how to create a static method to create a new user, and also validate this user doesn't exist beforehand. I could implement all of this in one static method:
userSchema.statics.createUser = function (username, ..., cb) {
this.findOne({ username }, function (err) {
if (err) {
return new this({
username,
...
}).save(cb);
} else {
return Promise.reject(new Error("User already exists!"));
}
});
};
I'm pretty confused with the whole cb function and what I'm supposed to pass to it.
After reading other posts about validation, I realized I could also do something like this:
const userSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
validate: function (val, fn) {
this.find({ username: val }, function (err) {
fn(err || true);
});
},
message: function (props) {
`Username '${props.value}' already exists.`;
},
},
...
Also here I'm confused about what fn accepts and what it even does (I found an answer similar to this with no explanation online).
In the end, I would like to use this model in a controller to create a new user, like this
User.createNew({ username: "example", ...})
.then(doc => console.log("User was created: " + doc))
.catch(err => console.error) // The error is something custom like "This user already exists"
Any help is appreciated!

I want to check another field When I get dublicate key error for email (unique) in mongodb findoneandupdate

User.findOneAndUpdate({ _id: userFindByid }, {
$set: {
"email": req.body.email,
"username": req.body.username,
"phone_number": req.body.phone_number,
"address": req.body.address,
"isBenefactor": req.body.isBenefactor,
"location": req.body.location
}
}, { runValidators: true, context: 'query' }, (err, doc) => {
if (err) {
// if request email has already exist in db I want to check that emails isDeleted field in here . if isDeleted is true I want to update .
return res.status(500).json({ message: err.message });
}
else {
return res.status(200).json({ message: 'Your account was updated' });
}
})
//
Let me explain scenario clearly,
I registered with an email address(first#gmail.com) then I deleted my account =>(first#gmail.com)=>isDeleted=true
After that I again registered with another email address(second#gmail.com)=>isDeleted=false
Now I want to update my second email address with first one I will get an unique key error because (first#gmail.com) is in mydb ,but I have to da update process because (first#gmail.com)=>IsDelete=true
If I use { 'email': req.body.email, 'isDeleted': true} I can not update (second#gmail.com)=>isDeleted=false
I can fix the problem by using too much if statements , but I dont want to use if statements too much. I am looking for best practice for that problem.
I hope I could explain
Here is my code block , can someone help me ?
THIS ANSWER ASSUMES YOU ARE USING MONGOOSE!
One way you can do is instead of using findOneAndUpdate you can use .save this way you can issue a hook on mongoose.
For example, you would do User.save(...) then you go to your schema code and you add the following (assuming your schema name is UserSchema)
UserSchema.post('save', function(error, doc, next) {
// Error code 11000 means this is a duplicate
if (error.name === 'MongoError' && error.code === 11000) {
// So instead of throwing an error you would do anything you want
// Such as look for the other record and delete it, update its isDelete
// field, remove email, etc... really is up to you
}
next()
});
EDIT: Of course before User.save(...) you need to find the user!
For example,
User.findOne({_id:1}, function(err, doc){
// Update doc values
// Finally do doc.save(...)
})
You can make it look much better by using the async library and using async.waterfall
EDIT2: Okay so now that I understand your requirement better, here is your best solution in my opinion.
Find the user you want to update
Change email
Save
On the other side, you need to have a hook (unfortunately its poorly documented, you have to do your own digging but here is a link to mongoose documentation)
Here is how this will work
1. Hook a pre save (before actually saving execute specific block of code)
2. The block of code will only execute when the email is modified (we dont want to execute it everytime, its just a waste of resources)
3. The block of code will use deleteOne and delete the user matching that email
NOTE: For best performance make sure to index the email and make it unique!
I have created the full (similar to what you want) project with the code on here
But if you wish here are also some snippet
// This will run before saving the object
UserSchema.pre('save', function (next) {
let user = this
// Make sure to run only when email is modified
if(user.isModified('email')) {
// IF the email was modified, then attempt to delete a record with this email (if there is one, then it will be deleted otherwise it will just continue)
this.constructor.deleteOne({email:this.email, isDeleted:true}, (err) => {
err ? next(err) : next()
})
} else {
next()
}
})
// Code to save/update the user
User.findOne({_id:"1"}, function(err, user) {
if (err) {
throw err
} else {
if (user) {
user.email = "test2#test.com"
user.save(err => {
err ? console.log(err) : console.log("Success!")
})
} else {
console.log("User was not found!")
}
}
})
Good luck!

Sequelize findOrCreate falling back to catch if data is found

FindOrCreate is suppose to eitherĀ find or create based on the arguments you give it.
So I'm wondering why it's falling back to the catch promise when data (a user) is found, when it should just return the data that was found to the .spread function instead of trying to insert the data that already exists?!.
As you can see below if there is a user found or created it's going to run the same function regardless. All that function is doing is creating an auth token for the particular user. But for some reason it's going straight into the catch promise!?
User.findOrCreate({
where: {
userId: details.userId,
},
defaults: {
name: details.name,
email: details.email,
},
attributes: ['userId', 'name', 'email', 'profilePic'],
}).spread(function (new_user, created) {
console.log("\n\nNew user was created T or F: ", created);
// Create the token and then pass it back to our callback along with the user details
user.createTokenForUser(new_user.userId, function(token) {
cb(token, new_user);
});
}).catch(function (err) {
// For some reason I'm running...?!??!?!?!?!
console.log("\n\n", err);
});
Did you try removing the trailing comma in the where statement? Its just hitting an error and that looks like a culprit :)
where: {
userId: details.userId,
}
to
where: {
userId: details.userId
}

How to do simple mongoose findOne with multiple conditions?

How can I perform this findOne query with multiple conditions? Or a substitute method (preferrably an equally simple one) if it is not possible...
User.findOne({ 'email': email }, function (err, user) {
if (err) {
request.session.flash = {
type: "danger",
message: "Failed to log you in, error occurred."
};
response.redirect("/snippet/");
}
I have tried
User.findOne({ 'email': email, 'pwd': pwd }, function (err, user) {
and
User.findOne({ 'email': email $and 'pwd': pwd }, function (err, user) {
and
User.findOne({ 'email': email}, $and: {'pwd': pwd}, function (err, user) {
Try using the mongoose promise system (.exec()). The query inside .findOne() should be a single object.
User
.findOne({email: email, pwd: pwd})
.exec(function(err, user){
...
});
Sidenote - it looks like this is for authenticating a a user login. It might be a better strategy to query just on the email, then try to match the passwords to give more depth to the error responses than a blanket 'Invalid login' type response.
User
.findOne({email: email}) //If not found, no user with that email exists
.exec(function(err, user){
var hashed_pwd = hashPassword(pwd);
if(!hashed_pwd === user.pwd){
//If they don't match, user entered wrong password
}
...
});
User.findOne({ 'email': email, 'pwd': pwd }, function (err, user) {
this syntax is correct. if this isn't returning any results, it means you didn't match any records.
my guess is that your pwd field is hashed / encrypted and that you need to run the same hash / encryption on your pwd variable before doing this findOne
When the mongoDB is returning your docs, if it has not found Data then it will return null .
You can check your docs if it is null then response with some other page.
On the other hand if docs have data respond with the next page.
if(docs)
respond("your content");
else
respond("error page incorrect credentials")
I was doing the mistake thinking that it will create an exception err, but it's not an error it is simply a null data.

How to check values against the DB when using the pre-save hook?

On a User schema, I'd like to check if the specified email already exists for the specified shop, before saving.
var UserSchema = new Schema({
_shop: {
type: Schema.Types.ObjectId,
ref: 'Shop',
required: true
},
email: String,
//...
});
UserSchema.pre('save', function(next) {
if (!this.isNew) return next();
// How to do use the static method isThatEmailFreeForThisShop here?
});
UserSchema.statics.isThatEmailFreeForThisShop = function(email, shop_id, cb) {
this.find({email: email, _shop: shop_id}, function(err, users) {
// ...
});
});
There could be different users with the same email as long as they are from different shops.
I do not know how to use the static method in the pre-save hook...
Thanks!
You've created a User Model instance somewhere (I'll call it User):
var User = mongoose.model('user', UserSchema);
So, the isThatEmailFreeForThisShop function is available on the User model:
User.isThatEmailFreeForThisShop(...)
From your save hook:
UserSchema.pre('save', function(next) {
if (!this.isNew) return next();
User.isThatEmailFreeForThisShop(this.email, this._shop,
function(err, result) {
if (result) { // found
// do something
return next({ error: "duplicate found" });
}
return next();
});
});
You may also want to switch to using the pre-validate rather than save.
I'd expect in your function, isThatEmailFreeForThisShop that you'd call the cb parameter when the results have been "found".
You probably would use findOne (reference) rather than find. Given that there's still a race condition, you'd want to add an index as a compound index email and shop_id and set the unique attribute to true to prevent duplicates from sneaking in (then, you'll need to handle the fact that a save on a model instance may throw an error.)
UserSchema.statics.isThatEmailFreeForThisShop = function(email, shop_id, cb) {
this.findOne({email: email, _shop: shop_id}, function(err, user) {
// ...
cb(err, user != null);
});
});

Resources