2011-05-25 89 views
5

我對couchDB非常新,甚至在閱讀"how to store hierarchical data"後,它仍然沒有點擊。從CouchDB檢索分層/嵌套數據

而不是使用維基中描述的完整路徑模式我希望跟蹤孩子作爲一個UUID數組和父母作爲單個UUID。我傾向於這種模式,所以我可以通過他們在兒童陣列中的位置來維護兒童的順序。

以下是沙發上的一些示例文檔,水桶可以包含水桶和物品,物品只能包含其他物品。 (UUIDs爲了清晰起見而縮寫):

{_id: 3944 
name: "top level bucket with two items" 
type: "bucket", 
parent: null 
children: [8989, 4839] 
} 
{_id: 8989 
name: "second level item with no sub items" 
type: "item" 
parent: 3944 
} 
{ 
_id: 4839 
name: "second level bucket with one item" 
type: "bucket", 
parent: 3944 
children: [5694] 
} 
{ 
_id: 5694 
name: "third level item (has one sub item)" 
type: "item", 
parent: 4839, 
children: [5390] 
} 
{ 
_id: 5390 
name: "fourth level item" 
type: "item" 
parent: 5694 
} 

是否有可能通過嵌入文檔ID在地圖功能中查找文檔?

function(doc) { 
    if(doc.type == "bucket" || doc.type == "item") 
     emit(doc, null); // still working on my key value output structure 
     if(doc.children) { 
      for(var i in doc.children) { 
       // can i look up a document here using ids from the children array? 
       doc.children[i]; // psuedo code 
       emit(); // the retrieved document would be emitted here 
      } 
     } 
    } 
} 

在理想世界中,最終的JSON輸出看起來像這樣。

{"_id":3944, 
"name":"top level bucket with two items", 
"type":"bucket", 
"parent":"", 
"children":[ 
    {"_id":8989, "name":"second level item with no sub items", "type":"item", "parent":3944}, 
    {"_id": 4839, "name":"second level bucket with one item", "type":"bucket", "parent":3944, "children":[ 
     {"_id":5694", "name":"third level item (has one sub item)", "type":"item", "parent": 4839, "children":[ 
      {"_id":5390, "name":"fourth level item", "type":"item", "parent":5694} 
     ]} 
    ]} 
] 
} 

回答

6

你可以找到一般性討論on the CouchDB wiki

我沒有時間來測試它的權利,但你的地圖功能應該是這個樣子:

function(doc) { 
    if (doc.type === "bucket" || doc.type === "item") 
     emit([ doc._id, -1 ], 1); 
     if (doc.children) { 
      for (var i = 0, child_id; child_id = doc.children[i]; ++i) { 
       emit([ doc._id, i ], { _id: child_id }); 
      } 
     } 
    } 
} 

你應該include_docs=true查詢它獲得的文件,如CouchDB documentation解釋說:如果你的地圖函數發出一個具有{'_id': XXX}的對象值,並且您使用include_docs=true參數查詢視圖,則CouchDB將獲取id爲XXX的文檔,而不是處理爲發出鍵/值對的文檔。

添加startkey=["3944"]&endkey["3944",{}]只能得到ID爲「3944」的子文件。

編輯:看看this question瞭解更多詳情。

+0

感謝您幫助Marcello。當我運行map函數時,輸出並不像我希望的那樣嵌套,而是完全平坦。有任何想法嗎? – berg 2011-05-26 08:01:56

+0

我的答案是[here](http://stackoverflow.com/questions/6084741/how-to-merge-view-collat​​ion-into-useful-output-in-couchdb/6094540#6094540)。但我不推薦它。嵌套列表的優點是什麼?扁平列表的排列順序是讓每個「項目」或「桶」緊隨其子代的請求順序。遍歷此列表非常簡單且高效。爲什麼你需要一個嵌套列表?可能是我可以給你一個更好的解決方案。 – 2011-05-26 15:12:31

+0

我希望直接在我的客戶端JavaScript代碼中使用結果,這些代碼期望數據返回嵌套。但是在閱讀了與之相關的問題之後,看起來這與CouchDB的問題背道而馳,所以我打算做這個客戶端!再次感謝我將此標記爲答案! – berg 2011-05-26 17:39:02

6

你能從視圖中輸出樹結構嗎?編號CouchDB視圖查詢返回值列表,沒有辦法讓他們輸出列表以外的任何東西。所以,你必須處理你的地圖,返回給定桶的所有後代列表。

但是,您可以在視圖本身之後插入_list後處理函數,以將該列表重新轉換爲嵌套結構。這是可能的,如果你的值知道他們的父—的_id該算法是相當直接的,只是問另一個問題,如果它給你帶來麻煩。

你可以在地圖功能中通過它的ID獲取文檔嗎?不可以。根據CouchDB中的標識符無法抓取文檔。請求必須來自應用程序,或者以文檔標識符上的標準GET的形式,或者將include_docs=true添加到查看請求中。

技術原因很簡單:CouchDB只在文檔更改時運行map函數。如果文件A被允許獲取文件B,則當B更改時,發出的數據將變爲無效。

你可以輸出所有的後代,而不需要存儲每個節點的父節點列表嗎?編號。CouchDB映射函數爲數據庫中的每個文檔發出一組key-value-id對,因此key和id之間的對應關係必須基於單個文檔來確定。

如果你有四級樹狀結構A -> B -> C -> D但只讓一個節點知道其父母和孩子,上面再沒有一個節點知道DA後代,所以你將不能夠發射使用基於A的密鑰的D的ID,因此它在輸出中將不可見。

所以,你有三種選擇:

  • 抓鬥只有三個級別(這是可能的,因爲B知道CA後裔),並通過再次運行查詢搶額外的水平。
  • 以某種方式存儲節點內每個節點的後代列表(這是昂貴的)。
  • 存儲節點內每個節點的父節點列表。