MongoDB数据库关系表示和设计:(1)嵌套文档和引用链接

使用数据的时候,一个数据项常常和另外的一个或多个数据项产生关系,比如一个“人”对象,有一个名字,可能有多个电话号码,以及多个子女,等等。

 

在传统的SQL数据库中,关系被分为一个个表(table),在表中,每个数据项以主键(primary key)标识,而一个表的主键又作为另一个表的外键(reference key),在两个表之间引用。当遇上多对多关系的时候,还需要一个额外的关联表(reference table),将多对多关系转化成两个一对多关系。

 

而在MongoDB中,表示关系有两种办法:

 

一种是嵌套(embedded),既是将一个文档包裹一个子文档;

 

而另一种是引用链接(reference link),使用MongoDB的DBRef对象建立文档和文档之间的关系。

 

除此之外,MongoDB的关系比起传统的SQL表关系更丰富一些,可以有 1对1 , 1对N , N对1 和 N对N 几种关系。

 

本文的目的就是探讨MongoDB表示关系的方法。

 

首先,让我们来看看MongoDB表示数据关系的两种方式:嵌套和引用链接。

 

嵌套

 

每个MongoDB文档都由BSON文档组成,有类似JSON格式一样的数据类型,其中String、Int、Float称为基本类型(或常量),而Hash和Array称之为复合类型。

 

所谓的嵌套,就是说文档中,利用复合类型,包裹一个多或多个其他类型的值,这些值称之为子文档。

 

文档嵌套的数量和深度没有限制,但MongoDB目前版本限制一个文档最大为16MB。

 

下面是一个典型的个人档案文档(profile),其中有一个 name 常量域,还嵌套了一个朋友(firends)子数组,数组里面每个项是一个字典。

 

Python代码   收藏代码
  1. >>> huangz  
  2. {'friends': [{'name''peter'}, {'name''john'}, {'name''marry'}], 'name''huangz'}  

 

嵌套的好处是显而易见的:嵌套文档维持了数据逻辑上的完整性,可以将一整项数据作为一个整体来操纵。

 

对比在关系式的数据库中, 为了设计出符合范式的表,我们常常要将多个数据项分拆为几个表,然后通过外键获取数据。

 

当你阅读一个单独的表的数据时,你通常只看到了其中一部分数据,而其他的都是外键id,就像这样:

 

Sql查询的输出代码   收藏代码
  1. ['name''huangz''friends_reference_id': [122630]]  

 

这种数据给人的感觉就像打开了一本电话簿,却发现里面只有电话号码,没有联系人姓名,真是太糟糕了阿。

 

 

引用链接

 

比起嵌套,引用链接更接近传统意义上的(也就是,关系型数据库术语中的)“引用”,它是两个文档之间的一种关系。

 

引用链接通过DBRef对象建立,DBRef对象储存了如何找到目标文档的信息,就像现实世界中的门牌号码一样(也类似关系型数据库中的外键)。

 

如果在一个文档A中,有一个DBRef对象,而这个DBRef对象储存了关于如何找到文档B的信息,那么文档A就可以通过解释这个DBRef对象(称之为解引用)来获取文档B的数据。

 

下面是建立一个文档的引用,以及解引用的过程,这次我们同样表示一个一对多的朋友关系,但这次,我们用链接来建立关系。

 

Python代码   收藏代码
  1. >>> # 数据  
  2. >>> peter = {'name':'peter'}  
  3. >>> marry = {'name':'marry'}  
  4. >>> john = {'name':'john'}  
  5. >>>  
  6. >>> # 插入数据  
  7. >>> c.test.people.insert([peter, john, marry])  
  8. [ObjectId('4e98075224b7d408dc000004'), ObjectId('4e98075224b7d408dc000005'), ObjectId('4e98075224b7d408dc000006')]  
  9. >>>  
  10. >>> # 建立huangz文档,以及指向各个朋友的链接  
  11. >>> huangz = {'name''huangz',  
  12. ...     'friends': [ DBRef('people', peter['_id']),  
  13. ...                  DBRef('people', john['_id']),  
  14. ...                  DBRef('people', marry['_id']) ]}  
  15. >>>  
  16. >>> c.test.people.insert(huangz)  
  17. ObjectId('4e9807d924b7d408dc000007')  
  18. >>>  
  19. >>> # 查看huangz文档  
  20. >>> huangz  
  21. {'_id': ObjectId('4e9807d924b7d408dc000007'), 'friends': [DBRef('people', ObjectId('4e98075224b7d408dc000004')), DBRef('people', ObjectId('4e98075224b7d408dc000005')), DBRef('people', ObjectId('4e98075224b7d408dc000006'))], 'name''huangz'}  
  22. >>>  
  23. >>> # 对friends中的所有域进行解引用  
  24. >>> [ c.test.dereference(friend) for friend in huangz['friends'] ]  
  25. [{u'_id': ObjectId('4e98075224b7d408dc000004'), u'name': u'peter'}, {u'_id': ObjectId('4e98075224b7d408dc000005'), u'name': u'john'}, {u'_id': ObjectId('4e98075224b7d408dc000006'), u'name': u'marry'}]  
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值