2014-02-21 35 views
1

据我所知,猫鼬预保存钩子在文档插入到集合中但在验证发生之前触发。因此,如果一个验证失败,预保存钩子将不会被调用。mongoose unique:真正的预保存钩子调用验证之前的钩子

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

什么简单的代码波纹管的作用是试图让用户在其中通过_id注册时参考其他用户的模式。预保存挂钩被添加以自动推送新用户的ID在他们的推荐人列表中。

所以用户没有转诊登记 - >确定

用户B注册了一个推荐 - >确定

使用相同的电子邮件作为B的用户B2寄存器(也不行,是唯一的)和引用一个 - >应该失败,它不应该在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); 

测试代码:

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

其他一切检查出来的错误抛出,失败发生,但不顾一切的预存钩保存

任何想法如何解决这一问题或验证后,如果有一个真正的预存钩?

回答

2

据我所知,如果您为referencedBy路径提供了一个异步验证函数,它将与预保存函数并行(有效)执行,而不是串行地以这种方式阻止预存功能的执行。

您可能会考虑将它们组合到一个函数中,并且如果您想要阻止更新ReferByBy对象的引用列表,直到例如电子邮件值的唯一约束得到满足(这显然没有得到强制执行直到实际的保存尝试),您可能希望在保存后挂钩中保留这一点逻辑。

干杯。

编辑

我看了这个号码的方式,而这一切似乎在这一点相当清楚:

1)自定义验证函数之前执行预存钩并能通过返回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)内置的验证不需要做一个数据库查询来执行(如“必需的”约束)之前还执行的预保存功能,并能防止其执行。注释掉B2的电子邮件赋值,而不是分配非唯一值轻易表现出来:

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

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

所以,验证(定制或内置)确实之前发生预保存钩被执行时,除在的情况下,内置的验证这需要一个分贝查询执行

+0

是的,我想到了,但我试图避免结合验证,并希望有一种方法,使其如广告 – Stefan

+0

做了一点挖掘。似乎它主要按照您期望的方式工作,除了内置唯一性验证程序的情况。有关详细信息,请参阅我的更新回答 – prattsj