本篇博客翻譯自: http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1?mkt_tok=3RkMMJWWfF9wsRonsq7Ldu%2FhmjTEU5z14uUsUKGxhokz2EFye%2BLIHETpodcMTcVnM7zYDBceEJhqyQJxPr3FLdcN0tJuRhTrCw%3D%3D 備注:本譯文不
本篇博客翻譯自:
http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1?mkt_tok=3RkMMJWWfF9wsRonsq7Ldu%2FhmjTEU5z14uUsUKGxhokz2EFye%2BLIHETpodcMTcVnM7zYDBceEJhqyQJxPr3FLdcN0tJuRhTrCw%3D%3D
備注:本譯文不是嚴(yán)格意義上的翻譯,只是在基于對(duì)該原文的理解之上,盡可能表達(dá)清楚。如有疑問(wèn)或不妥,請(qǐng)參考原文。
很多剛從傳統(tǒng)SQL開(kāi)發(fā)轉(zhuǎn)向MongoDB開(kāi)發(fā)的朋友都會(huì)問(wèn)到一個(gè)問(wèn)題:如何用MongoDB表達(dá)傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)中的一對(duì)多(1 to n)關(guān)系?
基于MongoDB豐富的表達(dá)力,我們不能說(shuō)我們必須采用一個(gè)標(biāo)準(zhǔn)的方法來(lái)進(jìn)行1 to n的建模。稍后我們從3個(gè)具體場(chǎng)景來(lái)展開(kāi)講解。
首先,我們將1 to n中的n進(jìn)行場(chǎng)景細(xì)化。這個(gè)n究竟代表多大的量級(jí)呢?是幾個(gè)到幾十個(gè)?還是幾個(gè)到幾千個(gè)?還是成千上萬(wàn)個(gè)?
1) 1 to n(n代表好幾個(gè),或幾十個(gè),反正不太多)
比如每個(gè)Person會(huì)有多個(gè)Address。此種情況下,我們采用最簡(jiǎn)單的嵌入式文檔來(lái)建模。
{ 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' } ]}
優(yōu)點(diǎn):你不需要執(zhí)行單獨(dú)的查詢就可以獲得某個(gè)Person的所有Address信息。
缺點(diǎn):你無(wú)法像操作獨(dú)立文檔那樣來(lái)操作Address信息。你必須首先操作(比如查詢)Person文檔后,才有可能繼續(xù)操作Address。
在本實(shí)例中,我們不需要對(duì)Address進(jìn)行獨(dú)立的操作,且Address信息只有在關(guān)聯(lián)到某一個(gè)具體Person后才有意義。所以結(jié)論是:采用這種embedded(嵌入式)建模是非常適合Person-Address場(chǎng)景的。
2)1 to n(n代表好些個(gè),比如幾十個(gè),甚至幾百個(gè))
比如產(chǎn)品(Product)和零部件(part),每個(gè)產(chǎn)品會(huì)有很多個(gè)零部件。這種場(chǎng)景下,我們可以采用引用方式來(lái)建模,如下:
零部件(Part):{ _id : ObjectID('AAAA'), partno : '123-aff-456', name : '#4 grommet', qty: 94, cost: 0.94, price: 3.99} 產(chǎn)品(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 ]}
首先每個(gè)part作為單獨(dú)的文檔存在。每個(gè)產(chǎn)品中包含一個(gè)數(shù)組類(lèi)型字段(parts),這個(gè)數(shù)組中存放的是所有該產(chǎn)品包含的零部件的編號(hào)(_id主鍵)。當(dāng)你需要根據(jù)某一個(gè)產(chǎn)品編號(hào)查詢?cè)摦a(chǎn)品包含的所有部件信息時(shí),你可以執(zhí)行以下操作:
> 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() ;
優(yōu)點(diǎn):部件是作為獨(dú)立文檔(document)存在的,你可以對(duì)某一部件進(jìn)行獨(dú)立的操作,比如查詢或更新。
缺點(diǎn):如上,你必須通過(guò)兩次查詢才能找到某一個(gè)產(chǎn)品所屬的所有部件信息。
在本例中,這個(gè)缺點(diǎn)是可以接受的,本身實(shí)現(xiàn)起來(lái)也不難。而且,通過(guò)這種建模,你可以輕易的將1 to n擴(kuò)展到n to n,即一個(gè)產(chǎn)品可以包含多個(gè)部件,同時(shí)一個(gè)部件也可以被多個(gè)產(chǎn)品所引用(即同一部件可以被多個(gè)產(chǎn)品使用)。
3)1 to n(這個(gè)n代表很大的數(shù)值,比如成千上萬(wàn),甚至更大)
比如,每一個(gè)機(jī)器(host)會(huì)產(chǎn)生很大數(shù)量的日志信息(logmsg)。在這種情況下,如果你采用嵌入式建模,則一個(gè)host文檔會(huì)非常龐大,從而輕易超過(guò)MongoDB的文檔大小限制,所以不可行。如果你采用第二中方式建模,用數(shù)組來(lái)存放所有l(wèi)ogmsg的_id值,這種方式同樣不可行,因?yàn)楫?dāng)日止很多時(shí),即使單單引用objectId也會(huì)輕易超過(guò)文檔大小限制。所以此時(shí),我們采用以下方式:
機(jī)器(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}
綜上所述,在對(duì)1 to n關(guān)系建模時(shí),我們需要考慮:
1)n代表的數(shù)量級(jí)很小,且n代表的實(shí)體不需要單獨(dú)操作時(shí),可以采用嵌入式建模。
2)n代表的數(shù)量級(jí)比較大,或者n代表的實(shí)體需要單獨(dú)進(jìn)行操作時(shí),采用在1中用Array存放引用的方式建模。
3)n代表的數(shù)量級(jí)非常大時(shí),我們沒(méi)有選擇,只能在n端添加一個(gè)引用到1端。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com