【问题标题】:CouchDB View - filter keys before groupingCouchDB View - 分组前过滤键
【发布时间】:2017-05-16 13:13:35
【问题描述】:

我有一个 CouchDB 数据库,其中包含以下格式的文档:

{ createdBy: 'userId', at: 123456, type: 'action_type' }

我想编写一个视图,告诉我每种类型的操作是由哪个用户创建的。我能够做到这一点,创建一个执行此操作的视图:

 emit([doc.createdBy, doc.type, doc.at], 1);

使用reduce函数“sum”并以这种方式消费视图:

/_design/userActionsDoc/_view/userActions?group_level=2

这会以我想要的方式返回包含行的结果:

"rows":[ {"key":["userId","ACTION_1"],"value":20}, ...

问题是现在我想过滤给定时间段的结果。所以我想获得完全相同的信息,但只考虑在给定时间段内发生的动作。

如果我以不同的顺序发出字段,我可以通过“at”过滤文档。

?group_level=3&startkey=[149328316160]&endkey=[1493283161647,{},{}]

emit([doc.at, doc.type, doc.createdBy], 1);

但是我不会得到按 userId 和 actionType 分组的结果。有没有办法两者兼得?也许写我自己的reduce函数?

【问题讨论】:

    标签: mapreduce couchdb


    【解决方案1】:

    我感觉到你的痛苦。我过去做过两件不同的事情来尝试解决类似的问题。

    第一种模式很痛苦,可能效果很好,也可能根本不起作用。两种我都经历过。您的地图功能如下所示:

    function(doc) {
      var obj = {};
      obj[doc.createdBy] = {};
      obj[doc.createdBy][doc.type] = 1;
    
      emit(doc.at, obj);
      // Ignore this for now
      // emit(doc.at, JSON.stringify(obj));
    }
    

    那么你的 reduce 函数如下所示:

    function(key, values, rereduce) {
      var output = {};
      values.forEach(function(v) {
        // Ignore this for now
        // v = JSON.parse(v);
        for (var user in v) {
          for (var action in v[user]) {
            output[user][action] = (output[user][action] || 0) + v[user][action];
          }
        }
      });
      return output;
      // Ignore this for now
      // return JSON.stringify(output);
    }
    

    对于大型数据集,这通常会导致沙发错误,说明您的 reduce 函数收缩得不够快。在这种情况下,您可以对对象进行字符串化/解析,如代码中的“忽略”cmets 所示。

    这背后的原因是 couchdb 最终希望您在 reduce 函数中输出一个简单的对象,例如字符串或整数。以我的经验,只要它仍然是一个字符串,字符串变得更长似乎并不重要。如果您输出一个对象,则函数有时会出错,因为您向该对象添加了太多道具。


    第二种模式可能更好,但需要提前“定义”您的时间段。如果您的时间段要求可以锁定到特定的年份、特定的月份、日期、季度等。您只需在地图功能中发出多次。下面我假设at 属性是纪元毫秒,或者至少是日期构造函数可以准确解析的值。

    function(doc) {
      var time_key;
      var my_date = new Date(doc.at);
    
      //// Used for filtering results in a given year 
      //// e.g. startkey=["2017"]&endkey=["2017",{}]
      time_key = my_date.toISOString().substr(0,4);
      emit([time_key, doc.createdBy, doc.type], 1);
    
      //// Used for filtering results in a given month
      //// e.g. startkey=["2017-01"]&endkey=["2017-01",{}]
      time_key = my_date.toISOString().substr(0,7);
      emit([time_key, doc.createdBy, doc.type], 1);
    
      //// Used for filtering results in a given quarter
      //// e.g. startkey=["2017Q1"]&endkey=["2017Q1",{}]
      time_key = my_date.toISOString().substr(0,4) + 'Q' + Math.floor(my_date.getMonth()/3).toString();
      emit([time_key, doc.createdBy, doc.type], 1);
    }
    

    然后,您的 reduce 功能与您原来的功能相同。本质上,您只是尝试为键中的第一项定义一个常量值,该值对应于定义的时间段。非常适用于业务报告,但不太适合灵活的时间段。

    【讨论】:

      猜你喜欢
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-31
      • 2011-10-28
      • 1970-01-01
      • 2021-12-17
      相关资源
      最近更新 更多