【问题标题】:Custom Validation on a field that checks if field already exists and is active对字段进行自定义验证,检查字段是否已存在且处于活动状态
【发布时间】:2019-08-20 07:25:36
【问题描述】:

我有一个 mongodb 集合“用户”,其中包含“名称”、“电子邮件”、“活动”字段。 我想添加一个验证,即每个文档电子邮件都应该是唯一的。但是,如果文档无效,即 Active 为 false,则可以接受电子邮件。

这是模型

email: { type: String, validate: {
  validator: function(v) {
    return new Promise((resolve, reject)=> {
      console.log("validating email")
      const UserModel = mongoose.model('User');
      UserModel.find({email : v, active: true}, function (err, docs) 
         {
          if (!docs.length){
              resolve();
          }else{
              console.log('user exists: ',v);
              reject(new Error("User exists!"));
          }
      });
    })
  },
  message: '{VALUE} already exists!'
   }
 },
name: {
  type: String,
  required: true
 },
 active: {
   type: Boolean,
    default: true
 }

问题是每当我对此模型进行任何更新时都会调用此验证。 因此,如果我更新名称,那么也会调用此验证,并给出电子邮件已存在的错误。

如何在电子邮件字段上添加验证,以便如果有人向数据库添加新条目或更新电子邮件,如果现有用户具有相同的电子邮件 ID 并且处于活动状态,它会在数据库中检查?

【问题讨论】:

  • 您可以使用 presave 和 preupdate 挂钩。
  • 你也可以使用 npm 包 mongoose-unique-validator
  • 使用 $set 更新文档而不是 mongoose 更新,例如:UserModel.update({_id: 1},{$set: {email: "abc@xyz.com"}}) 这将跳过 mongoose-schema-validation

标签: node.js validation mongoose mongoose-schema


【解决方案1】:

我会使用unique compound index,而不是对您的数据库再进行一次额外查询。您的代码如下所示:

const schema = = new Schema(...);

schema.index({email: 1, active: 1}, {unique: true});

Mongo 本身会拒绝您的文档,您可以像这样在代码中捕获它:

const {MongoError} = require('mongodb'); // native driver

try {
  await model.updateOne(...).exec(); // or .save()
} catch (err) {
  //11000 is error code for unique constraints
  if (err instanceof MongoError && err.code === 11000)
    console.error('Duplicate email/active pair');
}

【讨论】:

  • 这意味着我们只能有 1 个存档条目..即如果有一个带有电子邮件的文档:abc@gmail.com 和活动:false..那么不能有任何其他存档条目具有相同的电子邮件有活动:假..如果我错了,请纠正我
  • 所以你应该可以有两封不活跃的电子邮件,对吧?
  • 是的,在实际应用中,我可能存档了一个帐户..然后使用相同的电子邮件创建了一个帐户..然后可能希望再次存档此帐户..
  • 好吧,如果你想在你的主集合中拥有过时的用户,那么你就不能真正利用索引,这将显示在你的性能中,因为每次更新/创建都需要查找带有findOne 的数据库。如果您对此表示满意,请继续在 cmets 中使用 Shivam Pandey 获取原始帖子。预保存和预更新挂钩是您的选择。
  • 只有一件事..确保索引仍然用于唯一的电子邮件..您会将存档文档放在哪里?您会永久删除它们吗?或者有更好的方法来处理存档文档吗?我有多个集合中的存档文档
【解决方案2】:

例如,如果用户已经注册了 Mongo DB,我会首先调用 Mongoose findOne 函数;

let foundUser = await User.findOne({email});
if (!foundUser) {
 // update user, create user etc.
 ...
}

我认为最好不要在 Mongoose 文档对象中使用逻辑。也许有办法实现它,但我更喜欢在代码中进行这些验证,而不是在文档中,这只是我的偏好。

您也可以尝试使电子邮件独一无二,如下所示:

email: {
  type: String,
  unique: true
}

【讨论】:

  • 使电子邮件唯一的问题是,当用户被归档时,即活动变为假。当新条目添加到数据库时,电子邮件唯一仍然会发生冲突。我想在猫鼬模型级别执行此操作的原因是,当在此文档上完成任何操作时,集合的基本完整性不会被破坏,即两个具有相同电子邮件 ID 且均处于活动状态的文档。否则我知道该怎么做在控制器中
  • 哦,我明白了,这会导致很大的问题
  • 你可以使用预钩作为 Shivam 在你的问题中回答的问题,你可以在这里找到一个例子 medium.com/@justinmanalad/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-02
  • 1970-01-01
  • 2014-12-07
  • 2020-01-11
  • 2019-12-10
  • 2016-07-18
  • 1970-01-01
相关资源
最近更新 更多