標籤:
轉文:
本篇部落格翻譯自:
http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1?mkt_tok=3RkMMJWWfF9wsRonsq7Ldu%2FhmjTEU5z14uUsUKGxhokz2EFye%2BLIHETpodcMTcVnM7zYDBceEJhqyQJxPr3FLdcN0tJuRhTrCw%3D%3D
備忘:本譯文不是嚴格意義上的翻譯,只是在基於對該原文的理解之上,儘可能表達清楚。如有疑問或不妥,請參考原文。
很多剛從傳統SQL開發轉向MongoDB開發的朋友都會問到一個問題:如何用MongoDB表達傳統關聯式資料庫中的一對多(1 to n)關係?
基於MongoDB豐富的表達力,我們不能說我們必須採用一個標準的方法來進行1 to n的建模。稍後我們從3個具體情境來展開講解。
首先,我們將1 to n中的n進行情境細化。這個n究竟代表多大的量級呢?是幾個到幾十個?還是幾個到幾千個?還是成千上萬個?
1) 1 to n(n代表好幾個,或幾十個,反正不太多)
比如每個Person會有多個Address。此種情況下,我們採用最簡單的嵌入式文檔來建模。
{ name: ‘Kate Monster‘, id: ‘123-456-7890‘, addresses : [ { street: ‘123 Sesame St‘, city: ‘Anytown‘, cc: ‘USA‘ }, { street: ‘123 Avenue Q‘, city: ‘New York‘, cc: ‘USA‘ } ]}
這種建模的方式包含了顯而易見的優點和缺點:
優點:你不需要執行單獨的查詢就可以獲得某個Person的所有Address資訊。
缺點:你無法像操作獨立文檔那樣來操作Address資訊。你必須首先操作(比如查詢)Person文檔後,才有可能繼續操作Address。
在本執行個體中,我們不需要對Address進行獨立的操作,且Address資訊只有在關聯到某一個具體Person後才有意義。所以結論是:採用這種embedded(嵌入式)建模是非常適合Person-Address情境的。
2)1 to n(n代表好些個,比如幾十個,甚至幾百個)
比如產品(Product)和零組件(part),每個產品會有很多個零組件。這種情境下,我們可以採用引用方式來建模,如下:
零組件(Part):{ _id : ObjectID(‘AAAA‘), partno : ‘123-aff-456‘, name : ‘#4 grommet‘, qty: 94, cost: 0.94, price: 3.99} 產品(Product):{ 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 ]}
首先每個part作為單獨的文檔存在。每個產品中包含一個數群組類型欄位(parts),這個數組中存放的是所有該產品包含的零組件的編號(_id主鍵)。當你需要根據某一個產品編號查詢該產品包含的所有組件資訊時,你可以執行以下操作:
> product = db.products.findOne({catalog_number: 1234}); // Fetch all the Parts that are linked to this Product> product_parts = db.parts.find({_id: { $in : product.parts } } ).toArray() ;
這種建模方式的優缺點也非常明顯:
優點:組件是作為獨立文檔(document)存在的,你可以對某一組件進行獨立的操作,比如查詢或更新。
缺點:如上,你必須通過兩次查詢才能找到某一個產品所屬的所有組件資訊。
在本例中,這個缺點是可以接受的,本身實現起來也不難。而且,通過這種建模,你可以輕易的將1 to n擴充到n to n,即一個產品可以包含多個組件,同時一個組件也可以被多個產品所引用(即同一組件可以被多個產品使用)。
3)1 to n(這個n代表很大的數值,比如成千上萬,甚至更大)
比如,每一個機器(host)會產生很大數量的日誌資訊(logmsg)。在這種情況下,如果你採用嵌入式建模,則一個host文檔會非常龐大,從而輕易超過MongoDB的文檔大小限制,所以不可行。如果你採用第二中方式建模,用數組來存放所有logmsg的_id值,這種方式同樣不可行,因為當日止很多時,即使單單引用objectId也會輕易超過文檔大小限制。所以此時,我們採用以下方式:
機器(hosts):{ _id : ObjectID(‘AAAB‘), name : ‘goofy.example.com‘, ipaddr : ‘127.66.66.66‘} 日誌(logmsg):{ time : ISODate("2014-03-28T09:42:41.382Z"), message : ‘cpu is on fire!‘, host: ObjectID(‘AAAB‘) // Reference to the Host document}
我們在logsmg中,存放對host的_id引用即可。
綜上所述,在對1 to n關係建模時,我們需要考慮:
1)n代表的數量級很小,且n代表的實體不需要單獨操作時,可以採用嵌入式建模。
2)n代表的數量級比較大,或者n代表的實體需要單獨進行操作時,採用在1中用Array存放引用的方式建模。
3)n代表的數量級非常大時,我們沒有選擇,只能在n端添加一個引用到1端。
MongoDB 一對多關聯性建模