作者:k172ausoreor_878 | 来源:互联网 | 2016-02-29 23:52
很多初学者认为在MongoDB中针对一对多建模唯一的方案就是在父文档中内嵌一个数组子文档,但是这是不准确的。因为你可以在MongoDB内嵌一个文档不代表你就必须这么做。
当你设计一个MongoDB数据库结构,你需要先问自己一个在使用sql时不会考虑的问题:这个关系中集合的大小是什么样的规模?你需要意识到一对很少,一对许多,一对非常多,这些细微的区别。不同的情况下你的建模也将不同。
一对很少
一个人的地址为例,这时候使用内嵌文档是很合适,可以在person文档中嵌入数组地址文档:
{
name: ‘Kate Monster’,
ssn: ’123-456-7890′,
addresses : [
{ street: '123 Sesame St', city: 'Anytown', cc: 'USA' },
{ street: '123 Avenue Q', city: 'New York', cc: 'USA' }
]
}
这种设计拥有内嵌文档设计中所有的优缺点。最主要的优点就是不需要单独执行一条语句去获取内嵌的内容。最主要的缺点是你无法把这些内嵌文档当做单独的实体去访问。
一对多
以商品替换零件订货系统为例。每个商品有数百个可替换的零件,但是不会超过数千个。这个用例很适合使用间接引用-将零件的objectid作为数组存放在商品文档中(在这个例子中我使用更加易读的2字节的ObjectID,现实世界中他们可能是由12个字节组成的)。
{
_id : ObjectID(‘AAAA’),
partno : ’123-aff-456′,
name : ‘#4 grommet’,
qty: 94,
cost: 0.94,
price: 3.99
}
{
name : ‘left-handed smoke shifter’,
manufacturer : ‘Acme Corp’,
catalog_number: 1234,
parts : [ // array of references to Part documents
ObjectID('AAAA'), // reference to the #4 grommet above
ObjectID('F17C'), // reference to a different Part
ObjectID('D2AA'),
// etc
]
在获取特定产品中所有零件,需要一个应用层级别的join
为了能快速的执行查询,必须确保products.catalog_number有索引。当然由于零件中parts._id一定是有索引的,所以这也会很高效。
这中引用的方式是对内嵌优缺点的补充。每个零件是个单独的文档,可以很容易的独立去搜索和更新他们。使用这种建模方式需要考虑的一个问题是需要一条单独的语句去获取零件的具体内容
这种建模方式中的零件部分可以被多个产品使用,所以在多对多时不需要一张单独的连接表。
一对很多
我们用一个收集不同机器日志的例子来讨论一对很多的问题。由于每个mongodb的文档有16M的大小限制,所以即使你是存储ObjectID也是不够的。我们可以使用很经典的处理方法“父级引用”—用一个文档存储主题,在每个日志文档中保存这个主机的ObjectID。
{
_id : ObjectID(‘AAAB’),
name : ‘goofy.example.com’,
ipaddr : ’127.66.66.66′
}
{
time : ISODate(“2014-03-28T09:42:41.382Z”),
message : ‘cpu is on fire!’,
host: ObjectID(‘AAAB’) // Reference to the Host document
}
以下是个稍微不同的应用级别的join用来查找一台主机最近5000条的日志信息
所以,即使这种简单的讨论也有能察觉出mongobd的建模和关系模型建模的不同之处。你必须要注意一下两个因素:
一对多中的多是否需要一个单独的实体。
这个关系中集合的规模是一对很少,很多,还是非常多。
基于以上因素来决定采取一下三种建模的方式
一对很少且不需要单独访问内嵌内容的情况下可以使用内嵌多的一方的方案。
一对多且多的一段内容因为各种理由需要单独存在的情况下可以使用通过数组的方式引用多的一方的方案。
一对非常多的情况下,请将一的那端引用签入进多端的方案。