1. 程式人生 > >MongoDB資料庫關係表示和設計:(1)巢狀文件和引用連結

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'}]