Mongo username and email unique fields validation - mongodb

Hi I want to tell the user which one is already taken if is Username or Email but the way Im validating this is in the save function if this finds a duplicate it will give you a error 11000 but I cant specify which one is already taken. I want the error to give an index or something I can put in a if statement explaining which one is the duplicate key so I can be more specific with the error. Is there a way i can accomplish this? Also why Im getting always index: 0? Shouldn't be different for each field? Let me know if you have any questions.
Schema
var user = db.Schema({
name: { type: String, required: true},
username: { type: String, required: true, index: { unique: true }},
email: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false},
admin: { type: Boolean, required: true},
verify: { type: Boolean, required: true},
created_at: { type: String, required: true, default: Date.now },
updated_at: { type: String, required: true, default: Date.now },
campaigns_donated: []
})
Post Action
router.post('/register', function(req, res){
var user = new User()
user.name = req.body.name
user.username = req.body.username
user.email = req.body.email
user.password = req.body.password
user.admin = false,
user.verify = false
user.save(function(err) {
if (err) {
console.log(err);
if (err.code == 11000) {
return res.json({ success: false, message: 'username or email already taken'})
} else {
return res.send(err);
}
}
res.json({ success: true, message: 'User created'})
})
})
Pre Save Function
user.pre('save', function(next) {
var user = this
// hash the password only if the password has been changed or user is new
if (!user.isModified('password')) { return next() }
//Generate Hash function
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) return next(err)
//Change Password to hash
user.password = hash
next()
})
})
Console log error
{ [MongoError: E11000 duplicate key error index: ReachPeeps.users.$username_1 dup key: { : "xandor" }]
name: 'MongoError',
message: 'E11000 duplicate key error index: ReachPeeps.users.$username_1 dup key: { : "xandor" }',
index: 0,
code: 11000,
errmsg: 'E11000 duplicate key error index: ReachPeeps.users.$username_1 dup key: { : "xandor" }' }

Related

Mongoose model unique

I´m rather new to this..
If I dont want the user to be able to add duplicated countries to visitedCountry, shoulden unique true work?
Or are there any easy way to block that in the patch?
const User = mongoose.model('User', {
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString('hex')
},
visitedCountries:[ {
country: {
type: Object,
ref: "Country",
unique: true
},
comments: String
}]
})
app.patch('/countries', authenticateUser)
app.patch('/countries', async (req, res) => {
const { username, visitedCountry } = req.body
try {
const countryByAlphaCode = await Country.findOne({ alphaCode: visitedCountry }).lean()
const updatedUser = await User.findOneAndUpdate({ username: username, }, {
$push: {
visitedCountries: { country: countryByAlphaCode, comments: "no comments yet"}
},
}, { new: true })
res.json({ success: true, updatedUser })
} catch (error) {
res.status(400).json({ success: false, message: "Invalid request", error })
}
})
The options unique works for all documents. It prevents two (or more) documents from having the same value for your indexed field. It's often used for the email or username.
For your case, I recommend you to perform a check on the user data before you call findOneAndUpdate.

Mongoose findOneAndUpdate + upsert always replaces existing document

