Node, Mongoose - $or operator not functioning as expected - javascript

Within my express app, I have a mongoose schema like so:
User model schema:
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
minlength: 4,
maxlength: 50
},
email: {
type: String,
required: true,
minlength: 3,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 6,
},
})
Here is an operation I do in one of my endpoints. I expect a user to be returned if req.body.usernameOrEmail matches a username or email of a User. Here is the operation:
let user = await User.find({ $or: [ { username: req.body.usernameOrEmail }, { email: req.body.usernameOrEmail } ] })
if (!user) return res.send('No user found')
What happens is that if I put anything req.body.usernameOrEmail, the 'No user found' message is not printed as expected. Anybody know whats going wrong with the operation performed above? Thanks.

awaited .find() method returns an empty array which is truthy in JavaScript, try:
console.log(![])
You can use .findOne() instead and in that case null will be returned when there's no match and your condition will work

Related

MongoServerError: E11000 duplicate key error collection: with mongoose save()

i have this model with username and email set uniqe to false
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 3,
maxlength: 20,
unique: false,
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 64,
lowercase: true,
unique: false,
},
mailboxLink: {
type: String,
required: true,
unique: true,
default: nanoid(),
},
createdat: { type: String, default: dateJakarta },
});
and 1 user in my mongodb database
{"_id":{"$oid":"622eec9de7f66d1d633061e7"},"username":"jhon","email":"email#gmail.com","mailboxLink":"mfdYTDK","createdat":"2022-03-14 14:19:01","__v":0}
but when i'm trying to register the same username and email
userRouter.post("/register", async (request, response) => {
const newUser = new userSchema(request.body); // body: {username, email}
await newUser.save((err, user) => {
if (err) {
console.log(err);
} else {
const token = newUser.generateAuthToken();
response.status(201).json({
token,
user,
message: "Register successfully",
});
}
});
});
i got this error from console.log(err);
MongoServerError: E11000 duplicate key error collection: PigeonProjekt.users index: email_1 dup key: { email: "email#gmail.com" }
index: 0,
code: 11000,
keyPattern: { email: 1 },
keyValue: { email: 'email#gmail.com' }
could someone help me solve this problem.
If you can, try dropping the database or the collection itself then test again. If you first set the email field to unique: true and then inserted the user that you have in the database, but later changed the unique value of email to false in your schema, this will not update the collection itself. In short, rules in the database for email unique: true still apply and you need to remove them.

bcryptjs does not work on insertMany (works fine on create)

bcryptjs version: "bcryptjs": "^2.4.3"
mongoose version: "mongoose": "^6.0.12"
I am trying to encrypt user password on user creation or password update. When I create a single user with User.create() everything works as intended (password is encrypted). When I use User.insertMany() the data is inserted successfully but the password is not encrypted. This is my schema:
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
voterId: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
unique: true,
},
votedFor: [
{
type: mongoose.Schema.ObjectId,
ref: 'Election'
}
],
finishedVoting: {
type: Boolean,
required: true,
default: false
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', async function(next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with salt 10
this.password = await bcrypt.hash(this.password, 10);
next();
});
This is some sample data that I am trying to insert:
const voters = [
{
name: "Sherali",
surname: "Samandarov",
voterId: "194199",
password: "FA654644", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
},
{
name: "Sherali",
surname: "Samandarov",
voterId: "184183",
password: "MB454644", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
},
{
name: "Sherali",
surname: "Samandarov",
voterId: "194324",
password: "FA651684", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
}
]
I am guessing that userSchema.pre('save', ...) does not trigger on insertMany() for some reason
Solved.
I was able to solve the problem by following #victorkt's answer:
Use pre('validate') instead of pre('save') to set the value for the
required field. Mongoose validates documents before saving, therefore
your save middleware won't be called if there are validation errors.
Switching the middleware from save to validate will make your function
set the password field before it is validated.
So,
userSchema.pre('validate', async function(next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with salt 10
this.password = await bcrypt.hash(this.password, 10);
next();
});

