【问题标题】:CouchDB Modelling - Time filtered and group dataCouchDB 建模 - 时间过滤和分组数据
【发布时间】:2013-09-04 13:10:34
【问题描述】:

我正在努力加深对 CouchDB 以及如何为某些现实世界场景建模数据的理解。我现在已经尽可能多地“按日期获取我的博客文章”;)

给定这样的文件:

{
    "_id": "couch1",
    "_rev": "2-338d0a592ad1e5570000002b00000000",
    "eventType": "event1",
    "date": 1328805860000
}

{
    "_id": "couch2",
    "_rev": "1-1e0315c2e1ca7f5f0000002b00000000",
    "eventType": "event1",
    "date": 1328133600000
}

{
    "_id": "couch3",
    "_rev": "1-154cd416b78cb2ef0000002b00000000",
    "eventType": "event2",
    "date": 1325434920000
}

如果日期是一个纪元,是否可以让 Couch 创建一个视图,在该视图中您要求在两个时间戳之间发生的所有“事件”,然后按“事件类型”对这些数据进行分组?

因此,使用上述方法并假设传入的时间戳包含这些文档 - 我们希望看到输出:

"event1": 2
"event2": 1

我获得的更多信息

我知道 Couch 会按键排序,所以如果我想要“前 10 名”,那将是第二阶段,但我可以处理。

所以这里的核心问题是您按一列过滤,然后按另一列分组?

如果我们使用下面的地图函数:

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

使用count reduce 函数,我们看到因为时间戳本质上是唯一的,所以 Couch 无法分组,并且 key 的值为 1。

所以你可以把map函数改成如下:

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

然后将 group level 更改为 1,这将按事件正确分组,但您的数据无法按时间切片,因为您的主要排序是按事件名称,这意味着时间排序现在已损坏?

人们对此有什么战争故事吗?这需要用re-reduce来完成吗?

非常感谢任何花时间阅读本文的人

鸡蛋

【问题讨论】:

  • 答案很棒;但是请注意,按 A 键进行筛选/选择,然后按 B 键进行分组基本上是一个二维查询;和 CouchDB 视图始终是一维的。
  • 所以我们说本质上它不能以最有效的方式完成并将其驱动到数据库。我们总是可以在客户端上进行分组,但我们宁愿不这样做。这种问题似乎是分析问题的典型形式,我想知道是否有任何其他为大规模数据构建的 NoSQL 解决方案可以执行这样的任务

标签: nosql couchdb theory data-modeling


【解决方案1】:

我会建议一个视图/列表组合:

查看:

"eventByDate": 
{
  "map": "function(doc) { emit(doc.date, doc.eventType);}"
}

列表:

"test": "function(head,req) {
  var eventO=new Object();
  while(row=getRow()) {
  if(eventO[row.value]==undefined) {
    eventO[row.value]=1;
  }else{
    eventO[row.value]++;
  }
 }
 send("[");
 for (var curEvent in eventO) {
  send ("{\"event\":\""+curEvent +"\",\"count\":"+eventO[curEvent]+"}");
 }
 send("]");
}"

结果:

[
{"event":"event2","count":1} 
{"event":"event1","count":2} 
]

但您必须在此处或您的后端手动按计数排序(我没有实现)

【讨论】:

  • 嗨,奥利弗。很高兴在 Stack Overflow 上见到你!
【解决方案2】:

您有固定数量的事件类型吗?它是一个小的、相对静态的列表吗?

如果不是,请跳过我的其余答案。

如果是这样,请继续阅读以获得快速而肮脏的选择。

您可以根据事件类型值将 map.js 函数更改为具有多个 emit() 函数。

if(eventType == event1 ) {emit(doc.date, {'eventType1': 1} 

对每种事件类型重复。或者,如果您可以将文档更改为将 eventType1、eventType2 等作为字段并以 1 为值,则可以跳过所有花哨的 if...then 废话,只需:

emit(doc.date, doc).

然后让你的 reduce.js 函数循环遍历行并将它们添加到最终看起来像这样的对象:

{eventType1: 25, eventType2: 2, ...}

for ( i = 0; i < values.length; i++){
    if ( values[i].eventType1 > 0) { eventType1 += 1 }
    if ( values[i].eventType2 > 0) { eventType2 += 1 }
    ...
}   

在没有 group 或 group=false 的情况下查询该视图,您应该会获得一条带有空键的记录,其中包含您的事件类型及其计数。

我正在处理类似类型的请求。但我的“eventType”列表永远不会改变。

【讨论】:

  • 很遗憾没有。不过,谢谢你的想法。发生的事件永远无法提前知道,并且可能会扩大到很大的规模。
  • 您最好的选择是使用列表功能。或者,如果您的 eventType 列表变得非常大,请寻找可链接的 map/reduce。
【解决方案3】:

您可以按照@user791770 的说明进行操作,但不必通过稍微更改代码来硬编码事件类型列表。

地图:

function(doc) {
  var data = {};
  data[doc.type] = 1;
  emit(doc.time, data);
}

减少:

function(keys, values, rereduce) {
  var data = {};
  for ( i = 0; i < values.length; i++) {
    for (var field in values[i]) {
      if (typeof data[field] == 'undefined') data[field] = 0;
      data[field] += values[i][field];
    }
  }
  return data;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-13
    相关资源
    最近更新 更多