首页 > 解决方案 > 当MongoDB中至少需要一个子文档时,有什么更好的方法来防止删除所有子文档

问题描述

采用 mongoose 中定义的简单模式,如下所示:

const Person = new Schema({
username: {
    type: String,
    index: true,
},
roles: [{
    type: Schema.Types.ObjectId,
    ref: 'Role',
    required: [true,
        'Role is required'],
}],
})

当您在不提供至少一个角色的情况下保存新记录时,MongoDB 将抛出需要角色的错误。

我有另一段代码,您可以在其中更新(添加或删除)角色数组中的角色。但是,我可以使用下面的代码段删除所有角色。

Person.findOneAndUpdate({_id: 'some id'}, {
        $pull: {
            roles: {
                $in: [...list of ids,]
            }
        }
    }, {new: true}).populate('roles').exec()

假设我列出了该人拥有的所有角色,我将删除它们,使该人没有任何角色。

如何强制执行需要角色的早期要求?是否有一个简单的 hack 或者我应该只写一段代码来验证这个删除并自己执行它?

标签: node.jsmongodbmongoose

解决方案


您之所以能够删除所有角色,并且所需的验证不起作用,因为验证是作为 Mongoose 中的内部中间件实现的,并且在更新期间不会执行中间件。你可以在这里阅读更多关于它的信息。

如果您使用的是MongoDB 4.0 或更高版本,则可以在调用中使用runValidators: true选项。您可以在Mongoose 更新验证器文档update中阅读有关 runValidators 选项的更多信息。

你可以试试下面的代码(虽然我还没有测试过):

Person.findOneAndUpdate({_id: 'some id'}, {
        $pull: {
            roles: {
                $in: [...list of ids,]
            }
        }
    }, {new: true, runValidators: true}).populate('roles').exec()

根据官方 Mongoose 文档,更新验证器适用于以下运算符(和 MongoDB 版本):

  • $set
  • $未设置
  • $push (>= 4.8.0)
  • $addToSet (>= 4.8.0)
  • $pull (>= 4.12.0)
  • $pullAll (>= 4.12.0)

或者你也可以做的是设置一个Pre钩子findOneAndUpdate

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

如果你有MongoDB < 4.0

然后您可以使用 找到文档,在文档中.findOne()设置新属性,然后尝试使用 再次保存.save()。这应该运行验证。


推荐阅读