我刚刚完成了“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的孩子。
答案 0 :(得分: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。
答案 1 :(得分:1)
在上面的示例中,为了获取文档ID的所有子项,您的map函数将如下所示:
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作为第二个关键元素,您可以使用任何自然排序属性最有意义。 (创建日期,名称,标题等等。)
如果您没有“child”属性,可以使用reduce函数来获取所有父级的子级:
function (key, vals) {
var children = [];
for (var docId in vals) {
if (key[1] !== "") {
children.push(docId);
}
}
return children;
}
该函数查看键的子部分是否为空,如果是,则将文档ID推送到数组中。它以这种方式遍历所有值,并在完成后返回数组。