2011-05-25 115 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后裔),并通过再次运行查询抢额外的水平。
  • 以某种方式存储节点内每个节点的后代列表(这是昂贵的)。
  • 存储节点内每个节点的父节点列表。