为啥猫鼬在更新时不验证?

Posted

技术标签:

【中文标题】为啥猫鼬在更新时不验证?【英文标题】:Why Mongoose doesn't validate on update?为什么猫鼬在更新时不验证? 【发布时间】:2013-03-15 17:04:00 【问题描述】:

我有这个代码

var ClientSchema = new Schema(
  name: type: String, required: true, trim: true
);

var Client = mongoose.model('Client', ClientSchema);

使用 express,我用这段代码创建了一个新客户端

var client = new Client(req.body);
client.save(function(err, data) 
  ....
);

如果我将表单上的名称字段留空,mongoose 不允许创建客户端,因为我在架构上根据需要设置了它。另外,如果我在名称前后留有空格,猫鼬会在保存前删除这些空格。

现在,我尝试使用此代码更新客户端

var id = req.params.id;
var client = req.body;
Client.update(_id: id, client, function(err) 
  ....
);

它允许我更改名称,但如果我在表单上将其留空,mongoose 不会验证并保存一个空名称。如果我在名称前后添加空格,它会用空格保存名称。

为什么猫鼬在保存时验证但在更新时不验证?我做错了吗?

mongodb: 2.4.0 猫鼬:3.6.0 快递:3.1.0 节点:0.10.1

【问题讨论】:

您采用了哪种方法?只是好奇,面临完全相同的问题。你能在这里分享例子吗?非常感谢。 【参考方案1】:

从 Mongoose 4.0 开始,您在 update()findOneAndUpdate() 上使用新标志 runValidators: true can run validators。

Mongoose 4.0 引入了在 update() 上运行验证器的选项和 findOneAndUpdate() 来电。打开此选项将运行验证器 对于您的update() 调用尝试$set$unset 的所有字段。

例如,给定 OP 的 Schema:

const ClientSchema = new Schema(
  name: type: String, required: true, trim: true
);

const Client = mongoose.model('Client', ClientSchema);

在每次更新时传递标志

您可以像这样使用新标志:

const id = req.params.id;
const client = req.body;
Client.update(_id: id, client,  runValidators: true , function(err) 
  ....
);

pre 钩子上使用标志

如果你不想每次更新时都设置标志,你可以为findOneAndUpdate()设置一个pre钩子:

// Pre hook for `findOneAndUpdate`
ClientSchema.pre('findOneAndUpdate', function(next) 
  this.options.runValidators = true;
  next();
);

然后您可以使用验证器update(),而无需每次都传递runValidators 标志。

【讨论】:

我喜欢这个答案,但是 findOneAndUpdate 的 prehook 抛出了一个猫鼬错误......看起来可能是由于该方法不受支持?此外,仅指定“更新”预挂钩不会触发验证。错误:/.../mongoose/node_modules/hooks/hooks.js:149 if ('undefined' === typeof proto[methodName].numAsyncPres ) Doug,在 Mongoose 4.0 (github.com/Automattic/mongoose/issues/2138) 中添加了用于向 findONeAndUpdate 添加挂钩的查询中间件。您确定您使用的是 Mongoose 4.0 或更高版本吗? 在更新调用中指定 runValidators: true 效果很好。但是 pre hook option set 在 Mongoose 4.1.0 版中对我不起作用【参考方案2】:

您没有做错任何事情,validation 是作为 Mongoose 中的内部中间件实现的,并且中间件不会在 update 期间执行,因为这基本上是对本机驱动程序的传递。

如果您希望验证您的客户端更新,您需要 find 要更新的对象,对其应用新的属性值(参见下划线的 extend 方法),然后对其调用 save

Mongoose 4.0 更新

如 cmets 和 victorkohl 的回答中所述,当您在 update 调用中包含 runValidators: true 选项时,Mongoose 现在支持验证 $set$unset 运算符的字段。

【讨论】:

有一些关于 Mongoose 更新验证的 GitHub 票证,请参阅 Issue 860、Issue 892 和 Issue 4722。我希望他们能尽快解决这个问题.. _.extend 真的帮了我大忙,因为我需要从部分 json 对象更新许多不同的子文档字段。谢谢! 从 v3.9.3 开始,update() 有 2 个附加选项:setDefaultsOnInsert 和 runValidators @see github.com/LearnBoost/mongoose/commit/… 我一般是拿到文档,然后用Lo-Dash的merge方法做一个深度扩展,以防我有嵌套属性......lodash.com/docs#merge【参考方案3】:

