MongoDB - 在数组中搜索_id或不

时间:2012-07-11 15:00:52

标签: arrays mongodb

我想实现用户关注系统。用户可以关注其他用户。我正在考虑两种方法。一个是followers架构中有followeesUser,它们都是用户_id的数组。另一个是模式中只有followers。每当我想找到用户的关注者时,我都必须搜索所有用户的followers数组,即db.user.find( { followers: "_id" } );。这两种方法的优点和缺点是什么?感谢。

2 个答案:

答案 0 :(得分:1)

您正在考虑的是这里经典的“多对多”关系。与RDBMS不同,在这种架构中只有一个“正确”的正常形式,在MongoDB中,正确的架构设计取决于您使用数据的方式,以及此处未提及的其他一些因素

请注意,对于这个讨论,我假设“跟随”关系不对称 - 也就是说,A可以跟随B,而B不必遵循A.

1)在MongoDB中有两种基本方法来建模这种关系。

  • 您可以在用户文档中嵌入索引“跟随”数组。
  • 您可以单独收集“以下”文档,如下所示:

    {user:ObjectID(“x”),如下:ObjectID(“y”)}

对于以下每种关系,您在此集合中都有一个文档。你需要在这个集合上有两个索引,一个用于“user”,另一个用于“follow”。

请注意,您问题中的第二个建议(在用户文档中包含“跟随”和“跟随”的数组)只是第一个的变体。

2)正确的设计取决于您在此未提及的几个因素。

  • 一个人可以拥有多少粉丝,一个人可以关注多少人?
  • 您最常见的查询是什么?是提供关注者列表,还是提供正在关注的用户列表?
  • 您多久更新一次关注者/关注列表?

3)权衡取舍如下:

嵌入式阵列方法的优点是代码更简单,您可以在单个文档中获取整个跟随用户的数组。如果索引“跟随”数组,那么查找所有用户关注者的查询将相对较快,只要该索引完全适合RAM。 (这与关系数据库没有什么不同。)

如果您经常更新关注者,或者您允许无限数量的关注者/关注者,则会出现嵌入式阵列方法的缺点。

如果您允许无限数量的关注者/关注者,那么您可能会溢出MongoDB文档的最大大小。有些人拥有100K以上的粉丝并非闻所未闻。如果是这种情况,那么您将需要采用单独的收集方法。

如果您知道关注者会经常更新,那么您可能也想要使用单独的收集方法。原因是每次添加关注者时,都会增加“关注者”数组的大小。当它达到一定的大小时,它将超过在磁盘上为它保留的空间量,并且MongoDB将不得不移动文档。这将产生额外的写入开销,因为该文档的所有索引也必须更新。

4)如果你想使用嵌入式阵列方法,你可以采取一些措施使其更加可行。

首先,您可以限制一个人可以拥有的关注者总数。其次,当您创建新用户时,您可以创建具有预先创建的大量虚拟关注者的文档。 (例如,您使用大量条目填充'followers'数组,您知道这些条目不会引用任何实际用户 - 也许ID为0.)这样,当您添加新的关注者时,您将替换其中一个ID带有实际条目的0个条目,文档大小不会增长。

其次,您可以限制某人可以拥有的关注者数量,并在应用程序中检查该关注者。

请注意,如果您在文档中使用双数组方法,则会减少一个人可以拥有的最大关注者数量(因为文档的一部分将被他们关注的用户数组占用)。

5)作为优化,您可以更改要跟踪的“跟随”文档。因此,不是每个后续关系都有一个文档,您可以按用户将其删除:

   { user: "X", following: [ "A", "B", "C" ... ] }
   { user: "X", following: [ "H", "I", "J" ... ] } 
   { user: "Y", following: [ "A", "X", "K" ... ] } 

6)有关多对多模型的更多信息,请参阅此演示文稿:

有关“bucketing”设计模式的更多信息,请参阅MongoDB文档中的此条目:

答案 1 :(得分:0)

如果同时提供followersfollowees,那么您可以有效地为大多数查询提供服务,而无需在这两个字段中使用二级索引。例如,您可以检索当前用户,然后使用_id上的默认索引来检索所有连接的列表。

db.users.find({_id: {$in: user_A.followers}})

如果您不包含followees,则需要在followers上创建辅助索引,以便在没有收集扫描的情况下为某些查询提供服务。例如,要确定用户A的所有跟随者,您将使用如下查询:

db.users.find({followers: user_A._id})

二级索引会花费您一些内存和磁盘空间,但会避免潜在的数据不一致(跟随者和跟随者列表不匹配)。