2010-07-29 89 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

相关问题