面向文档的数据库 CouchDB(5)

来源:developerWorks 中国 作者:成 富
  


清单 13.word-count视图的运行结果
{ 
  "rows":[ 
    {"key":null,"value":439} 
  ] 
 }

从代码清单 13中可以看到,视图的运行结果只有一行,value的值 439 是 Reduce 方法的最终运行结果,表示全部图书简介中共包含 439 个字符。默认情况下,Reduce 方法会把 Map 方法输出的记录变成一行。不过这里需要统计的是每个字符的出现次数,应该需要对字符进行分组来计数。通过在请求中添加参数group=true可以让 Reduce 方法按照 Map 方法输出的键进行分组,得到的部分运行结果如代码清单 14所示。


清单 14. 添加参数group=true之后word-count视图的运行结果
{ 
  "rows":[ 
    {"key":"\u4ea7","value":1}, 
    {"key":"\u4eba","value":6}, 
    {"key":"\u4ec0","value":1}, 
    {"key":"\u4ee5","value":1}, 
    {"key":"\u4eec","value":4}, 
    {"key":"\u4f1a","value":1}, 
    {"key":"\u4f24","value":1}, 
    {"key":"\u4f46","value":1}, 
    {"key":"\u97f3","value":2}, 
    {"key":"\u9996","value":1} 
  ] 
 }

在代码清单 14中,rows 数组中的每个元素表示一条记录,其中 key 是由 emit 方法输出的键,而 value 则是 emit 方法输出的值经过 Reduce 方法(如果有的话)得到的结果。由于指定了参数 group=true,相同的字符被分在一组并计数。

在获取视图运行结果的时候可以添加额外的参数,具体如表 4 所示。


表 4. 运行视图时的可选参数
参数 说明
key 限定结果中只包含键为该参数值的记录。
startkey 限定结果中只包含键大于或等于该参数值的记录。
endkey 限定结果中只包含键小于或等于该参数值的记录。
limit 限定结果中包含的记录的数目。
descending 指定结果中记录是否按照降序排列。
skip 指定结果中需要跳过的记录数目。
group 指定是否对键进行分组。
reduce 指定reduce=false可以只返回 Map 方法的运行结果。

视图定义说明

视图定义是存放在设计文档中views字段中的,因此需要在 Web 应用根目录下新建一个 views 目录,该目录下的每个子目录都表示一个视图。每个子目录下至少需要有 map.js 文件提供 Map 方法,可以有 reduce.js 文件提供 Reduce 方法。下面通过几个具体的视图定义来解释视图的用法。

第一个例子是对应用中的标签(Tag)进行统计。每本图书都可以有多个用户自定义的标签,一个常见的需求是统计每个标签的使用次数,并生成标签云(Tag Cloud)方便用户浏览。该视图定义的 Map 和 Reduce 方法见代码清单 15。


清单 15. 标签统计的视图定义
//Map 方法
 function(doc) { 
  if(doc.tags && doc.tags.length) { 
        for(var index in doc.tags) { 
            emit(doc.tags[index], 1); 
        } 
  } 
 } 

 //Reduce 方法
 function(key, values) { 
    return sum(values); 
 }

在代码清单 15中,Map 方法首先判断文档是否包含标签,然后对于某个标签,输出标签作为键,计数值 1 作为值;而在 Reduce 方法中,将计数值累加。该视图定义与代码清单 12 中 word-count 视图定义类似。

第二个视图是根据标签来浏览图书,也就是说给定一个标签,列出包含该标签的图书。由于需要根据标签进行查询,因此把标签作为键,而对应的值则是图书文档。通过使用参数key=" 原创 "就可以查询包含标签“原创”的图书。该视图定义只包含 Map 方法,如代码清单 16 所示。


清单 16. 根据标签浏览图书的视图定义
//Map 方法
 function(doc) { 
  if(doc.type == 'book' && doc.tags && doc.tags.length) { 
        for(var index in doc.tags) { 
            emit(doc.tags[index], doc); 
        } 
  } 
 }

最后一个视图是用来查询每本图书对应的用户评论。该视图只有 Map 方法,其实现是对于用户评论,以其关联的图书文档 ID 和评论的创建时间作为键,输出文档的内容作为值。在使用该视图的时候需要添加参数 startkey=[docId] 和 endkey=[docId, {}]来限定只返回 ID 为 docId 的图书的用户评论。具体的视图定义如代码清单 17 所示。


