Mapreduce 使用CouchDB视图,我可以同时计数组和按键范围过滤吗?

Mapreduce 使用CouchDB视图,我可以同时计数组和按键范围过滤吗?,mapreduce,couchdb,Mapreduce,Couchdb,我用的是CouchDB。我希望能够在查询时指定的日期范围内统计特定字段值的出现次数。我似乎能够做到这一点的一部分,但我很难理解最好的方法来把这一切结合起来 假设文档有一个时间戳字段和另一个字段,例如: { date: '20120101-1853', author: 'bart' } { date: '20120102-1850', author: 'homer'} { date: '20120103-2359', author: 'homer'} { date: '20120104-1200'

我用的是CouchDB。我希望能够在查询时指定的日期范围内统计特定字段值的出现次数。我似乎能够做到这一点的一部分,但我很难理解最好的方法来把这一切结合起来

假设文档有一个时间戳字段和另一个字段,例如:

{ date: '20120101-1853', author: 'bart' }
{ date: '20120102-1850', author: 'homer'}
{ date: '20120103-2359', author: 'homer'}
{ date: '20120104-1200', author: 'lisa'}
{ date: '20120815-1250', author: 'lisa'}
我可以轻松创建一个视图,根据灵活的日期范围过滤文档。这可以通过以下视图完成,该视图使用键范围参数调用,例如
\u view/all docs?startkey=20120101-0000&endkey=20120201-0000

所有文档/map.js:

对于上面的数据,这将返回一个CouchDB视图,其中只包含前4个文档(日期范围内的唯一文档)

我还可以创建一个查询,统计给定字段的出现次数,就像这样,通过分组调用,即
\u view/author count?group=true

作者计数/map.js:

author count/reduce.js:

这将产生如下结果:

{
    "rows": [
        {"key":"bart","value":1},
        {"key":"homer","value":2}
        {"key":"lisa","value":2}
     ]
}
但是,我找不到按日期过滤和计数事件的最佳方法。例如,使用上面的数据,我希望能够指定范围参数,如
startkey=20120101-0000&endkey=20120201-0000
,并得到这样的结果,其中最后一张单据由于超出指定的日期范围而被排除在计数之外:

{
    "rows": [
        {"key":"bart","value":1},
        {"key":"homer","value":2}
        {"key":"lisa","value":1}
     ]
}

最优雅的方式是什么?这是否可以通过单个查询实现?我应该使用另一个CouchDB构造,还是一个视图就足够了?

您需要创建一个组合视图:

组合/map.js:

组合/reduce.js:

这样,您就可以按开始/结束日期筛选文档

startkey=[20120101-0000, "a"]&endkey=[20120201-0000, "a"]

虽然您的问题在一般情况下很难解决,但了解可能查询的更多限制会有很大帮助。例如,如果您知道您将搜索涵盖完整天数/月份的范围,您可以使用
[年、月、日、时间]
的数组,而不是字符串:

emit([doc.date_year, doc.date_month, doc.date_day, doc.date_time, doc.author] doc);

即使您无法预测所有可能的查询都将适合基于此键类型的分组,拆分键也可以帮助您优化范围查询并减少所需的查找次数(同时需要一些额外的空间)。

您可以通过以下列表获得非常接近所需的结果:

{
  _id: "_design/authors",
  views: {
    authors_by_date: {
      map: function(doc) {
        emit(doc.date, doc.author);
      }
    }
  },
  lists: {
    count_occurrences: function(head, req) {
      start({ headers: { "Content-Type": "application/json" }});

      var result = {};
      var row;
      while(row = getRow()) {
        var val = row.value;
        if(result[val]) result[val]++;
        else result[val] = 1;
      }
      return result;
    }
  }
}
这种设计可以要求如下:

http://<couchurl>/<db>/_design/authors/_list/count_occurrences/authors_by_date?startkey=<startDate>&endkey=<endDate>
我们所做的基本上是发射很多元素,然后使用列表按我们的需要对它们进行分组。列表可用于以任何方式显示结果,但速度通常较慢。虽然普通映射reduce可以缓存,并且只能根据差异进行更改,但每次请求时都必须重新构建列表

它与从映射中获取所有元素一样慢(编排数据的开销几乎可以忽略不计):比获取reduce的结果慢得多

如果要将列表用于其他视图,只需在请求的URL中交换即可:

http://<couchurl>/<db>/_design/authors/_list/count_occurrences/<view>
http:////_design/authors/_list/count_occurrences/

阅读更多信息。

我的第一个想法是查询视图中的日期范围,然后使用a进行分组/计数。你不能在同一个查询中对多个键进行这样的操作,所以你需要第二层。你知道@rewbs吗?这不允许我按作者分组和计数,所以我会得到这样的结果:(每个条目列出的计数为1)。据我所知,最好的解决方案是使用Dominic建议的_list函数。当然,如果获取
字段并使每个条目包含
键和
值对您很重要,您可以编辑列表以获得所需的格式
startkey=[20120101-0000, "a"]&endkey=[20120201-0000, "a"]
emit([doc.date_year, doc.date_month, doc.date_day, doc.date_time, doc.author] doc);
{
  _id: "_design/authors",
  views: {
    authors_by_date: {
      map: function(doc) {
        emit(doc.date, doc.author);
      }
    }
  },
  lists: {
    count_occurrences: function(head, req) {
      start({ headers: { "Content-Type": "application/json" }});

      var result = {};
      var row;
      while(row = getRow()) {
        var val = row.value;
        if(result[val]) result[val]++;
        else result[val] = 1;
      }
      return result;
    }
  }
}
http://<couchurl>/<db>/_design/authors/_list/count_occurrences/authors_by_date?startkey=<startDate>&endkey=<endDate>
{
  "bart": 1,
  "homer": 2,
  "lisa": 2
}
http://<couchurl>/<db>/_design/authors/_list/count_occurrences/<view>