10

我正在使用 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'}

我可以轻松地创建一个按灵活日期范围过滤文档的视图。这可以通过如下视图来完成,使用关键范围参数调用,例如_view/all-docs?startkey=20120101-0000&endkey=20120201-0000.

所有文档/map.js:

function(doc) {
    emit(doc.date, doc);
}

使用上面的数据,这将返回一个仅包含前 4 个文档(日期范围内唯一的文档)的 CouchDB 视图。

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

作者计数/map.js:

function(doc) {
  emit(doc.author, 1);
}

作者计数/reduce.js:

function(keys, values, rereduce) {
  return sum(values);
}

这将产生类似:

{
    "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 构造,还是一个视图就足够了?

4

3 回答 3

2

您可以使用列表非常接近所需的结果:

{
  _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>

这将比普通的 map-reduce 慢,并且是一种解决方法。不幸的是,这是进行多维查询的唯一方法,“CouchDB 不适合”

请求此设计的结果将是这样的:

{
  "bart": 1,
  "homer": 2,
  "lisa": 2
}

我们所做的基本上是发出很多元素,然后使用列表将它们分组为我们想要的。列表可用于以您想要的任何方式显示结果,但通常也会较慢。而一个普通的 map-reduce 可以被缓存并且只根据差异进行更改,每次请求时都必须重新构建列表。

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

如果您想将该列表用于不同的视图,您可以简单地在您请求的 URL 中交换它:

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

在 couchdb wiki 上阅读有关列表的更多信息。

于 2015-06-19T14:24:38.380 回答
0

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

组合/map.js:

function(doc) {
    emit([doc.date, doc.author], 1);
}

组合/减少.js:

_sum

这样,您将能够按开始/结束日期过滤文档。

startkey=[20120101-0000, "a"]&endkey=[20120201-0000, "a"]
于 2012-10-18T06:50:40.203 回答
0

尽管您的问题在一般情况下很难解决,但了解对可能查询的更多限制会大有帮助。例如,如果您知道您将搜索涵盖全天/月的范围,您可以使用数组[year, month, day, time]而不是字符串:

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

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

于 2012-10-21T18:07:37.723 回答