从CouchDB检索分层/嵌套数据

8

我对couchDB还比较陌生,即使阅读了(最新存档现已删除)http://wiki.apache.org/couchdb/How_to_store_hierarchical_data(via 'Store the full path to each node as an attribute in that node's document'),仍然不太理解。

与其使用维基中描述的完整路径模式,我希望将子项作为UUID数组以及父项作为单个UUID进行跟踪。我倾向于这种方式,以便通过子项在子项数组中的位置来维护子项的顺序。

以下是couch中的一些示例文档,bucket可以包含bucket和item,item只能包含其他item。(UUID缩写以提高清晰度):

{_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
}

在map函数中,通过嵌入的文档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}
         ]}
     ]}
 ]
}
2个回答

8

您可以在CouchDB维基上找到有关实体关系的一般讨论

我现在没有时间测试它,但是您的映射函数应该类似于:

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文档中所解释的那样:如果您的映射函数发出一个具有{'_id': XXX}的对象值,并且您使用include_docs=true参数查询视图,则CouchDB将获取具有id XXX的文档,而不是处理以发出键/值对的文档。

添加startkey=["3944"]&endkey["3944",{}]以仅获取具有其子元素的id为“3944”的文档。

编辑:查看此问题以获取更多详细信息。


我尝试在数据上使用这个视图(使用 include_docs 为 true),但内部的 emit 总是产生一个空对象作为值,并且排序似乎是任意的。 - James Hopkin
@JamesHopkin,您正在尝试查看哪些数据? 视图整理 在CouchDB中已被很好地定义。 - Marcello Nuccio
我想我真正想知道发出一个值 { _id: "<some-id>" } 会产生什么影响。在我看到的结果中似乎没有任何影响。 - James Hopkin
@JamesHopkin,我已经编辑了答案,并附上了官方文档的相关片段。希望对你有所帮助。 - Marcello Nuccio
啊,问题出在视图中的 child._id 应该改为 child.toString()。这样修改后对我来说可以工作了。 - James Hopkin
显示剩余5条评论

8
你能从视图中输出树形结构吗?不行。CouchDB视图查询返回一个值列表,没有办法让它们输出除列表以外的任何东西。所以,你必须处理你的映射函数返回给定桶的所有后代的列表。
但是,您可以在视图本身之后插入一个 _list 后处理函数,将该列表转换回嵌套结构。如果您的值知道其父母的_id,那么这是可能的——算法非常简单,如果有问题,请另外提问。 在映射函数中,你能通过其id获取文档吗?不行。没有办法从CouchDB内部通过标识符获取文档。请求必须来自应用程序,可以采用标准的文档标识符GET形式,或者在视图请求中添加include_docs=true
这个技术原因很简单:CouchDB仅在文档更改时运行映射函数。如果允许文档A获取文档B,则当B更改时,发出的数据将变得无效。 你能输出所有后代而不存储每个节点的父节点列表吗?不行。CouchDB映射函数为数据库中的每个文档发出一组键-值-id对,因此必须基于单个文档确定键和id之间的对应关系。
如果您有一个四级树形结构A -> B -> C -> D,但只让一个节点知道其父节点和子节点,则上面的任何节点都不知道DA的后代,因此您将无法发出基于A的键的D的id,并且它将不会在输出中可见。
所以,你有三个选择:
  • 仅获取三个级别(这是可能的,因为B知道CA的后代),并通过再次运行查询来获取更多级别。
  • 以某种方式存储节点内每个节点的后代列表(这很昂贵)。
  • 存储节点内每个节点的父节点列表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接