How to do validations before saving the edited data in mongoose?
For example, if sample.name
already exists in the database, the user will receive a som
check with one query if email or phoneNumber already exists in DB
let userDB = await UserS.findOne({ $or: [
{ email: payload.email },
{ phoneNumber: payload.phoneNumber }
] })
if (userDB) {
if (payload.email == userDB.email) {
throw new BadRequest({ message: 'E-mail already exists' })
} else if (payload.phoneNumber == userDB.phoneNumber) {
throw new BadRequest({ message: 'phoneNumber already exists' })
}
}
Another way to continue with the example @nfreeze used is this validation method:
UserModel.schema.path('name').validate(function (value, res) {
UserModel.findOne({name: value}, 'id', function(err, user) {
if (err) return res(err);
if (user) return res(false);
res(true);
});
}, 'already exists');
There is a more simpler way using the mongoose exists function
router.post("/groups/members", async (ctx) => {
const group_name = ctx.request.body.group_membership.group_name;
const member_name = ctx.request.body.group_membership.group_members;
const GroupMembership = GroupModels.GroupsMembers;
console.log("group_name : ", group_name, "member : ", member_name);
try {
if (
(await GroupMembership.exists({
"group_membership.group_name": group_name,
})) === false
) {
console.log("new function");
const newGroupMembership = await GroupMembership.insertMany({
group_membership: [
{ group_name: group_name, group_members: [member_name] },
],
});
await newGroupMembership.save();
} else {
const UpdateGroupMembership = await GroupMembership.updateOne(
{ "group_membership.group_name": group_name },
{ $push: { "group_membership.$.group_members": member_name } },
);
console.log("update function");
await UpdateGroupMembership.save();
}
ctx.response.status = 201;
ctx.response.message = "A member added to group successfully";
} catch (error) {
ctx.body = {
message: "Some validations failed for Group Member Creation",
error: error.message,
};
console.log(error);
ctx.throw(400, error);
}
});
If you're searching by an unique index, then using UserModel.count may actually be better for you than UserModel.findOne due to it returning the whole document (ie doing a read) instead of returning just an int.
For anybody falling on this old solution. There is a better way from the mongoose docs.
var s = new Schema({ name: { type: String, unique: true }});
s.path('name').index({ unique: true });
Typically you could use mongoose validation but since you need an async result (db query for existing names) and validators don't support promises (from what I can tell), you will need to create your own function and pass a callback. Here is an example:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/testDB');
var UserSchema = new Schema({
name: {type:String}
});
var UserModel = mongoose.model('UserModel',UserSchema);
function updateUser(user,cb){
UserModel.find({name : user.name}, function (err, docs) {
if (docs.length){
cb('Name exists already',null);
}else{
user.save(function(err){
cb(err,user);
});
}
});
}
UserModel.findById(req.param('sid'),function(err,existingUser){
if (!err && existingUser){
existingUser.name = 'Kevin';
updateUser(existingUser,function(err2,user){
if (err2 || !user){
console.log('error updated user: ',err2);
}else{
console.log('user updated: ',user);
}
});
}
});
UPDATE: A better way
The pre hook seems to be a more natural place to stop the save:
UserSchema.pre('save', function (next) {
var self = this;
UserModel.find({name : self.name}, function (err, docs) {
if (!docs.length){
next();
}else{
console.log('user exists: ',self.name);
next(new Error("User exists!"));
}
});
}) ;
UPDATE 2: Async custom validators
It looks like mongoose supports async custom validators now so that would probably be the natural solution:
var userSchema = new Schema({
name: {
type: String,
validate: {
validator: function(v, cb) {
User.find({name: v}, function(err,docs){
cb(docs.length == 0);
});
},
message: 'User already exists!'
}
}
});