2009-10-07 54 views
108

我有一個問題,我一直在努力,現在回答了一段時間,但無法弄清楚:原則建模的CouchDB文檔

如何設計,或瓜分,CouchDB的文件呢?

以博客文章爲例。

半 「關係」 的方式做到這一點是創建幾個對象:

  • 用戶
  • 評論
  • 標籤
  • 片段

這非常有道理。但我正在嘗試使用couchdb(出於所有原因,它很棒)來模擬相同的事情,這非常困難。

大部分的博客文章給你一個簡單的例子,如何做到這一點。他們基本上以相同的方式劃分它,但是說你可以爲每個文檔添加'任意'屬性,這絕對是很好的。所以你必須在CouchDB中是這樣的:

  • 後(與標籤和片斷「僞」車型在DOC)
  • 評論
  • 用戶

有些人甚至會說你可以扔在那裏的評論和用戶,所以你有這樣的:


post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author: { name: "Lance" age: "23" } tags: ["sample", "post"] comments { comment { id: 93930414809 body: "Interesting Post" } comment { id: 19018301989 body: "I agree" } } } 

這看起來非常好,很容易理解。我也瞭解如何編寫視圖,從所有Post文檔中提取評論,以便將它們導入評論模型中,與用戶和標籤相同。

但後來我想,「爲什麼不乾脆把我的整個網站到單個文件?」:


site { domain: "www.blog.com" owner: "me" pages { page { title: "Blog" posts { post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author: { name: "Lance" age: "23" } tags: ["sample", "post"] comments { comment { id: 93930414809 body: "Interesting Post" } comment { id: 19018301989 body: "I agree" } } } post { id: 18091890192984 title: "Second Post" ... } } } } } 

你可以很容易地使視圖來找到你想和什麼。

那麼我的問題是,您如何確定何時將文檔分成較小的文檔,或何時在文檔之間建立「關係」?

我認爲這將是更加「面向對象」,也更容易映射到值對象,如果它被分成像這樣:


posts { post { id: 123412804910820 title: "My Post" body: "Lots of Content" html: "<p>Lots of Content</p>" author_id: "Lance1231" tags: ["sample", "post"] } } authors { author { id: "Lance1231" name: "Lance" age: "23" } } comments { comment { id: "comment1" body: "Interesting Post" post_id: 123412804910820 } comment { id: "comment2" body: "I agree" post_id: 123412804910820 } } 

...但隨後開始看起來更像一個關係數據庫。而且我經常會繼承一些看起來像「整個網站在一個文件中」的東西,所以用關係模型化它更加困難。

我讀過很多有關如何/何時使用關係數據庫與文檔數據庫的內容,因此這不是主要問題。我更想知道的是,在CouchDB中建模數據時應用的最佳規則/原則是什麼。

另一個例子是XML文件/數據。一些XML數據嵌套深度超過10個層次,我想用相同的客戶端(例如Rails上的Ajax或Flex)可視化我將從ActiveRecord,CouchRest或任何其他對象關係映射器呈現JSON的情況。有時我會得到整個站點結構的巨大XML文件,就像下面的文件一樣,我需要將它映射到Value Objects以在我的Rails應用程序中使用,因此我不必編寫其他序列化/反序列化數據的方式:


<pages> <page> <subPages> <subPage> <images> <image> <url/> </image> </images> </subPage> </subPages> </page> </pages> 

所以一般CouchDB的問題是:

  1. 什麼規則/原則,你用瓜分你的文件(關係等)?
  2. 可以將整個網站放入一個文檔嗎?
  3. 如果是這樣,你如何處理具有任意深度級別的序列化/反序列化文檔(如上面的大json示例或xml示例)?
  4. 或者您是否將它們轉化爲VO,您是否決定「這些內容過於嵌套到對象關係映射,因此我只能使用原始XML/JSON方法訪問它們」?

非常感謝您的幫助,關於如何使用CouchDB分隔數據的問題一直很難說「這就是我應該從現在開始做的事情」。我希望很快到達那裏。

我研究了以下網站/項目。

  1. Hierarchical Data in CouchDB
  2. CouchDB Wiki
  3. Sofa - CouchDB App
  4. CouchDB The Definitive Guide
  5. PeepCode CouchDB Screencast
  6. CouchRest
  7. CouchDB README

...但他們還沒有回答這個問題。

+2

