2010-07-29 101 views
5

我剛寫完「couchdb:權威指南」一書,並開始玩設計文檔。但有一件事我不明白。我迄今看到的所有例子都是線性的。如何在couchdb視圖中調用另一個視圖?

例子:

{ 
    "_id": "1", 
    "_rev": ".....", 
    "name": "first", 
    "something": "blue", 
    "child": "2" 
} 

{ 
    "_id": "2", 
    "_rev": ".....", 
    "name": "second", 
    "something": "green", 
    "child": "3" 
    "parent" : "1" 
    } 

{ 
    "_id": "3", 
    "_rev": ".....", 
    "name": "second", 
    "something": "red", 
    "parent" : "2"; 
} 

我沒有問題,寫一個視圖,該視圖返回所有顏色:(!)

function(doc) { 
     if (doc.something) { 
      emit(doc.something,doc._id);  
    } 
} 

但是,如果我想知道的所有後代什麼(不孩子,對不起我的錯誤)的元素與_id = 1(「東西」:「藍色」)?我的編程經驗告訴我,我應該使用遞歸,但我不知道如何。如何從視圖函數調用另一個視圖函數?

一般來說:當你在json文檔之間引用數據庫時,會出現這個問題。更具體地說,與元素之間的傳遞關係。

編輯: 對於例如:我只知道_id = 1,結果應該是這樣的[_id = 2,_id = 3],因爲2是1和3一個孩子的孩子2.

回答

8

如果可能的話,不要這樣定義文檔層次結構 - 您將在每一步中與CouchDB作鬥爭。

您無法真正在視圖中執行層級結構。視圖意味着在每個文檔上獨立地傳送每個文檔(地圖)並從它們生成一些合計值(減少)。

您可以使用列表同時在多個文檔上操作,但這也不是一個好的解決方案。

如果你需要保持這種數據結構(鏈接到父/子),我建議你從CouchDB的外部組裝結構:獲取父文檔,得到它的孩子們,讓他們的孩子,等等。

然而,存儲在CouchDB中樹的首選方法是讓每個節點記住這是在樹形路徑:

{ 
    "_id": "1", 
    "name": "first", 
    "something": "blue", 
    "path": [1] 
} 

{ 
    "_id": "2", 
    "name": "second", 
    "something": "green", 
    "path": [1,2] 
    } 

{ 
    "_id": "3", 
    "name": "second", 
    "something": "red", 
    "path": [1,2,3] 
} 

然後,您可以使用此視圖來獲取文檔的後裔:

function(doc) { 
    for (var i in doc.path) { 
     emit([doc.path[i], doc.path], doc) 
    } 
} 

要獲得_id 1後代可以運行此查詢:

http://c.com/db/_design/colors/_view/descendants?startkey=[1]&endkey=[1,{}] 

存儲一個完整的路徑有其自身的缺點也一樣,雖然。我建議你檢查這個CouchDB wiki page on trees。其來源是this blog post by Paul Bonser

+0

我需要模擬元素之間的關係,如上所述。不過,我有幸在java web應用程序中移動邏輯,這對於couchdb應用程序來說有點包裝。我的意圖是儘可能多地將邏輯移至couchdb設計文檔並避免多次調用。我會嘗試你提到的兩種方法:a)多次調用(「獲取父文檔,獲取子文檔,獲取子項等)」和b)保存完整路徑(這是不太理想的),看看哪個是更快/更好。謝謝你的答案。 – 2010-07-30 16:53:29

+0

感謝您的提問。我在回答時學到了新東西。如果你在這裏(或者網絡上的任何地方)發佈你的發現,那麼當你發現什麼對你最有效時,這將是非常棒的。 – 2010-08-01 00:33:06

1

在你上面,讓所有的孩子爲一個文件ID的例子,你的地圖功能會是這個樣子:

function (doc) { 
    if (doc.parent) { 
     emit(doc.parent, { "_id": doc._id }); 
    } 
} 

(「子」屬性,你的文件有2甚至沒有必要。)

鑑於你的數據。例如,這將發出兩次:

[ "1", { "_id": "2" } ] 
[ "2", { "_id": "3" } ] 

爲了得到孩子的ID爲單親家庭,你會訪問視圖像這樣:

http://.../db/_design/viewName/_view/childfunc?key="2" 

要獲得完整的文檔,請將include_docs參數添加到查詢字符串中。

如果你想在同一時間的家長和孩子,你的地圖功能是隻有一點點不同:

function (doc) { 
    emit([ doc._id, "" ], { "_id": doc.id }); 
    if (doc.parent) { 
     emit([ doc.parent, doc._id ], { "_id": doc.id }) 
    } 
} 

此功能可以發出兩次,所以你最終有以下:

[ [ "1", "" ], { "_id": "1" } ] 
[ [ "1", "2" ], { "_id": "2" } ] 
[ [ "2", "" ], { "_id": "2" } ] 
[ [ "2", "3" ], { "_id": "3" } ] 
[ [ "3", "" ], { "_id": "3" } ] 

由於排序整理,父母最終(因爲他們的第二個關鍵元素是「」),然後孩子最終結束。你不必使用子_id作爲第二個關鍵元素,你可以使用任何自然排序屬性最有意義。 (創建日期,姓名,職務,等等)

如果你沒有足夠的「孩子」屬性,你可以做一個減函數來獲取所有的父母的孩子:

function (key, vals) { 
    var children = []; 
    for (var docId in vals) { 
     if (key[1] !== "") { 
      children.push(docId); 
     } 
    } 
    return children; 
} 

這個函數查看密鑰的子部分是否不爲空,如果是,則將文檔ID推入數組中。它以這種方式遍歷所有值,並在完成時返回數組。

+1

我犯了一個錯誤。我不是說孩子。我的意思是後代。抱歉。您的解決方案適用於兒童問題。但是如果我需要從reduce函數中訪問另一個map函數呢?例如要根據其他標準測試密鑰?從reduce或map函數中調用map函數是不可能的?例如,如果我需要比較兩個或多個文件彼此?在一個函數中,我一次只查看一個文檔,但是我不能比較兩個不同的文檔而不在任何地方保存值。 – 2010-07-29 19:20:45

相關問題