本篇博客翻譯自:
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 documentsObjectID('AAAA'), // reference to the #4 grommet aboveObjectID('F17C'), // reference to a different PartObjectID('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也會輕易超過文檔限制大小。所以此時,我們採用下面方式:
我們在logsmg中,存放對host的_id引用就可以。主機(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}
綜上所述,在對1 to n關系建模時,我們須要考慮:
1)n代表的數量級非常小。且n代表的實體不須要單獨操作時,能夠採用嵌入式建模。
2)n代表的數量級比較大。或者n代表的實體須要單獨進行操作時,採用在1中用Array存放引用的方式建模。
3)n代表的數量級很大時,我們沒有選擇。僅僅能在n端加入一個引用到1端。