How to verify if email exists in two seperate documents of mongoose?

I have two models, patients and doctors. When the user signs in, be it the doctor or patient, there is only one route which runs the login function of the backend. But what I fail to understand is how to query such that it searches in both collections of patients and doctors by using single query.
This is the doctor model:
const doctorSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
practitionerLicense: {
type: String,
required: true
}
});
module.exports = Doctor = mongoose.model("doctors", doctorSchema);
And the patient model:
const patientSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Patient = mongoose.model("patient", patientSchema);
Now I want something like the following pseudo code where users could be like a base class or something.
Users.findOne({email}).then(...)
I went through many other similar questions but saw methods like populate which I believe would not suit my case. Any suggestions?
Your patient and doctor schemas are almost same except the practitionerLicense field.
So instead of two different schemas, I would create a common user schema with an additional role field like this:
const userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
practitionerLicense: {
type: String,
required: false
},
role: {
type: String,
enum: ["patient", "doctor"],
required: true
}
});
module.exports = User = mongoose.model("users", userSchema);
Note that I also set practitionerLicense's required option to false.
This way you can use a common user login form and route.
Your register route and register form in React can also be common, if you could verify if a user enters a practitioner license, and you can validate it using an API if there is such an API.
If that is not possible, your register routes and register components must be different for patient and doctor. When a user registers in patient register form, you can set role to patient. And when a user registers in doctor register form, you can set role to doctor.

How to fix "trim" not working in mongoose schemas

I'm learning mongoose and tried setting "trim" to true inside a mongoose schema. However it is not working as expected.
I've tried setting other things like "lowercase" to true and it does work, so I don't know why "trim" isn't working.
var userSchema = {
name: {type: String, required: true, trim: true, lowercase: true},
email: {
type: String,
required: true,
validate: function(value){
if(!(validator.isEmail(value))){
throw new Error("Not a valid email address");
}
},
trim: true,
},
age: {
type: Number,
validate: function(value){
if(value < 0){
throw new Error("Age must be a positive number");
}
},
default: 0
},
password: {
type: String,
required: true,
minlength: 7,
validate: function(value){
if(value.toLowerCase().includes("password")){
throw new Error(" Passwords should not contain the word
'password ' ");
}
},
trim: true
}
}
var User = mongoose.model('User', userSchema);
var someuser = new User({
name: "some user",
age: 25,
email: "user#something.com",
password: "verysecurepassword"
})
I expected an the name of the new user to be 'someuser', but instead it turned out to be 'some user'.
Name "some user" has space in the middle of the string.
What you are trying to do will not work as trim will remove whitespaces from start and end of string only.
Please check trim() definition in the docs, it seems like you're trying to remove unwanted characters in the middle of the string, but trim() removes them only at the start and at the end of the string MongoDocs
I would suggest you to define a custom settermiddleware or preSavemiddleware docs hook for this and transform string using regex (if you want to remove only spaces): str.replace( /\s\s+/g, ' ' )

Unique index not working on Mongoose schema

I have a schema setup like this:
var UserSchema = new Schema({
id: {type: String, required: true, unique: true, index: true, default: mongoose.Types.ObjectId},
name: { type: String, required: true },
email: { type: String, required: true, unique: true, index: true },
mobile: { type: String, unique: true, sparse: true },
password: { type: String, required: true }
});
Seems to work great except the email field is letting in duplicates, despite have unique:true set. I do the following:
User.create({
name: req.body.name,
email: req.body.email,
mobile: req.body.mobile,
password: password
}, function(err, user) {
if (err) return res.send({ invalid : true });
});
If req.body.email is a value that's already in the database, the query above should return err. But it doesn't, it creates the new user perfectly fine, resulting in duplicate emails in the database.
Why is this happening?
mongo and hence mongoose will assign an automatic id fields to your document. usually that is "_id".
Try removing the "id" from your schema to fix your issue.

Categories

Resources