如何在couchdb视图中调用另一个视图?

时间:2010-07-29 17:17:57

标签: json 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的孩子。

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推送到数组中。它以这种方式遍历所有值,并在完成后返回数组。