我有一个CouchDB数据库,其中包含以下格式的文档:
array (
0 =>
array (
'ab' => 'err',
'bb' => 'iuierru',
'eta' => 2,
),
1 =>
array (
'ab' => 'jdgfd',
'bb' => 'edfguiru',
'eta' => 2,
),
2 =>
array (
'ab' => 'fdfdf',
'bb' => 'dfdf',
'eta' => 3,
),
3 =>
array (
'ab' => 'jdfd',
'bb' => 'iweuiru',
'eta' => 4,
),
4 =>
array (
'ab' => 'dsdjdfd',
'bb' => 'iuiru',
'eta' => 5,
),
)
我想写一个视图,它会告诉我哪个用户创建了多少种类型的操作。我能够创建一个执行此操作的视图:
{ createdBy: 'userId', at: 123456, type: 'action_type' }
使用reduce函数“sum”并以这种方式使用视图:
/ _设计/ userActionsDoc / _view / userActions?group_level = 2
这会以我想要的方式返回行结果:
emit([doc.createdBy, doc.type, doc.at], 1);
问题是现在我想过滤给定时间段的结果。所以我希望得到完全相同的信息,但只考虑在给定时间段内发生的行为。
如果我以不同的顺序发出字段,我可以通过“at”过滤文档。
group_level = 3&安培; startkey = [149328316160]&安培; endkey = [1493283161647,{},{}]
"rows":[ {"key":["userId","ACTION_1"],"value":20}, ...
但是我不会得到按userId和actionType分组的结果。有两种方法可以同时拥有吗?也许写自己的reduce函数?
答案 0 :(得分:1)
我感觉到你的痛苦。我过去做过两件不同的事情试图解决类似的问题。
第一种模式是痛苦,可能效果很好或者可能根本不起作用。我经历过这两个。你的map函数看起来像这样:
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函数没有足够快地缩小。在这种情况下,您可以对代码进行字符串化/解析,如代码中的“忽略”注释所示。
这背后的原因是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功能与原始功能相同。基本上,您只是尝试为键中与定义的时间段对应的第一项定义常量值。适用于业务报告,但不能用于允许灵活的时间段。