在开始对数据建模之前,您已经完成了定义用例的第一步,也是正确的一步。
但是,您误解了相关数据需要 RDBMS。
model relationships between documents有几种方式。
注意:为了简洁和易于理解,以下示例进行了高度简化
嵌入
可以简单地通过嵌入来建模 1:1 关系:
{
_id: "joe",
name: "Joe Bookreader",
address: {
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
}
1:many 关系也可以通过嵌入来建模:
{
_id: "joe",
name: "Joe Bookreader",
phone:[
{ type:"mobile", number:"+1 2345 67890"},
{ type:"home", number:"+1 2345 987654"}
]
}
参考文献
与 RDBMS 相比的主要区别在于您在应用程序代码中解析引用,如下所示。
隐式引用
假设我们有出版商和书籍。发布者文档可能如下所示
{
_id: "acme",
name: "Acme Publishing, Inc."
}
书籍文档可能看起来像这样
{
_id:"9788636505700",
name: "The Book of Foo",
publisher: "acme"
}
现在重要的部分来了:我们基本上有两个可以用这些数据覆盖的用例。第一个是
对于“The Book of Foo”,获取出版商的详细信息
很简单,因为我们已经有了“The Book of Foo”和它的价值观
db.publishers.find({_id:"acme"})
另一个用例是
Acme Publishing, Inc. 出版了哪些书籍?
既然我们有 Acme Publishing, Inc 的数据,这很容易:
db.books.find({publisher:"acme"})
显式引用
MongoDB有一个引用的概念,通常称为DBRefs
但是,这些引用由驱动程序解析,而不是由 MongoDB 服务器解析。就我个人而言,我从未使用过或不需要它,因为隐式引用在大多数情况下都能完美运行。
“用户发帖”的示例建模
假设我们有一个类似的用户文档
{
_id: "joe",
name: "John Bookreader",
joined: ISODate("2015-05-05T06:31:00Z"),
…
}
如果天真地这样做,我们只是将帖子嵌入到用户文档中。
但是,这将限制一个人可以发布的帖子数量,因为 BSON 文档的硬编码大小限制为 16MB。
因此,很明显,每个帖子都应该有自己的文档,并注明作者:
{
_id: someObjectId,
title: "My first post",
text: "Some text",
author: "joe"
}
但是,这带来了一个问题:我们想要显示作者的姓名,而不是他的 ID。
对于单个帖子,我们可以简单地在用户集合中进行查找以获取名称。但是当我们想要显示帖子列表时怎么办?这将需要大量查询。因此,我们使用冗余来保存这些查询并优化我们的应用程序:
{
_id: someObjectId,
title: "My first post",
text: "Some text",
author: { id:"joe", name: "Joe Bookreader"}
}
因此,对于帖子列表,我们可以将它们与发布者的姓名一起显示,而无需额外查询。只有当用户想要获取有关海报的详细信息时,您才会通过他的 id 查找海报。有了这个,我们为一个常见的用例保存了很多查询。您可能会说“停止!如果用户更改了他的名字怎么办?”好吧,对于初学者来说,这是一个相对罕见的用例。即使在什么时候,这也不是什么大问题。首先,我们必须更新用户文档:
db.users.update({"_id":"joe"},{$set:{name:"Joe A. Bookreader"}})
然后,我们必须采取额外的步骤:
db.posts.update(
{ "author.id": "joe" },
{ $set:{ "author.name": "Joe A. Bookreader" }},
{ multi: true}
)
当然,这有点贵。但是我们在这里做了什么?我们以一个相当罕见的用例为代价优化了一个常见的用例。在我的书中很划算。
我希望这个简单的示例可以帮助您更好地理解如何使用 MongoDB 处理应用程序的用例。