I have a collection I want to upsert with findOneAndUpdate. In addition to that I have two fields (isHandled, isNotADuplicate) that should be:
defaulted to 'false' upon insert
left untouched upon update (e.g. isHandled stays 'true')
I have however found that
isHandled, isNotADuplicate are always defaulted back to 'false'
_id is also regenerated upon every update (I use a compound key to query the doc, not _id)
My Model
export const QuickbrainFindingSchema = new Schema<QuickBrainFindingDocument>({
connectedApplicationType: { type: String, required: true, enum: ['jira'] },//e.g. jira
clientKey: { type: String, required: true },//e.g. 135eb702-156c-3b67-b9d0-a0c97548xxxx
//key
projectKey: { type: String, required: true },//e.g. AL
type: { type: String, required: true },
doc1key: { type: String, required: true },//e.g. AL-7
doc2key: { type: String, required: true },//e.g. AL-16
//data
calculationDate: { type: SchemaTypes.Date, default: Date.now },
direction: { type: String, required: true },
reasonAndMetric: { type: SchemaTypes.Mixed, reason: true },
scoreSummary: { type: String, reason: true },
isHandled: { type: SchemaTypes.Boolean, default: false },
isNotADuplicate: { type: SchemaTypes.Boolean, default: false },
similarityReference: { type: SchemaTypes.ObjectId, required: true, ref: "QuickbrainSimilarityMatrix" }
}, {
//options
});
QuickbrainFindingSchema.index(
{ connectedApplicationType: 1, clientKey: 1, project: 1, doc1key: 1, doc2key: 1, type: 1 },
{ unique: true, name: "compoundKey" }
);
export const QuickbrainFindingModel = model<QuickBrainFindingDocument>("QuickbrainFinding", QuickbrainFindingSchema);
My Code
public async addFinding(
projectKey: string,
doc1key: string,
doc2key: string,
type: ET_FindingType
, data: QuickbrainFindingData): Promise<QuickbrainFinding> {
let keyFull: QuickbrainFindingKey = {
connectedApplicationType: this.connectedApplicationType,
clientKey: this.clientKey,
projectKey: projectKey,
doc1key: doc1key,
doc2key: doc2key,
type: type
};
let insertObj: QuickbrainFinding = <QuickbrainFinding><unknown>{};
Object.assign(insert, keyFull);
Object.assign(insert, data);
delete (<any>insertObj).isHandled;
delete (<any>insertObj).isNotADuplicate;
return new Promise<QuickbrainFinding>(function (ok, nok) {
QuickbrainFindingModel.findOneAndUpdate(
keyFull, { $set: insertObj},
{
runValidators: true,
upsert: true,
setDefaultsOnInsert: true,
new: true,
omitUndefined: true,//I think only available for findAndReplace(..)
})
.lean().exec(function (err, result) {
if (err) {
nok(err);
}
else
ok(result)
});
});
}
Mongoose Debug Output
quickbrainfindings.findOneAndUpdate(
{
connectedApplicationType: 'jira',
clientKey: '135eb702-256c-3b67-b9d0-a0c975487af3',
projectKey: 'ITSMTEST',
doc1key: 'ITSMTEST-7',
doc2key: 'ITSMTEST-10',
type: 'Email'
},
{
'$setOnInsert':
{ __v: 0, isHandled: false, isNotADuplicate: false, _id: ObjectId("60789b02c094eb3ef07d2929") },
'$set': {
connectedApplicationType: 'jira',
clientKey: '135eb702-256c-3b67-b9d0-a0c975487af3', projectKey: 'ITSMTEST', doc1key: 'ITSMTEST-7', doc2key: 'ITSMTEST-10', type: 'Email',
calculationDate: new Date("Thu, 15 Apr 2021 19:58:58 GMT"),
direction: '2', scoreSummary: '100.0%',
similarityReference: ObjectId("60789b029df2079dfa8aa15a"),
reasonAndMetric: [{ reason: 'Title Substring', metricScore: '100%' },
{ reason: 'Title TokenSet', metricScore: '54%' }, { reason: 'Description TokenSet', metricScore: '100%' }]
}
},
{
runValidators: true, upsert: true, remove: false, projection: {},
returnOriginal: false
}
)
What happens
Existing documents are found, but when they are updated I'm confused that:
_id is regenerated
isHandled and isNotADuplicate are reset to 'false' (although insertObj does not contain them)
When looking at the debug output I can see that the new _id is the one fron $setOnInsert, which confuses the heck out of me, since the selector works
Notable
keyFull is used to query the existing document, it does not contain _id;
delete (<any>insertObj).isHandled <- the object used for $set does NOT contain isHandled
This is embarrasing to admit, but thanks to Joe I have found the problem.
Before every findOneAndUpdate / Upsert I had a delete statement removing the existing documents Pipeline:
Delete old documents
Calculate new documents
Upsert new documents -> always resulted in Insert
let matchAnyDoc = this.filterForDocKeyAny(projectKey, docKeyAny, findingType);
matchAnyDoc.forEach(async (condition) => {
QuickbrainFindingModel.deleteMany(condition).exec(function (err, res) {
if (err) {
nok(err);
} else {
ok();
}
});
}, this);

