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程式碼- >>> huangz
- {'friends': [{'name': 'peter'}, {'name': 'john'}, {'name': 'marry'}], 'name': 'huangz'}
巢狀的好處是顯而易見的:巢狀文件維持了資料邏輯上的完整性,可以將一整項資料作為一個整體來操縱。
對比在關係式的資料庫中, 為了設計出符合正規化的表,我們常常要將多個數據項分拆為幾個表,然後通過外來鍵獲取資料。
當你閱讀一個單獨的表的資料時,你通常只看到了其中一部分資料,而其他的都是外來鍵id,就像這樣:
Sql查詢的輸出程式碼- ['name': 'huangz', 'friends_reference_id': [12, 26, 30]]
這種資料給人的感覺就像打開了一本電話簿,卻發現裡面只有電話號碼,沒有聯絡人姓名,真是太糟糕了阿。
引用連結
比起巢狀,引用連結更接近傳統意義上的(也就是,關係型資料庫術語中的)“引用”,它是兩個文件之間的一種關係。
引用連結通過DBRef物件建立,DBRef物件儲存瞭如何找到目標文件的資訊,就像現實世界中的門牌號碼一樣(也類似關係型資料庫中的外來鍵)。
如果在一個文件A中,有一個DBRef物件,而這個DBRef物件儲存了關於如何找到文件B的資訊,那麼文件A就可以通過解釋這個DBRef物件(稱之為解引用)來獲取文件B的資料。
下面是建立一個文件的引用,以及解引用的過程,這次我們同樣表示一個一對多的朋友關係,但這次,我們用連結來建立關係。
Python程式碼- >>> # 資料
- >>> peter = {'name':'peter'}
- >>> marry = {'name':'marry'}
- >>> john = {'name':'john'}
- >>>
- >>> # 插入資料
- >>> c.test.people.insert([peter, john, marry])
- [ObjectId('4e98075224b7d408dc000004'), ObjectId('4e98075224b7d408dc000005'), ObjectId('4e98075224b7d408dc000006')]
- >>>
- >>> # 建立huangz文件,以及指向各個朋友的連結
- >>> huangz = {'name': 'huangz',
- ... 'friends': [ DBRef('people', peter['_id']),
- ... DBRef('people', john['_id']),
- ... DBRef('people', marry['_id']) ]}
- >>>
- >>> c.test.people.insert(huangz)
- ObjectId('4e9807d924b7d408dc000007')
- >>>
- >>> # 檢視huangz文件
- >>> huangz
- {'_id': ObjectId('4e9807d924b7d408dc000007'), 'friends': [DBRef('people', ObjectId('4e98075224b7d408dc000004')), DBRef('people', ObjectId('4e98075224b7d408dc000005')), DBRef('people', ObjectId('4e98075224b7d408dc000006'))], 'name': 'huangz'}
- >>>
- >>> # 對friends中的所有域進行解引用
- >>> [ c.test.dereference(friend) for friend in huangz['friends'] ]
- [{u'_id': ObjectId('4e98075224b7d408dc000004'), u'name': u'peter'}, {u'_id': ObjectId('4e98075224b7d408dc000005'), u'name': u'john'}, {u'_id': ObjectId('4e98075224b7d408dc000006'), u'name': u'marry'}]