默认情况下,MongoDB 不会对更新进行验证。

为了在更新发生时默认进行验证,在连接到 MongoDB 之前,您可以只设置全局设置:

mongoose.set('runValidators', true); // here is your global setting

mongoose.connect(config.database,  useNewUrlParser: true );
mongoose.connection.once('open', () => 
    console.log('Connection has been made, start making fireworks...');
).on('error', function (error) 
    console.log('Connection error:', error);
);

因此任何内置或自定义验证也将在任何更新上运行

【讨论】:

【参考方案4】:

如果您在 mongoose 的配置中添加此选项,它会起作用:

mongoose.set('runValidators', true)

【讨论】:

【参考方案5】:

您可以通过设置选项runValidators: true 在更新时运行验证。

示例 1:


const Kitten = db.model('Kitten', kittenSchema);

const update =  color: 'blue' ;
const opts =  runValidators: true ;
Kitten.updateOne(, update, opts, function() 
  // code
);

示例 2:

const Kitten = db.model('Kitten', kittenSchema);

const update =  color: 'blue' ;
const opts =  runValidators: true ;
Kitten.updateOne(
  
    _id: req.params.id
  ,
  
    $set:  ...update ,
  ,
  opts
).then(result => 
    // code
)

阅读更多:https://mongoosejs.com/docs/validation.html#update-validators

【讨论】:

【参考方案6】:

如果您在findOneAndUpdate 选项中使用upsert,则接受的答案不起作用。解决这个问题的方法是创建一个模型静态方法,该方法执行findOne,然后在引擎盖下执行updateOnecreatecreate 自动运行验证。

export async function findOneAndUpdateWithValidation(
  this: LocationModel,
  filter: FilterQuery<LocationDocument>,
  update: UpdateQuery<LocationDocument>,
  options?: QueryOptions
) 
  const documentFound = await this.findOne(filter);
  
  if (!documentFound) return this.create(update);

  return this.updateOne(filter, update, options);


locationSchema.statics = 
  findOneAndUpdateWithValidation

【讨论】:

杰米我赞成这个答案,因为它实际上解决了这个问题。我花了最后一天在这个问题上着迷,这是一个从你的工作中获得灵感的通用解决方案。 gist.github.com/Corky3892/2452203657e5a31423d990faddf4ebad【参考方案7】:

在您的模型中,例如。 Category.js 文件:

const CategorySchema = mongoose.Schema(
category_name : 
type : String,
required : [true, 'Category Name Is Required !'],
trim : true,
maxlength : [30, 'Category Name Is To Long !'],
unique : true,
);
const Category = module.exports = mongoose.model("Category",CategorySchema);

在您的路线文件中:

router.put("/",(req,res,next)=>
  Category.findOneAndUpdate(
  _id : req.body.categoryId,
  $set : category_name : req.body.category_name ,
  **runValidators: true**, function(err,result) 
    if(err)
      if(err.code === 11000)
       var duplicateValue = err.message.match(/".*"/);
       res.status(200).json("defaultError":duplicateValue[0]+" Is Already Exsist !");
       else
         res.status(200).json("error":err.message || "defaultError":'Error But Not Understood !');
       
    else
     console.log("From category.js (Route File) = "+result);
     res.status(200).json("success":"Category Updated Successfully!!");
    
);

【讨论】:

不鼓励仅使用代码的答案。请解释代码的目的。此外,语法高亮也有帮助。

以上是关于为啥猫鼬在更新时不验证?的主要内容,如果未能解决你的问题,请参考以下文章

猫鼬在查找中排除时返回默认值

猫鼬在保存前等待查找完成

如何使用基于关键字的猫鼬在节点中进行搜索

如何在平均堆栈中使用猫鼬在 Mongodb 中存储值

猫鼬在循环中获取多个对象

为啥猫鼬不更新?