mongoose唯一:真正的预保存钩子在validation前调用钩子

据我了解,mongoose预保存挂钩在文档被插入集合之前,但在validation发生之后触发。 因此,如果一个validation失败,预保存的钩子将不会被调用。

在我的情况下,他们被称为无论:

下面这个简单的代码试图build立一个用户在注册时通过_id引用其他用户的模式。 添加预保存挂钩以自动将新用户的ID推入其引荐列表中。

所以用户注册时没有引荐 – >确定

用户b注册一个转介 – >确定

用户b2使用与b相同的电子邮件注册(不是确定的,唯一的),并且引用一个 – >应该失败,并且不应该在a.references中推送b2的ID

架构:

var userSchema = new Schema({ email: {type:String, unique:true, required:true}, isVerified: {type:Boolean, default:false}, referredBy: {type:Schema.ObjectId, ref:'User'}, referred: [{type:Schema.ObjectId, ref:'User'}], }); userSchema.pre('save', function (next) { if (!this.isNew) return next(); if (!this.referredBy) return next(); User.findById(this.referredBy, function (err, doc) { if (err) return next(err); if (!doc) return next(new DbError(['referredBy not found: %s', this.referredBy])); doc.referred.push(this._id); doc.save(next); }.bind(this)); }); userSchema.path('referredBy').validate(function (value, respond) { User.findById(value, function (err, user) { if (err) throw err; if (!user) return respond(false); respond(true); }); }, 'doesntExit'); var User = mongoose.model('User', userSchema); 

testing代码:

 var a = new User(); a.email = 'a'; a.save(function () { var b = new User(); b.email = 'b'; b.referredBy = a._id; b.save(function () { var b2 = new User(); b2.email = 'b'; b2.referredBy = a._id; b2.save(function (err, doc) { console.log('error:', err); // duplicate error is thrown, which is OK console.log(!!doc); // this is false, which is OK User.findById(a._id, function (err, result) { console.log('# of referrals: ', result.referred.length); // 2, which is BAD }); }); }); }); 

其他所有检查,错误被抛出,失败发生,但所有预保存钩子保存不pipe

任何想法如何解决这个问题,或者如果validation钩子后有一个真正的预先保存?

据我所知,如果你为被引用的path提供了一个asynchronousvalidation函数,它将与预保存函数并行(有效地)执行,而不是串行地以这种方式阻止预保存函数执行。

你可能会考虑将它们合并成一个函数,如果你想阻止被引用的对象的引用列表的更新,直到例如邮件值的唯一约束被满足(这显然不被强制实施直到实际保存尝试),你可能想要在保存后的钩子中保留这些逻辑。

干杯。

编辑

我已经看了很多方面,这一点似乎很清楚:

1)自定义validation函数在预保存钩子之前执行,并且可以通过返回false来阻止预保存钩子的执行(当然也可以保存自己)。 示范如下:

 userSchema.pre('save', function (next) { console.log('EXECUTING PRE-SAVE'); next(); }); userSchema.path('referredBy').validate(function (value, respond) { console.log('EXECUTING referredBy VALIDATION') respond(false); }, 'doesntExit'); 

2)内置的validation器,不需要做一个数据库查询强制执行(如“必要的”约束)也执行前保存function,可以阻止他们的执行。 通过评论b2的电子邮件值赋值而不是分配非唯一值来轻松演示:

 var b2 = new User(); //b2.email = 'b'; b2.referredBy = a._id; 

3)需要进行数据库查询的内置validation器(例如强制唯一性)不会阻止预先保存的钩子执行。 据推测,这是优化的成功案例,否则将不得不涉及1查询检查唯一性,然后另一个查询通过唯一性validation后作出upsert。

因此,在执行预保存钩子之前确实 (自定义或内置) 确实发生, 除非内置validation需要db查询来执行