Unique index not working on Mongoose schema - javascript

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.

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

can a mongoose schema field reference more than one other models

I have a mongoose schema field called "bookmarks" and I would like it to reference more than one model. for example, if I have 2 other models named "post" and "complain". I would like the user to be able to bookmark both of them.
const userSchema = new mongoose.Schema({
fullname: {
type: String,
required: true,
trim: true,
lowercase: true
},
username: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
bookmarks: [{
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'Post'
}],
})
below is a post model, where users can post things in general
const postSchema = new mongoose.Schema({
body: {
type: String,
required: true,
trim: true,
lowercase: true
}
})
below is a complain model, where users can post a complain
const complainSchema = new mongoose.Schema({
body: {
type: String,
required: true,
trim: true,
lowercase: true
}
})
How can I get the bookmarks field in the user model to be able to get the object id of both the complain model and the post model?
You can nest those two models in userSchema itself just like you are doing with bookmark schema .
You can also refer to this link , i hope it will resolve your query.
Link - : What is the proper pattern for nested schemas in Mongoose/MongoDB?
Here is the correct way to achieve this.
First remove bookmarks
Add this
ref: {
kind: String, // <-- Model Name post,complain
item: {
type: mongoose.Schema.Types.ObjectId,
refPath: 'ref.kind',
fields: String,
},
},
When you want to fetch the records,you can use populate
model.User.find({})
.populate({ path: 'ref.item' })
.then(data=>{console.log(data)})
.catch(err => {console.log(err)});

Node, Mongoose - $or operator not functioning as expected

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

Adding new object array to a spesific id, but it creates a new row

I need to push new objects to my products array. I found out that findOneAndUpdate() method works for the first time if the array is empty. When I do a save() method, it creates a seperate row with only the products. I've tried everythig to my knowledge without success.
My Schemas;
const ProdcutSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
productDescription:{
type: String,
required: true
},
pricePerUnit:{
type: Number,
required: true
},
productAmmount:{
type:Number,
required: true
},
/*productImagePath:{
type:String,
required: true
}*/
});
const UserSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
date:{
type: Date,
default: Date.now
},
products:[ProdcutSchema]
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
Mongoose ran on POST method:
const newProduct = new User({
email: useremail,
products:[{
name :product_name,
productDescription : product_Description,
pricePerUnit : price_PerUnit,
productAmmount : product_Ammount
}]
});
newProduct.save((err)=>{
if(err) console.log(err);
res.redirect('/dashboard');
});
In your case, i think you should use $push operator.
Example:
await User.findOneAndUpdate(your_condition, {$push: {products: your_new_product}});

Categories

Resources