哇你在這裏寫了一篇文章... :-) – Eero 2009-10-07 10:46:35

+7

嘿,這是一個很好的問題 – elmarco 2009-10-07 19:43:29

回答

15

book說,如果我記得正確,直到「它傷害」,同時牢記您的文檔可能更新的頻率,正常化。

  1. 你用什麼規則/原則來劃分你的文件(關係等)?

作爲一個經驗法則,我包括顯示有關該項目的頁面所需的所有數據。換句話說,你可以在真實世界的一張紙上打印所有你會交給別人的東西。例如。除了數字之外,股票報價文件將包括公司名稱,交易所,貨幣;合同文件將包括交易對手的名稱和地址,以及有關日期和簽字的所有信息。但是,不同日期的股票報價將形成單獨的文件,單獨的合約將形成單獨的文件。

  1. 可以將整個網站放到一個文檔中嗎?

不,那將是愚蠢的,因爲:

  • 你就必須讀取和寫入每次更新整個網站(文檔),這是非常低效的;
  • 你不會從任何視圖緩存中受益。
+3

感謝您與我進行了一點。我的想法是「包含所有需要的數據來顯示有關該項目的頁面」,但這仍然很難實現。 「頁面」可能是一個網頁的評論,用戶的網頁,帖子的網頁,或評論和帖子的頁面等你將如何劃分起來以後,主要是?您也可以將您的合同顯示給用戶。我得到'類似'的文件,這有助於將它們分開。 – 2009-10-07 11:55:44

15

我知道這是一個古老的問題,但我碰到它試圖找出這個完全相同的問題的最佳方法。克里斯托弗Lenz寫了一篇關於methods of modeling "joins" in CouchDB的好博客文章。我的一個結論是:「允許不衝突地添加相關數據的唯一方法是將相關數據放入單獨的文檔中。」所以,爲了簡單起見,你需要傾向於「非規範化」。但是由於在某些情況下寫入衝突,你會遇到天生的障礙。

在你的帖子和評論的例子中,如果單個帖子及其所有評論都存在於一個文檔中,那麼兩個人試圖同時發表評論(即針對文檔的同一修訂版)會導致衝突。在「單個文檔中的整個站點」情況下,這會變得更糟。

所以我認爲經驗法則是「非正規化直到它受到傷害」,但是它會「傷害」的地方是很可能發生針對文檔的同一版本的多次編輯。

+0

有趣的迴應。考慮到這一點,人們應該懷疑是否有任何合理的高流量網站甚至可以在一個文檔中爲單個博客文章提供所有評論。如果我閱讀這個權利,這意味着每當有人快速連續添加評論時,您可能需要解決衝突。當然,我不知道他們必須有多快才能觸發這一點。 – pc1oad1etter 2011-02-18 15:15:05

+1

如果評論是沙發文檔的一部分,同時評論帖可能會有衝突,因爲您的版本範圍是包含所有評論的「帖子」。在每個對象都是文檔集合的情況下,這些會簡直成了兩個新的「評論」的鏈接返回到崗位和沒有碰撞的關注文檔。我還要指出的是,建立在「面向對象」的文檔設計的觀點是直截了當 - 你在後,例如關鍵傳球,然後發出所有的意見,通過某種方法來分類,該職位。 – 2011-09-17 01:57:22

5

我認爲Jake的迴應指出了使用CouchDB最重要的方面之一,這可能會幫助您做出範圍確定:衝突。

如果您有作爲帖子本身的數組屬性的註釋,並且您只是有一個'post'數據庫和一大堆'post'文檔,就像Jake和其他人正確指出的那樣設想一個非常受歡迎的博客文章,其中兩個用戶同時向帖子文檔提交編輯,導致該文檔發生衝突和版本衝突。

ASIDE:this article points out一樣,還要考慮到每次您要求/更新該文檔時,您必須完整地獲取/設置文檔,以便將大量文檔傳遞給代表整個網站或帖子的文檔很多評論都可能成爲你想避免的問題。

在將帖子與評論分開建模並且兩個人對故事提交評論的情況下,這些簡單地成爲該DB中的兩個「評論」文檔,沒有衝突的問題;只需兩次PUT操作即可將兩個新評論添加到「評論」數據庫。

