Mongoose中的级联样式删除

Posted

技术标签:

【中文标题】Mongoose中的级联样式删除【英文标题】:Cascade style delete in Mongoose 【发布时间】:2012-12-30 04:30:39 【问题描述】:

有没有办法在 Mongoose 中删除父级的所有子级,类似于使用 mysql 的外键?

例如,在 MySQL 中,我会分配一个外键并将其设置为删除时级联。因此,如果我要删除客户端,所有应用程序和关联用户也会被删除。

从顶层:

    删除客户端 删除抽奖活动 删除提交内容

抽奖和提交都有一个 client_id 字段。 Submissions 有一个用于sweepstakes_id 和client_id 的字段。

现在,我正在使用以下代码,我觉得必须有更好的方法。

Client.findById(req.params.client_id, function(err, client) 

    if (err)
        return next(new restify.InternalError(err));
    else if (!client)
        return next(new restify.ResourceNotFoundError('The resource you requested could not be found.'));

    // find and remove all associated sweepstakes
    Sweepstakes.find(client_id: client._id).remove();

    // find and remove all submissions
    Submission.find(client_id: client._id).remove();

    client.remove();

    res.send(id: req.params.client_id);

);

【问题讨论】:

【参考方案1】:

这是 Mongoose 的 'remove' middleware 的主要用例之一。

clientSchema.pre('remove', function(next) 
    // 'this' is the client being removed. Provide callbacks here if you want
    // to be notified of the calls' result.
    Sweepstakes.remove(client_id: this._id).exec();
    Submission.remove(client_id: this._id).exec();
    next();
);

这样,当您调用client.remove() 时,会自动调用此中间件来清理依赖关系。

【讨论】:

不,在通过mongoose.modeldb.model 调用创建模型之前,您会在用于定义Client 模型的Schema 对象上调用它。 当我将它移到我的架构定义文件(模型)中时效果很好。不过,我必须将 .exec() 添加到删除调用的末尾。没有坏处,对吧? 您是对的,当您不提供回调时,您需要使用remove 执行此操作。我更新了答案。 @JohnnyHK:只有当我们调用 client.remove() 时才会调用这个方法。在我的应用程序中,我使用以下语句将其删除:Client.findByIdAndRemove(client_id,function(err)) 这个 pre hook 不会被调用吗? @Inquisitive 对,在这种情况下不会调用钩子。仅当您在文档实例上调用 remove 时才会调用它。【参考方案2】:

如果您的引用以其他方式存储,例如,client 有一个 submission_ids 数组,那么以与接受的答案类似的方式,您可以在 submissionSchema 上定义以下内容:

submissionSchema.pre('remove', function(next) 
    Client.update(
         submission_ids : this._id, 
         $pull:  submission_ids: this._id  ,
         multi: true )  //if reference exists in multiple documents 
    .exec();
    next();
);

这将从submission.remove()上的clients'引用数组中删除提交的 id。

【讨论】:

请您举一个使用 OneToMany 的例子吗?如果此代码删除所有子级并更新引用和父级,我是不对的。 不确定我是否理解您的评论。但让我用例子来解释我的答案。假设有 3 个客户端文档:C1、C2、C3。并且有许多提交文件,其中一个是S273。现在 S273 的 id 存在于 C1 和 C3 的 submission_ids 中(OneToMany,即客户端有很多提交)。我上面的回答解决了您要删除 S273 的情况。当您这样做时,S273 的 id 将自动从 C1 和 C3 的submission_ids 中删除。 嗯.. 我知道submissionSchemaClient schema 上引用的子集合对吗? 是的。这是“hasMany”的方式。客户端拥有许多提交 ID 的数组。另一种是“belongsTo”方式,每个提交文档都有一个属于它的客户端id。 @FrancisRodrigues 因为它与 NoSql 而非 Mongoose 相关(它只是 mongodb 的一个节点库/包装器,以帮助简化开发)。我不确定 mongodb 有一个部分来解释这一点,但 couchbase(另一个 NoSql db)有一个超级信息文档详细解释它。 developer.couchbase.com/documentation/server/4.6/data-modeling/…, developer.couchbase.com/documentation/server/3.x/developer/…, developer.couchbase.com/documentation/server/3.x/developer/…【参考方案3】:

这是我找到的另一种方式

submissionSchema.pre('remove', function(next) 
    this.model('Client').remove( submission_ids: this._id , next);
    next();
);

【讨论】:

删除回调中的next 是因为您无论如何都会在它下面的行中调用next 对吗?【参考方案4】:

我注意到这里的所有答案都有一个pre 分配给架构,而不是post

我的解决方案是:(使用 mongoose 6+)

ClientSchema.post("remove", async function(res, next)  
    await Sweepstakes.deleteMany( client_id: this._id );
    await Submission.deleteMany( client_id: this._id );
    next();
);

根据定义,post 在流程结束后执行pre => process => post

现在,您可能想知道这与此处提供的其他解决方案有何不同。 如果找不到服务器错误或该客户端的 id 怎么办? 在预先,它将在删除过程开始之前删除所有sweeptakessubmissions client。因此,如果出现错误,最好在client 或主文档被删除后级联删除其他文档。

async 和 await 在这里是可选的。但是,它对大数据很重要。这样如果删除进度仍在进行,用户就不会得到那些“将被删除”的级联文档数据。

最后,我可能是错的,希望这对他们的代码有所帮助。

【讨论】:

userSchema.post('findOneAndDelete', async id => await Comments.deleteMany( user: id ); ); findOneAndDelete 方法在这里也有效,似乎删除方法已被弃用

以上是关于Mongoose中的级联样式删除的主要内容,如果未能解决你的问题,请参考以下文章

SQL中的级联菱形删除

Ruby ActiveRecord 模型中的级联删除?

休眠 - 多对多关系中的级联删除

没有级联删除目标的级联删除关系

android房间数据库KOTLIN中的级联删除

15.翻译系列:EF 6中的级联删除EF 6 Code-First 系列