不同集合中的文档之间的Mongo DB关系

我还没有准备好放弃这个,这就是为什么我重新思考问题并编辑了Q(原文如下)。


我正在使用mongoDB进行一个周末的项目,它需要DB中的一些关系,这就是悲惨的事情:

我有三个集合:

Users Lists Texts 

用户可以有文本和列表 – 列出“包含”文本。 文本可以在多个列表中。

我决定使用单独的集合(而不是embedded),因为子文档不总是出现在其父文档(例如,所有文本,而不在列表中)的上下文中。

所以需要做的是将属于某个列表的文本与那些列表完全对应。 可以有无限的列表和文本,但是列表比较less。

与我最初想到的不同的是,我也可以把参考文献放在每一个文本文件中,而不是所有文本中的文本。 这实际上会有所作为,因为我可以逃脱一个查询来查找列表中的每个片段。 甚至可以索引该参考。

 var TextSchema = new Schema({ _id: Number, name: String, inListID: { type : Array , "default" : [] }, [...] 

文本很less会出现在很多列表中,所以这个数组不会真的爆炸。 问题的种类仍然存在,是否有机会扩大或实际上是一个更好的方式与mongoDB实施? 是否有助于限制文本可能在(可能)的列表数量? 有几个很less的关系吗?

甚至可以很好的引用已完成的项目以及如何实施(less数几个关系)。 我不能相信,只要有一些关系需要,每个人都会避开孟戈。



原来的问题

我将在以下两个问题中把它分解:1)假设列表包含5个文本。 我如何参考列表中包含的文本? 只需打开一个数组并将文本的_ids存储在那里? 看起来像这些数组可能会成长到月球和背部,放慢了应用程序? 另一方面,文本需要没有列表,所以embedded不是一个真正的select。 如果我想要获得包含100个文本的列表的所有文本,听起来像两个查询和一个有100个字段的数组: – /。 那么这种引用正确的方式来做到这一点?

 var ListSchema = new Schema({ _id: Number, name: String, textids: { type : Array , "default" : [] }, [...] 

问题2)我看到这种方法是清除引用如果文本被删除。 它的引用仍然在每个包含文本的列表中,我不想遍历所有的列表来清除那些死的引用。 或者我会? 有一个聪明的方法来解决这个问题吗? 只是使文本保持参考(在哪个列表中)只是移动问题,所以这不是一个选项。

我想我不是第一个遇到这样的问题,但是我也无法find一个关于如何做到这一点的明确答案。

我也对这种引用(多对多)的最佳实践以及特别的可伸缩性/性能感兴趣。

关系通常不是一个大问题,尽pipe涉及关系的某些操作可能是。 这在很大程度上取决于您正在尝试解决的问题,并且非常依赖于结果集的基数和密钥的select性。

我写了一个简单的testing平台 ,可以在典型的长尾分布之后生成数据。 事实certificate,MongoDB通常比人们相信的关系更好。

毕竟,关系数据库只有三个不同之处:

  • 外键约束:你必须自己pipe理这些,所以死链接有一些风险
  • 事务隔离:由于没有多文档事务,所以即使代码是正确的(即从不尝试创build死链接),但是仅在运行时中断,也有可能创build无效的外键约束。 此外,由于您可能正在观察竞争状况,因此很难检查死链
  • 连接:MongoDB不支持连接,尽pipe带有$in的手动子查询可以很好地扩展到$in clause中的几千个项目,当然,前提是参考值被编入索引

如果您需要执行大型连接,也就是说,如果您的查询是真正的关系数据,并且您需要大量的相应数据,那么MongoDB可能不太适合。 然而,关系数据库中需要的许多连接并不是真正的关系,因为必须将对象拆分为多个表,因为它包含一个列表。

一个“真正的”关系查询的例子可能是“find我所有购买了6月份营业额高的客户获得> 4星评价的产品的客户”。 除非您有一个非常专业化的架构,基本上是为支持此查询而构build的,否则您很可能需要查找所有订单,按客户ID对其进行分组,获取前n个结果,使用这些查询来使用$in查询评级$in然后使用另外$infind实际的客户。 不过,如果你可以将自己限制在最高层,比如说6月份的10K客户,那么这是三次往返和一些快速$in查询。

在典型的云硬件上,这可能会在10-30毫秒的范围内,只要你的查询由RAM中的索引支持,并且networking不完全拥塞。 在这个例子中,如果数据太稀疏的话,事情会变得很混乱,即前10k的用户很难写出> 4星评价,这会迫使你编写足够聪明的程序逻辑,以保持迭代复杂和缓慢的第一步但是如果这是一个非常重要的情况,那么无论如何可能会有更好的数据结构。

通过引用使用MongoDB是性能问题的一个途径。 什么不使用的完美的例子。 这是一种mn可以缩放到数百万的关系。 MongoDB在1:n(few)1:n(many)m(few):n(many)地方工作得很好。 但不是在你有m(many):n(many)情况下m(many):n(many) 。 这显然会导致2个查询和很多家务。

我不确定这个问题还是真实的,但是我有类似的经验。
首先我想说的是什么告诉官方mongo 文件 :

在以下情况下使用embedded式数据模型:您有一对一或一对多模型。
用于模型与文档引用的多对多使用关系。

我认为是答案),但这个答案提供了很多问题,因为:

  1. 如上所述,mongo根本不提供交易。
  2. 而且你没有外键限制。
  3. 即使你有文件之间的引用( DBRefs ),你将会面临如何解除引用这个文件的惊人的问题。

每个这个项目 – 是一个巨大的责任,即使你在周末项目工作。 这可能意味着你应该写很多代码来提供你的系统的简单行为(例如,你可以看到如何在这里实现mongo事务)。

我不知道外键约束是如何完成的,在mongo文档中我没有看到这方面的东西,这就是为什么我认为这是一个惊人的挑战(和项目风险)。

最后,mongo引用 – 它不是mysql连接,而且你不会从父集合中获得来自子集合的数据(就像表中的所有字段和mysql中连接的表中的所有字段一样),你将只收到参考在另一个集合中的另一个文档,你将需要做这个引用(取消引用)。 它可以很容易地通过callback到达节点,但只有当你只需要一个列表中的一个文本,但如果你需要在一个列表中的所有文本 – 这是可怕的,但如果你需要在多个列表中的所有文本 – 这是成为噩梦…

也许这不是我最好的体验…但我想你应该考虑一下…

在MongoDB中使用数组通常是不可取的,一般不会被专家build议。

这是我脑海中的一个解决scheme:

Users每个文档都是唯一的。 用户中可以有个别文档的ListsTexts 。 因此, ListsTexts有一个USER ID的字段,它将是Users_id

Users Lists总是有一个所有者,所以它们按原样存储。

Texts所有者可以是UsersList ,所以你应该保留一个字段的列表ID也在里面,这将是_id Lists

现在请注意, Texts不能同时具有用户ID和LIST ID,所以您必须保留一个条件,即两者中只有一个,另一个应该为null以便我们可以容易地知道谁是Texts的主要所有者。

写一个答案,因为我想解释我将如何从这里开始。

考虑到这里的答案以及我对这个主题的研究,实际上可能会把这些引用(不是真正的关系)存储在一个数组中,试图保持它相对较小:在我的情况下,可能不到1000个字段。

特别是因为我可以逃避一个查询(我第一次,虽然我不能),到目前为止甚至不需要使用$in ,但我相信这个方法将会扩展。 毕竟这只是一个周末项目,所以如果没有,我最终会重写 – 没关系。

有了这样的文本模式:

 var textSchema = new Schema({ _id: {type: Number, required: true, index: { unique: true }}, ... inList: { type : [Number] , "default" : [], index: true } }); 

我可以简单地使用此查询获取列表中的所有文本,其中inList是包含列表中文本的inList的索引数组。

 Text.find({inList: listID}, function(err, text) { ... }); 

我仍然需要处理外键约束,并编写我自己的“清理”函数,在删除列表时删除引用 – 删除列表中的每个文本中的引用。 幸运的是,这种情况很less会发生,所以我可以偶尔阅读每篇文章。

另一方面,如果文本被删除,我不必关心删除列表文档中的引用,因为我只在关系的一边存储引用(在文本文档中)。 在我看来很重要的一点!

@mnemosyn:感谢您的链接,并指出这确实不是一个大连接或换句话说:只是一个非常简单的关系。 还有一些数字,这些复杂的操作需要多长时间(ofc硬件dependet)是一个很大的帮助。
PS:Grüßeaus Bielefeld。

在我自己的研究中 ,我发现最有帮助的就是这个video ,艾文·理查兹(Alvin Richards)也在分钟左右谈到了多对多的关系。 17.这就是为什么我要把这种关系放在一边,为自己节省一些清理死亡参考的工作。

谢谢你的帮助👍