然後,要寫回給您回帖的評論的視圖,您需要傳遞postID,然後發出引用該父帖子ID的所有評論,並按照某種邏輯順序進行排序。也許你甚至會傳遞類似[postID,byUsername]的內容作爲'comments'視圖的關鍵字,以指示父帖子以及您希望結果排序的方式或沿着這些行的內容。

MongoDB處理文檔有點不同,允許在文檔的子元素上構建索引,因此您可能會在MongoDB郵件列表中看到相同的問題,並且有人說「只是將註釋作爲父帖子的屬性」。

由於Mongo的寫鎖定和單主控性質,添加註釋的兩個人的衝突修訂問題不會在那裏出現,並且內容的查詢能力不會太差因爲分指數。這就是說,如果你的子元素或者數據庫將會是巨大的(比如說成千上萬的評論),我相信這是兩個陣營建立這些獨立元素的建議;對於Mongo來說,我確實已經看到了這種情況,因爲對於文檔及其子元素的大小有一些上限。

+0

非常有幫助。謝謝 – 2013-10-17 04:00:56

23

已經有一些很好的答案了,但是我想添加一些更新的CouchDB功能來處理由viatropos描述的原始情況。

分解文檔的關鍵點是可能存在衝突的地方(如前所述)。您不應該在單個文檔中將大量「糾結」文檔集中在一起,因爲您將獲得完全不相關更新的單個修訂路徑(例如,添加評論爲整個站點文檔添加修訂版)。管理各種較小文檔之間的關係或連接起初可能會讓人困惑,但CouchDB提供了多種選項,可將不同部分組合成單一響應。

第一大問題是視圖整理。當您將鍵/值對發送到map/reduce查詢的結果中時,這些鍵將根據UTF-8歸類進行排序(「a」位於「b」之前)。您還可以將地圖/縮減中的複雜鍵作爲JSON數組輸出:["a", "b", "c"]。這樣做可以讓你包含一個由數組鍵構成的「樹」。使用上面的示例,我們可以輸出post_id,然後輸出我們引用的事物類型,然後輸出其ID(如果需要)。如果我們再輸出的引用文件的ID爲在所返回,我們可以使用「include_docs」查詢參數包括在地圖上的那些文件值的對象/減少輸出:

{"rows":[ 
    {"key":["123412804910820", "post"], "value":null}, 
    {"key":["123412804910820", "author", "Lance1231"], "value":{"_id":"Lance1231"}}, 
    {"key":["123412804910820", "comment", "comment1"], "value":{"_id":"comment1"}}, 
    {"key":["123412804910820", "comment", "comment2"], "value":{"_id":"comment2"}} 
]} 

請求與相同的觀點'?include_docs = true'將添加一個'doc'鍵,它將使用'value'對象中引用的'_id',或者如果'value'對象中不存在,它將使用'_id'從該行發出的文檔(在本例中是'post'文檔)。請注意,這些結果將包括一個引用源文件的'id'字段。我把它留給空間和可讀性。

然後,我們可以使用「start_key」和「end_key」參數來篩選結果到一個單一的文章的數據:

?start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]
甚至專門提取列表中某種類型:
?start_key=["123412804910820", "comment"]&end_key=["123412804910820", "comment", {}]
這些查詢參數的組合是可能的,因爲空對象(「 {}」)始終位於排序規則的底部,而null或「」總是位於最上面。

在這些情況下,來自CouchDB的第二個有用的補充是_list函數。這將允許您通過某種模板系統(如果您需要HTML,XML,CSV或其他類型)運行上述結果,或者如果您希望能夠請求整個帖子的內容(包括作者和評論數據),並作爲單個JSON文檔返回,該文檔與客戶端/用戶界面代碼所需的內容相匹配。這樣做將允許您以這種方式請求帖子的統一輸出文檔:

/db/_design/app/_list/posts/unified??start_key=["123412804910820"]&end_key=["123412804910820", {}, {}]&include_docs=true
您的_list函數(在本例中名爲「統一」)將採用視圖映射/縮減(在本例中爲「posts」)的結果並運行它們通過一個JavaScript函數發送你需要的內容類型的HTTP響應(JSON,HTML等)。

結合這些東西,你可以在任何級別您有幫助的「安全」的更新,衝突和分裂複製你的文件,然後把他們重新走到一起根據需要會要求當他們。

希望有所幫助。

+2

不知道這是否有助於蘭斯,但我知道一件事;它絕對幫助我很多!這太棒了! – Mark 2012-01-27 17:10:48