MongoDB 中缺乏类似连接查询的数据库设计:如何使用其他集合中文档的引用

Database design in lack of join-like query In MongoDB: how to use refs to documents in other collections

我正在研究 MongoDB 以了解它在实际项目中作为重要数据存储平台的表现如何,但我在理解这个概念时遇到了一点问题,或者更确切地说,数据如何设计应该看起来像。我理解嵌入对象的想法,例如而不是你在 RDBMS 中做的事情,比如:

PEOPLE (Table):
id | Name
---------
 1 | John
 2 | Steve

PHONES (Table):
id | peopleId | phone
 1 |    1     | 555 66 77
 2 |    1     | 555 66 78
 3 |    2     | 555 11 22

在 MongoDB 中,您将在嵌入详细信息对象的集合中创建两个文档,例如:

{
  name: "John",
  phones: [
    {phone: "555 66 77"},
    {phone: "555 66 78"}
  ]
},
{
  name: "Steve",
  phones: [
    {phone: "555 11 22"}
  ]
}

现在这种方法很好,并且可以在每个主文档的细节对象相当独特的情况下使用(因为每个 phone 只属于一个人),但是一旦你走进(是的)领域!) 关系,如果细节不是其所有者所独有的,你就会遇到麻烦。考虑一本书/作者的关系。有很多本书,可能有不止一个作者,而许多作者会有不止一本书(多对多关系)。如果你在书中嵌入作者文档,你将不得不复制作者数据的次数与他有书的次数一样多。反之亦然,如果您将书籍嵌入到作者中,那么重复数据的次数将与该书的作者数量相同,即同一本书将出现在其他作者文档中。不用说,这会造成大量的数据一致性问题。

{
  book: "A Nice Title",
  authors: [
    {name: "Jane", age: 30},
    {name: "Tom", age: 20}
  ]
},
{
  book: "Some Other Nice Title",
  authors: [
    {name: "Jane", age: 29},
    {name: "Tom", age: 21}
  ]
}

像这里,简是30岁还是29岁?

现在,据我阅读 here 的理解,解决此问题的首选方法是将子文档保留在其自己的集合中并使用其 _id 而不是嵌入它(任何人都觉得我们回来了此时到 RDBMS?),通过手动执行并查询您获得的每个文档以获取详细信息(导致对每个文档进行许多查询,如果您有文档列表,则将其乘以文档数!)或使用据说在驱动程序级别而不是在服务器级别执行完全相同的事情的 DBRef,这意味着完全相同,只是不是由我完成,而是由驱动程序完成,因此适用相同的查询编号,即:网络开销,服务器开销,等待,等待...这是一个例子:

people:
{
  _id: 1,
  name: "John",
  phones: [
    {phones_id: 1},
    {phones_id: 2}
  ]
},
{
  _id: 2,
  name: "Steve",
  phones: [
    {phone_id: 3}
  ]
}

phones:

{
  _id: 1,
  phone: "555 66 77"
},
{
  _id: 2,
  phone: "555 66 78"
},
{
  _id: 3,
  phone: "555 11 22"
}

这意味着在我从中获取人员文档的第一个查询之后,我将不得不对 phones 集合再执行 3 次查询以获得实际的 phones 以生成列表人民 phone 数字。

在今天的数据加载中我可以立即告诉你:那是 不会飞。想象一下,这是一个 50.000 长的书单,每个书单有 10 位作者?我不会向服务器发送 500.001 查询只是为了获得 one 列表。

构造如下:

bookLinks:
{ bookId: 1, authorId: 1}, {bookId: 2, authorId: 1}...

只会让事情变得更糟:现在你必须对 link 进行一次查询,与生成文档的查询数量相同,然后 link 将它们写入书籍,然后进行多次查询对于作者,产生 550.001 次查询(50000 本书,每本书有 10 位作者)。

所以...因为任何实际项目显然都会同时具有可嵌入(phone 书籍)和不可嵌入(authors/books)模型,并且由于 MongoDB 不能在服务器级别将 dbrefs 解析为其他集合中的文档并嵌入它们,该怎么做?在这种情况下设计文档集合的正确或首选方法是什么?

我希望我能够足够准确地描述我的担忧。

注意:请不要建议通过本地缓存详细信息来减少查询次数(不:我仍然不会向服务器发送 300.001 个查询而不是 500.001 个)。这些方法只是对糟糕设计的修补,它们不会解决问题。

在某些情况下,您可以使用聚合管道和 its $lookup operator 来完成。像这样(抱歉,文档示例,不是您的示例)

db.orders.aggregate([
    {
      $lookup:
        {
          from: "inventory",
          localField: "item",
          foreignField: "sku",
          as: "inventory_docs"
        }
   }
])

本质上,这是一个左外连接,需要付出代价(更复杂的查询语法)。在你的情况下,你可能必须先 $unwind 你的数组。

Also, can you use multiple lookups ?

是的,它应该像在管道中放置几个​​ $lookup 步骤一样简单。