对于 N:M 关系,在 MongoDB 中推荐的级联删除等效项是啥?

Posted

技术标签:

【中文标题】对于 N:M 关系,在 MongoDB 中推荐的级联删除等效项是啥?【英文标题】:What is the recommended equivalent of cascaded delete in MongoDB for N:M relationships?对于 N:M 关系,在 MongoDB 中推荐的级联删除等效项是什么? 【发布时间】:2013-12-20 16:04:02 【问题描述】:

假设以下“模式/关系”设计,使用类似级联删除的操作处理删除的推荐做法是什么?

关系架构:

+---------+ +--------+ |学生|-*--------1-[注册]-1--------*-|课程 | +---------+ +--------+

MongoDB:

+---------+ +--------+ |学生|-*----------------*-|课程 | +---------+ +--------+

考虑到学生注册课程的这种经典设计,在使用 MongoDB 时,在学生中包含课程集合(反之亦然)似乎是一个合适的数据模型(这对于关系/注册表来说没有任何意义)。但是来自关系世界的我应该如何处理删除课程的语义?也就是说,当一个课程被删除时,所有的“注册”记录也应该被删除。也就是说,我应该从每个学生记录的集合中删除课程。看起来我必须触发 2 个查询:一个用于删除课程,然后从每个学生的收藏中删除它。有没有办法让一个查询来执行这种“级联删除”之类的语义而无需额外的查询?数据模型需要改变吗?

注意:对于所有其他用例,上述数据模型可以正常工作:

删除学生=> 只需删除该学生以及随之删除的相关课程集合。 愿意放弃课程的学生=> 只需将其从学生课程集中删除即可 添加学生/课程=>实质上只需将其添加到相应的“表”中即可。

唯一棘手的事情是处理课程的删除。我应该如何在 MongoDB 中处理这种情况,因为我来自关系背景并且无法弄清楚这一点。

【问题讨论】:

没有级联删除。它需要分两步执行。 由于 MongoDB 的性质,如果在这种情况下必须执行“级联删除”之类的操作,我很难想象您正在正确设计数据库。同一模式有两种方法,一种是将课程嵌入到学生中,或者使用带有指向课程的指针的数组。无论如何,没有级联删除可用,Mongo 操作是孤立的文档更新-删除-插入,即使您使用正确的子句更新多个文档,它仍然一次处理一个文档。 嵌套有其自身的问题。在报告和更新操作期间它会适得其反(覆盖的索引/查询甚至不能在嵌套对象上进行)。我们对此的衡量标准基本上是:如果您需要对嵌套对象中的任何数据进行索引报告,或者计划独立于父对象更新嵌套对象,请将其设为单独的集合。 【参考方案1】:

您正在做的是在 Mongo 中最好和最优化的方式。我处于类似情况,在尝试了 N:M 设计模式的所有可能实现之后,也得出了相同的解决方案。

显然,这不是mongodb的东西,而是更多的NoSQL的概念,其中变化较少的数据(Courses)可以单独保存。而且由于删除课程不会是一个非常频繁的操作,因此可以通过所有记录来删除它。

另一方面,你可以让它保持原样。 在您的应用程序逻辑中,只需忽略 Student 文档中的 Courses 值,这些值在 Course 文档中根本没有 reference_id。但在这种情况下,您必须确保旧的已删除 Course_id 未被重复使用。

或者只使用课程文档中已删除的标志并处理应用程序逻辑中的所有其他内容。

【讨论】:

很好的答案!我唯一关心的是如何“确保不重复使用旧的已删除 Course_id”。 我同意。在我目前的工作中,我第一次遇到了一个基于 MongoDB 的非常复杂的项目,并且我很快就明白,当您需要以“关系术语”对数据进行建模时,面向文档的信息模型并不适合。需要保持设计简单和实体之间的低连接度 基于此:docs.mongodb.com/manual/reference/bson-types objectId 包含反映文档创建时间的时间戳。因此,我相信 objectId 值不会重复。您无需手动执行。【参考方案2】:

我将根据 Mongo 团队的建议进行回答。我也来自关系数据库,在开始理解这些概念时遇到了一些问题。 Mongo 团队建议使用“应用程序驱动”模式的思想进行设计,因此您必须首先弄清楚哪些数据块放在一起。请记住,在 Mongo 中以任何可能的方式都没有这样的事务概念,即使我们发明了一个处理事务的驱动程序,我们也应该为此实现自己的解决方案。这意味着如果我有两个需要始终同时更新的业务对象并且我不能容忍此操作失败,我必须将它们加入到单个文档中(原子)。

在您的情况下,您有两个文档,Student 和 Courses,以及它们之间的关系(一个学生注册了 N 个课程)。我认为课程不需要一直更改,因此可以将它们存储在不同的集合中。 但关键是它们之间的关系,在这种情况下,您需要自动删除一个学生和他注册的所有课程。 因此,最合适的解决方案是将关系嵌入到 Student 中,并保留一个单独的 Course 集合。当您删除学生时,关系同时被删除:

Json 学生:

 _id: ObjectId('...'), name:"John", lastname:"Smith", 
courses: [ 1, 100, 50, 67 ], ...

课程可以是它们之间的单独集合。 这是在 Mongo 中处理它的方式。原子操作必须嵌入到单个文档中。我假设 Courses 是一个变化不大的课程列表,如果它们是由 Student 设计的,我们可以稍微改变一下解决方案。

【讨论】:

我认为你误解了一些东西。我正在考虑删除一门课程,然后将其从所有学生中删除。你所说的就是我所说的容易/可行的。 好的,我明白了,但是在 MongoDb 中没有像级联删除这样的东西。您必须处理两个集合是孤立的这一事实,并且您可能会遇到不一致的问题。我要做的是首先“标记”我要删除的记录,然后通过“外键”删除其余记录。它可以通过一个单独的过程来完成,如果有任何不一致的情况,您可以重新开始,因为主记录仍将被标记。标记的记录当然应该从进程中排除。 最后,您可以删除主集合中的标记记录。由于 MongoDb 的性质,我认为这是一个很好的方法 @MaximilianoRios 问题 - 当课程被删除时要做什么外壳?你如何让学生保持同步? 正如我所提到的,没有任何开箱即用的机制可以做到这一点。如果你删除一个学生,你应该遍历整个学生集合并删除关系。另一种可能的解决方案是在访问时失败,这意味着当您访问此 John Smith 并阅读课程时,您可以删除不存在的课程。同样,Mongo 是一个文档数据库,所有内容都与单个文档相关,并且关系不受约束。

以上是关于对于 N:M 关系,在 MongoDB 中推荐的级联删除等效项是啥?的主要内容,如果未能解决你的问题,请参考以下文章

你如何处理数据库关系模式中的递归关系N:M

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

推荐一款关于MongoDB日志分析的工具--Mtools

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

12.创建关系时的级联操作

具有多对多关系的级联删除[重复]