清单 17. 查询图书评论的视图定义
function(doc) { 
  if (doc.type == "comment") { 
    emit([doc.book_id, doc.created_at], doc); 
  }  
 };

使用list方法呈现视图

与show方法对应,list方法用来把视图转换成非 JSON 格式。list方法保存在设计文档的lists字段中。代码清单 18 中给出了list方法在设计文档中的示例。


清单 18. 设计文档中的 lists 字段
{ 
 "_id" : "_design/dianping", 
 "views" { 
  "book-by-tag" : "function(doc){...}" 
 }, 
 "lists" : { 
  "browse-book-by-tag" : "function(head, row, req, row_info) { ... }" 
 }

代码清单 18 中的设计文档中定义了视图book-by-tag和 list 方法browse-book。通过 GET 请求访问/databasename/_design/dianping/_list/browse-book/book-by-tag可以获取用browse-book格式化视图book-by-tag的结果。由于视图的运行结果包含多行数据,list 方法需要迭代每行数据并分别进行格式化,因此对于一个视图的运行结果,list 方法会被多次调用。 list 方法的调用过程是迭代之前调用一次,对结果中的每行数据都调用一次,最后在迭代之后再调用一次。比如,假设结果中包含 10 条记录的话,list 方法会被调用1 + 10 + 1 = 12次。每个 list 方法都可以有四个参数:head、row、req和row_info。根据调用情况的不同,这四个参数的实际值也不同。具体如下面所示。

  • 在迭代之前的调用中,head的值非空,包含与视图相关的信息,其中有两个字段:total_rows表示视图结果的总行数,offset表示当前结果中第一条记录在整个结果集中的起始位置,可以用来对视图结果进行分页。
  • 在对每行数据的调用中,row和row_info的值非空:row的值为视图运行结果中的当前行,对应于代码清单 14中所示的rows数组中的一个元素。row_info包含与迭代状态相关的信息,包括row_number表示当前的行号,first_key表示结果中第一条记录的键,prev_key表示前一行的键。
  • 在迭代之后的调用中,head和row的值均为空。在所有的调用中,req都包含了与此次请求相关的信息,其内容与 show 方法的第二个参数req相同,如表 2所示。
在 list 方法的实现中,需要根据这四个参数的值来确定当前的迭代状态,并输出对应的结果。下面通过一个实例来说明 list 方法的使用。

 

该 list 方法用来列出应用中的全部图书的概要信息。首先需要定义一个视图recent-books,该视图用来查询全部图书的概要信息,其定义如代码清单 19 所示。


清单 19.recent-books视图定义
function(doc) { 
  if (doc.type == "book") { 
    emit(null, { 
      title : doc.title, 
      author : doc.author, 
      price : doc.price, 
      publish_date : doc.publish_date 
      press : doc.press 
    });     
  } 
 };

从代码清单 19 中可以看到,doc.type == "book"确定了只有图书才会出现在视图中,并且视图中的结果只包含图书的基本信息。在定义了视图之后,下面需要定义 list 方法。代码清单 20 中给出了 list 方法的定义。


清单 20. list 方法的定义
function(head, row, req, row_info) { 
  // !json templates.index 
  // !code vendor/couchapp/path.js 
  // !code vendor/couchapp/date.js 
  // !code vendor/couchapp/template.js 
  if (head) { 
    return template(templates.index.head, { 
      assets : assetPath(), 
      edit : showPath("book-edit"), 
      index : listPath('index','recent-books',{limit:10}), 
      total_books : head.total_rows 
    }); 
  } 
  else if (row) { 
    var book = row.value; 
    return template(templates.index.row, { 
      title : book.title, 
      author : book.author, 
      price : book.price, 
      publish_date : book.publish_date, 
      press : book.press, 
      link : showPath("book-view", row.id), 
      assets : assetPath() 
    }); 
  } 
  else { 
    return template(templates.index.tail, { 
      assets : assetPath() 
    }); 
  } 
 };

时间:2009-08-07 09:10 来源:developerWorks 中国 作者:成 富 原文链接

好文,顶一下
(3)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量