How to solve MongoError: E11000 duplicate key error collection

I am Working in a MERN application. In one of my model of express.js I have student schema like below which have unique fields
Fullname: {
type: String,
required: true,
trim: true,
},
AdmissionNumber: {
type: String,
required: true,
trim: true,
maxlength: 10,
unique: true,
},
RollNumber: {
type: Number,
required: true,
trim: true,
maxlength: 4,
},
Age: {
type: Number,
required: true,
maxlength: 2,
},
Email: {
type: String,
trim: true,
required: true,
unique: true,
},
Faculty: {
type: ObjectId,
ref: "Faculty",
required: true,
},
pass: {
type: Number,
default: 0,
}
I am saving the student with the help of form like this
exports.addStudent = (req, res) => {
let form = new formidable.IncomingForm();
form.keepExtensions = true;
form.parse(req, (err, fields) => {
if (err) {
res.status(400).json({
error: "problem with feilds",
});
}
// destructuring feilds
const {
Fullname,
AdmissionNumber,
RollNumber,
Age,
Email,
Faculty,
} = fields;
if (
!Fullname ||
!AdmissionNumber ||
!RollNumber ||
!Age ||
!Email ||
!Faculty
) {
return res.status(400).json({
error: "Please fill all fields",
});
}
// TODO: restriction on fields
let student = new Student(fields);
student.save((err, student) => {
if (err) {
res.status(400).json({
error: "Saving Student in DB failed",
});
console.log(err);
}
res.json(student);
// console.log(student.gender);
});
});
};
When I try to add student it will be added only first time after that it showing an error
I have checked my DB collection there is only one field in the database.
I had this problem before and the solution that worked for me is to delete the collection from the database then try again

User is not a constructor

im getting error: User is not a constructor when trying to add new document to my database. before I used mongoose.model without the Schema method and it worked great but I had to add validator and it needs this syntax and since then I can't make it work
CODE:
var UserSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, index: true, unique: true, required: true },
password: { type: String, required: true }
});
UserSchema.plugin(uniqueValidator);
let User = mongoose.model("user", UserSchema);
module.exports = User;
router.post('/', (req, res) => {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
// save the user
user.save(function (err) {
if (err) {
console.log('Error in Saving user: ' + err);
throw err;
}
console.log('User Registration succesful');
// return done(null, userData);
res.status(200).send({user: user})
});
});
mongoose.Schema is a constructor, so you need to call it with "new":
var UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, index: true, unique: true, required: true },
password: { type: String, required: true }
});
ok I fixed my issue instead of
module.exports = User;
I had to do:
module.exports = {User};

Create new subdocument in mongoose

What am I missing here? I want to add a sub document to the User schema I already have the schema predefined else where.
User.findById(req.body.id, function(err, user) {
if (err) return console.log(err);
reivews: [{
reviewer: req.body.name,
content: req.body.content
}]
user.save(function(err) {
if (err) console.log(err);
res.send('saved')
})
})
It's saying its saved but I don't see the review in the for the user with the id I tried to save to.
Schema
const Review = new Schema({
reviewer: String,
date : { type: Date, default: Date.now },
content : String,
isLive : { type: Boolean, default: false }
});
const User = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
reviews: [Review]
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('Review', Review);
module.exports = mongoose.model('User', User);
Please try this
User.findById(req.body.id, function(err, user) {
if (err) return console.log(err);
if (user.reviews === undefined || user.reviews.length == 0) {
user.reviews = [];
}
user.reviews.push({
reviewer: req.body.name,
content: req.body.content
});
user.save(function(err) {
if (err) console.log(err);
res.send('saved')
})
})
And ensure that data in user as per the defined Schema