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

时间:2015-05-26 13:18:16

标签: node.js mongodb express mongoose

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

我正在使用mongoDB进行周末项目,它需要DB中的一些关系,这就是痛苦的全部:

我有三个系列:

Users
Lists
Texts

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

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

因此,需要做的是将属于某些列表的文本与这些列表完全相同。可以有无限的列表和文本,但列表相比之下会更少。

与我最初想到的相反,我还可以将引用放在每个文本文档中,而不是列表文档中的所有text-id。它实际上会产生影响,因为我可以通过一个查询来查找列表中的每个片段。甚至可以索引该参考。

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

很少有文本会出现在MANY列表中,因此数组不会真正爆炸。问题仍然存在,是否有可能扩展或实际上是用mongoDB实现它的更好方法?是否有助于限制文本可能的列表数量(可能)?是否有很少的配方:很多关系?

获取对已经完成的项目及其实现方式的参考甚至是很棒的(很少:很多关系)。我不敢相信,只要需要一些关系,每个人都会回避mongo DB。

原始问题

我将在目前为止看到的两个问题中将其分解: 1)假设一个列表由5个文本组成。如何引用列表中包含的文本?只需打开一个数组并将文本的_ids存储在那里?似乎那些阵列可能会长到月球和背部,减慢应用程序的速度?另一方面,文本需要没有列表,因此嵌入不是一个真正的选择。如果我想获得包含100个文本的列表的所有文本,那该怎么办...听起来像两个查询和一个包含100个字段的数组: - /。这种引用正确方法的方式是这样的吗?

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

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

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

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

5 个答案:

答案 0 :(得分:6)

关系通常不是一个大问题,但涉及关系的某些操作可能是。这在很大程度上取决于您尝试解决的问题,并且非常强烈地取决于结果集的基数和键的选择性。

我写过a simple testbed,它会根据典型的长尾分布生成数据。事实证明,MongoDB在关系方面通常比人们所认为的更好。

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

  • 外键限制:您必须自己管理这些内容,因此存在死链接的风险
  • 事务隔离:由于没有多文档事务,即使代码是正确的(从某种意义上说它永远不会尝试创建死链接),也有可能创建无效的外键约束,但是只是在运行时中断。此外,很难检查死链接,因为您可能正在观察竞争状况
  • 加入:MongoDB不支持联接,尽管带有$in的手动子查询在$in - 子句中可以扩展到数千项,前提是参考值已编入索引,当然

如果您需要执行大型连接,即如果您的查询是真正的关系,并且您需要相应地加入大量数据,那么MongoDB可能不太合适。但是,关系数据库中所需的许多连接都不是真正的关系,它们是必需的,因为您必须将对象拆分为多个表,例如因为它包含列表。

一个真正的'关系查询可以是"找到所有购买了产品的客户,这些产品在6月份的营业额中排名靠前的客户获得了4星级评价"。除非您有一个非常专业的架构,基本上是为了支持这个查询而构建的,否则您很可能需要查找所有订单,按客户ID对它们进行分组,获取顶部的 n 结果,使用这些是使用$in查询评分并使用其他$in查找实际客户。尽管如此,如果你可以限制自己到6月的10万客户,这是三次往返和一些快速$in查询。

只要RAM中的索引支持您的查询并且网络不完全拥塞,那么在典型的云硬件上可能会在10-30ms的范围内。在这个例子中,如果数据太稀疏,事情会变得混乱,即前10k用户几乎没有写过4个星级评论,这会迫使你编写足够智能的程序逻辑来继续迭代第一步,既复杂又复杂。但是,如果这是一个非常重要的情况,那么无论如何都可能有更适合的数据结构。

答案 1 :(得分:4)

将MongoDB与引用一起使用是解决性能问题的途径。什么是不使用的完美例子。这是m:n种关系,mn可以扩展到数百万。 MongoDB在我们拥有1:n(few)1:n(many)m(few):n(many)的地方运行良好。但不是在你有m(many):n(many)的情况下。这显然会导致2次查询和大量的内务管理。

答案 2 :(得分:1)

我不确定这个问题是否仍然存在,但我有类似的经历。
首先,我想说一下官方mongo documentation

  

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

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

  1. 如上所述,mongo根本不提供交易。
  2. 并且您没有外键约束。
  3. 即使您在文档之间有引用(DBRefs),您也将面临如何取消引用此文档的惊人问题。
  4. 每个项目 - 即使你在周末项目工作,也是一项巨大的责任。这可能意味着您应该编写许多代码来提供系统的简单行为(例如,您可以看到如何在mongo here中实现事务。)

    我不知道外键约束是怎么做的,我没有在mongo文档中看到这个方向的东西,这就是为什么我认为这是一个惊人的挑战(以及项目的风险)。

    最后,mongo引用 - 它不是mysql join,并且你没有收到来自父集合的所有数据与来自子集合的数据(如表中的所有字段和mysql中连接表中的所有字段),你将收到只需 REFERENCE 到另一个集合中的另一个文档,您将需要对此引用(取消引用)执行某些操作。 它可以通过回调轻松到达,但仅在您只需要一个列表中的一个文本的情况下,但如果您需要一个列表中的所有文本 - 这很糟糕,但如果您需要多个列表中的所有文本 - 它是变成噩梦...

    也许这不是我最好的经历......但我认为你应该考虑一下......

答案 3 :(得分:0)

在MongoDB中使用数组通常不是优选的,通常不会被专家建议。

这是一个我想到的解决方案:

<StatusBarItem Content="{Binding CurrentCodingSpecification, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="200"/> 的每个文档始终是唯一的。 Users中的单个文档可以有ListsTexts。因此,UsersLists有一个用户名ID字段,该字段将是Texts的{​​{1}}。

_id始终拥有Users中的所有者,因此它们会按原样存储。

Lists的所有者可以是UsersTexts,因此您还应该在其中保留一个LIST ID字段,其中UsersList }}

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

答案 4 :(得分:0)

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

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

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

使用这样的文本架构:

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

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

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

我仍然需要处理外键约束并编写我自己的“清理”函数,如果删除列表则负责删除引用 - 删除列表中每个文本中的引用。 幸运的是,这种情况很少发生,所以我可以偶尔浏览一下每一个文本。

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

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

我在自己的研究中发现最有帮助的was this vid,其中Alvin Richards也谈到了最近几分钟的多对多关系。 17.这就是我想到让关系片面的想法,以节省自己清理死亡参考的工作。

感谢帮助人员