我正在干预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"}
]
}
现在这种方法一切都很好,并且可以在每个主文档中细节对象相当独特的情况下工作(因为每个手机只属于一个人),但是一旦你进入(是!)关系的领域,其中细节对于他们的主人来说并非严格独特,你会遇到麻烦。 Cosider书籍/作者关系。有很多书,可能有一个以上的作者,而许多作者将有不止一本书(多对多的关系)。如果您将作者文档嵌入书籍中,则必须与作者书籍一样多次复制作者数据。相反,如果您将书籍嵌入到作者中,您将拥有与该书作者相同的重复数据,即同一本书将出现在其他作者文档中。毋庸置疑,这会造成数据整合问题。
{
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,据说在DRIVER级别执行完全相同的操作,而不是在服务器级别,这意味着完全相同,只是我没有完成,而是由驱动程序完成,所以相同的查询号码适用,即:网络开销,服务器开销,等待,等待...这是一个例子:
people:
{
_id: 1,
name: "John",
phones: [
{phones_id: 1},
{phones_id: 2}
]
},
{
_id: 2,
name: "Steve",
phones: [
{phone_id: 3}
]
}
电话:
{
_id: 1,
phone: "555 66 77"
},
{
_id: 2,
phone: "555 66 78"
},
{
_id: 3,
phone: "555 11 22"
}
这意味着在我获得人员文件的第一个查询之后,我将不得不通过电话收集再进行3次查询,以获取实际电话以生成人员电话号码列表。
在今天的数据加载中,我可以告诉你:不会飞。想象一下,这是一本 50.000 长书单,每本书有10位作者?我没有向服务器发送 500.001 查询,只是为了获得一个列表。
构造如:
bookLinks:
{ bookId: 1, authorId: 1}, {bookId: 2, authorId: 1}...
只会让事情变得更糟:现在你必须对链接进行一次查询,将相同数量的查询作为结果文档将它们链接到书籍,然后对作者进行一些查询,从而产生550.001个查询(50000本书)每个都有10位作者。
所以......因为任何真正的项目显然都有嵌入式(电话簿)和非嵌入式(作者/书籍)模型,并且由于MongoDB无法在服务器级别将dbrefs解析为其他集合中的文档并嵌入它们,是要走的路?在这种情况下设计文档集合的正确或首选方法是什么?
我希望我能够准确地描述我的疑虑。
注意:请不要建议本地缓存详细信息以减少查询次数(否:我仍然不会向服务器发送300.001查询而不是500.001)。这样的方法可能是设计不佳的补丁,但它们无法解决问题。
答案 0 :(得分:1)
在某些情况下,您可以使用汇总管道和its $lookup
operator来完成此操作。像这样的东西(抱歉,doc示例,不是你的例子)
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
基本上,这是一个左外连接,需要付出代价(更复杂的查询语法)。在您的情况下,您可能必须首先$unwind
数组。
另外,你可以使用多次查找吗?
是的,它应该像在管道中放置几个$lookup
步骤一样简单。