尝试基本的 couchdb erlang 视图
Attempting a basic couchdb erlang view
我正在尝试做一个简单的视图,它只发出 id 以 "rating-" 开头的文档。我似乎根本无法在 couchdb 中调用任何 "string:" 函数。真的不知道该怎么做......我见过的每个例子都从不比较部分价值,总是比较整个价值。在 ecmascript 中,我可能只是做 if (!doc._id.indexOf('rating-'))
。在下面的代码中,它抱怨提供给 split 的参数之一无效。
fun({Doc}) ->
Id = proplists:get_value(<<"_id">>, Doc),
DocChk = binary:split(Id, <<"-">>),
case array:get(0, DocChk) of
<<"rating-">> -> Emit(Id, nil)
end
end.
我已经进行了几次尝试,我一直在 Erlang shell 中尝试一些东西,虽然我偶尔不会遇到语法错误,但我永远无法得到 运行 上的任何东西沙发数据库。这是我试图从 ecmascript 改编的整个视图。如您所见...我仍然停留在第 1 行哈哈。
地图
function (doc) {
if (doc._id.indexOf('rating-') !== 0) return;
if (!doc.isValid) return;
var nps;
doc.results.forEach(function (r) {
if (r.type === 'Nps') {
nps = r;
}
});
if (!nps) return;
var result = { t: 1, d: 0, p: 0 };
switch (nps.score) {
case 10:
case 9:
result.p++;
break;
case 8:
case 7:
break;
default:
result.d++;
break;
}
var week = new Date(doc.dateCaptured.year, doc.dateCaptured.month -1, doc.dateCaptured.day);
week.setDate(week.getDate() - ((week.getDay() === 0 ? 7 : week.getDay())-1));
emit(['week', doc.companyId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day', doc.companyId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month', doc.companyId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year', doc.companyId, doc.dateCaptured.year], result);
emit(['week-bylocation', doc.companyId, doc.locationId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year], result);
emit(['week-bysource', doc.companyId, doc.sourceId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year], result);
}
减少
function (keys, values, rereduce) {
var result = { t: 0, p: 0, d: 0 };
values.forEach(function (v) {
result.t += v.t;
result.p += v.p;
result.d += v.d;
});
return result;
}
这个视图的性能太慢了。这不会逐渐成为问题,但我必须为索引 6.3M 行设置种子,并且在总共 12 个内核的 3 节点集群上需要大约 12 小时。肯定cpu绑定。
编辑
多亏了 Hynek,我才能够移植我的地图功能。我确定它的 erlang 效率很低,但它似乎比对应的 ecmascript 快 30 倍。
fun({Doc}) ->
case lists:keyfind(<<"_id">>, 1, Doc) of
{_, <<"rating-", _/bytes>> = Id} ->
case couch_util:get_value(<<"isValid">>, Doc) of
true ->
Results = proplists:get_value(<<"results">>, Doc),
case lists:dropwhile(fun({R}) -> <<"Nps">> /= proplists:get_value(<<"type">>, R) end, Results) of
[] -> ok;
[{Nps} | _] ->
Score = proplists:get_value(<<"score">>, Nps),
A = case Score of
S when S > 8 -> [1, 0, 1];
S when S > 6 -> [0, 0, 1];
_ -> [0, 1, 1]
end,
CompanyId = proplists:get_value(<<"companyId">>, Doc),
LocationId = proplists:get_value(<<"locationId">>, Doc),
SourceId = proplists:get_value(<<"sourceId">>, Doc),
{DateCaptured} = proplists:get_value(<<"dateCaptured">>, Doc),
Year = proplists:get_value(<<"year">>, DateCaptured),
Month = proplists:get_value(<<"month">>, DateCaptured),
Day = proplists:get_value(<<"day">>, DateCaptured),
Emit([<<"year">>, CompanyId, Year], A),
Emit([<<"month">>, CompanyId, Year, Month], A),
Emit([<<"day">>, CompanyId, Year, Month, Day], A),
Emit([<<"year-bylocation">>, CompanyId, LocationId, Year], A),
Emit([<<"month-bylocation">>, CompanyId, LocationId, Year, Month], A),
Emit([<<"day-bylocation">>, CompanyId, LocationId, Year, Month, Day], A),
Emit([<<"year-bysource">>, CompanyId, SourceId, Year], A),
Emit([<<"month-bysource">>, CompanyId, SourceId, Year, Month], A),
Emit([<<"day-bysource">>, CompanyId, SourceId, Year, Month, Day], A)
end;
_ -> ok
end;
_ -> ok
end
end.
问题是 binary:split/2
结果不是 array
,所以您不能使用 array:get/2
。好吧,任何经验丰富的 Erlanger 都会这样写:
fun({Doc}) ->
case lists:keyfind(<<"_id">>, 1, Doc) of
{_, <<"rating-", _/bytes>> = Id} -> Emit(Id, nil);
_ -> ok
end
end.
或者,如果您不喜欢冒险并希望坚持使用推荐的功能(可能会慢一点):
fun({Doc}) ->
case couch_util:get_value(<<"_id">>, Doc) of
<<"rating-", _/bytes>> = Id -> Emit(Id, nil);
_ -> ok
end
end.
编辑:
Erlang 中 =
的含义需要更多解释。 Erlang 中的等号更像是数学中的等号,但不完全是。
1> X = 1.
1
2> 1 = X.
1
3> 1 = A = X.
1
4> A.
1
5> B = 1 = X.
1
6> B.
1
7> 1 = Z.
* 1: variable 'Z' is unbound
8> 2 = X.
** exception error: no match of right hand side value 1
那里发生了什么事? =
符号有两个不同的含义,并且与单个赋值 a.k.a 密切相关。捆绑。首先,它是表达式中的匹配运算符。你可以写一个匹配表达式来代替任何表达式:
<Pattern> = <Expression>
在第 3 和第 5 个表达式中是最右边的 =
,在上例中的其他表达式中是唯一的 =
。在第一个表达式之后,X
已经绑定了值为 1
的变量,它的左边是一个模式。模式可以是简单值或变量或复合模式。变量可以是绑定的或未绑定的。如果变量未绑定,它将绑定到值。如果变量已经绑定,则检查匹配。如果在匹配表达式中使用,匹配失败会引发 badmatch
异常(class error
),它以人类可读的格式显示在 shell 中,至于第 8 个表达式,因为我没有不知道 R17-ish。无论如何,您不能在匹配表达式中更改 =
运算符的左侧和右侧,因为变量绑定只能发生在模式中,例如=
的左操作数,如第 7 个表达式所示。
但是你也可以在模式中使用 =
,它作为统一,更类似于数学中的相等,你可以自由地左右交换。出于一个简单的原因,有经验的 Erlanger 习惯于按照我在示例中使用的方式编写模式。如果你阅读模式,你想知道值应该如何看起来像第一个和变量,你希望将值绑定为第二个不太重要的东西,或者你稍后在阅读代码的过程中需要知道的东西。
模式不仅出现在匹配表达式中,还出现在函数子句中(实际上示例中的 {Doc}
也是一种模式)以及 case
、try
, receive
表达式和列表理解生成器。模式匹配是 Erlang 语言本身最强大和最酷的特性之一,在我的例子中,我只展示了它的一瞥。您可以将复杂的数据结构解构为像 {_, <<"rating-", _/bytes>> = Id}
这样的元组。您可以模式匹配二进制文件,如本部分所示 <<"rating-", _/bytes>>
.
二进制文件的模式匹配使用 Bit Syntax,这是一个强大的工具,它使在 Erlang 中实现二进制协议变得简单而有趣,但在这里我使用它来使代码比 ecmascript 对应的代码更高效和清晰。在这里,我只是明确表示我对以前缀 rating-
开头并继续任何内容(任何二进制 a.k.a 字节)_/bytes
的二进制文件感兴趣。 (我更喜欢使用 bytes
和 bits
而不是 binary
和 bitstring
因为它似乎更能表达意图。)
我正在尝试做一个简单的视图,它只发出 id 以 "rating-" 开头的文档。我似乎根本无法在 couchdb 中调用任何 "string:" 函数。真的不知道该怎么做......我见过的每个例子都从不比较部分价值,总是比较整个价值。在 ecmascript 中,我可能只是做 if (!doc._id.indexOf('rating-'))
。在下面的代码中,它抱怨提供给 split 的参数之一无效。
fun({Doc}) ->
Id = proplists:get_value(<<"_id">>, Doc),
DocChk = binary:split(Id, <<"-">>),
case array:get(0, DocChk) of
<<"rating-">> -> Emit(Id, nil)
end
end.
我已经进行了几次尝试,我一直在 Erlang shell 中尝试一些东西,虽然我偶尔不会遇到语法错误,但我永远无法得到 运行 上的任何东西沙发数据库。这是我试图从 ecmascript 改编的整个视图。如您所见...我仍然停留在第 1 行哈哈。
地图
function (doc) {
if (doc._id.indexOf('rating-') !== 0) return;
if (!doc.isValid) return;
var nps;
doc.results.forEach(function (r) {
if (r.type === 'Nps') {
nps = r;
}
});
if (!nps) return;
var result = { t: 1, d: 0, p: 0 };
switch (nps.score) {
case 10:
case 9:
result.p++;
break;
case 8:
case 7:
break;
default:
result.d++;
break;
}
var week = new Date(doc.dateCaptured.year, doc.dateCaptured.month -1, doc.dateCaptured.day);
week.setDate(week.getDate() - ((week.getDay() === 0 ? 7 : week.getDay())-1));
emit(['week', doc.companyId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day', doc.companyId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month', doc.companyId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year', doc.companyId, doc.dateCaptured.year], result);
emit(['week-bylocation', doc.companyId, doc.locationId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year-bylocation', doc.companyId, doc.locationId, doc.dateCaptured.year], result);
emit(['week-bysource', doc.companyId, doc.sourceId, week.getFullYear(), week.getMonth()+1, week.getDate()], result);
emit(['day-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year, doc.dateCaptured.month, doc.dateCaptured.day], result);
emit(['month-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year, doc.dateCaptured.month], result);
emit(['year-bysource', doc.companyId, doc.sourceId, doc.dateCaptured.year], result);
}
减少
function (keys, values, rereduce) {
var result = { t: 0, p: 0, d: 0 };
values.forEach(function (v) {
result.t += v.t;
result.p += v.p;
result.d += v.d;
});
return result;
}
这个视图的性能太慢了。这不会逐渐成为问题,但我必须为索引 6.3M 行设置种子,并且在总共 12 个内核的 3 节点集群上需要大约 12 小时。肯定cpu绑定。
编辑
多亏了 Hynek,我才能够移植我的地图功能。我确定它的 erlang 效率很低,但它似乎比对应的 ecmascript 快 30 倍。
fun({Doc}) ->
case lists:keyfind(<<"_id">>, 1, Doc) of
{_, <<"rating-", _/bytes>> = Id} ->
case couch_util:get_value(<<"isValid">>, Doc) of
true ->
Results = proplists:get_value(<<"results">>, Doc),
case lists:dropwhile(fun({R}) -> <<"Nps">> /= proplists:get_value(<<"type">>, R) end, Results) of
[] -> ok;
[{Nps} | _] ->
Score = proplists:get_value(<<"score">>, Nps),
A = case Score of
S when S > 8 -> [1, 0, 1];
S when S > 6 -> [0, 0, 1];
_ -> [0, 1, 1]
end,
CompanyId = proplists:get_value(<<"companyId">>, Doc),
LocationId = proplists:get_value(<<"locationId">>, Doc),
SourceId = proplists:get_value(<<"sourceId">>, Doc),
{DateCaptured} = proplists:get_value(<<"dateCaptured">>, Doc),
Year = proplists:get_value(<<"year">>, DateCaptured),
Month = proplists:get_value(<<"month">>, DateCaptured),
Day = proplists:get_value(<<"day">>, DateCaptured),
Emit([<<"year">>, CompanyId, Year], A),
Emit([<<"month">>, CompanyId, Year, Month], A),
Emit([<<"day">>, CompanyId, Year, Month, Day], A),
Emit([<<"year-bylocation">>, CompanyId, LocationId, Year], A),
Emit([<<"month-bylocation">>, CompanyId, LocationId, Year, Month], A),
Emit([<<"day-bylocation">>, CompanyId, LocationId, Year, Month, Day], A),
Emit([<<"year-bysource">>, CompanyId, SourceId, Year], A),
Emit([<<"month-bysource">>, CompanyId, SourceId, Year, Month], A),
Emit([<<"day-bysource">>, CompanyId, SourceId, Year, Month, Day], A)
end;
_ -> ok
end;
_ -> ok
end
end.
问题是 binary:split/2
结果不是 array
,所以您不能使用 array:get/2
。好吧,任何经验丰富的 Erlanger 都会这样写:
fun({Doc}) ->
case lists:keyfind(<<"_id">>, 1, Doc) of
{_, <<"rating-", _/bytes>> = Id} -> Emit(Id, nil);
_ -> ok
end
end.
或者,如果您不喜欢冒险并希望坚持使用推荐的功能(可能会慢一点):
fun({Doc}) ->
case couch_util:get_value(<<"_id">>, Doc) of
<<"rating-", _/bytes>> = Id -> Emit(Id, nil);
_ -> ok
end
end.
编辑:
Erlang 中 =
的含义需要更多解释。 Erlang 中的等号更像是数学中的等号,但不完全是。
1> X = 1.
1
2> 1 = X.
1
3> 1 = A = X.
1
4> A.
1
5> B = 1 = X.
1
6> B.
1
7> 1 = Z.
* 1: variable 'Z' is unbound
8> 2 = X.
** exception error: no match of right hand side value 1
那里发生了什么事? =
符号有两个不同的含义,并且与单个赋值 a.k.a 密切相关。捆绑。首先,它是表达式中的匹配运算符。你可以写一个匹配表达式来代替任何表达式:
<Pattern> = <Expression>
在第 3 和第 5 个表达式中是最右边的 =
,在上例中的其他表达式中是唯一的 =
。在第一个表达式之后,X
已经绑定了值为 1
的变量,它的左边是一个模式。模式可以是简单值或变量或复合模式。变量可以是绑定的或未绑定的。如果变量未绑定,它将绑定到值。如果变量已经绑定,则检查匹配。如果在匹配表达式中使用,匹配失败会引发 badmatch
异常(class error
),它以人类可读的格式显示在 shell 中,至于第 8 个表达式,因为我没有不知道 R17-ish。无论如何,您不能在匹配表达式中更改 =
运算符的左侧和右侧,因为变量绑定只能发生在模式中,例如=
的左操作数,如第 7 个表达式所示。
但是你也可以在模式中使用 =
,它作为统一,更类似于数学中的相等,你可以自由地左右交换。出于一个简单的原因,有经验的 Erlanger 习惯于按照我在示例中使用的方式编写模式。如果你阅读模式,你想知道值应该如何看起来像第一个和变量,你希望将值绑定为第二个不太重要的东西,或者你稍后在阅读代码的过程中需要知道的东西。
模式不仅出现在匹配表达式中,还出现在函数子句中(实际上示例中的 {Doc}
也是一种模式)以及 case
、try
, receive
表达式和列表理解生成器。模式匹配是 Erlang 语言本身最强大和最酷的特性之一,在我的例子中,我只展示了它的一瞥。您可以将复杂的数据结构解构为像 {_, <<"rating-", _/bytes>> = Id}
这样的元组。您可以模式匹配二进制文件,如本部分所示 <<"rating-", _/bytes>>
.
二进制文件的模式匹配使用 Bit Syntax,这是一个强大的工具,它使在 Erlang 中实现二进制协议变得简单而有趣,但在这里我使用它来使代码比 ecmascript 对应的代码更高效和清晰。在这里,我只是明确表示我对以前缀 rating-
开头并继续任何内容(任何二进制 a.k.a 字节)_/bytes
的二进制文件感兴趣。 (我更喜欢使用 bytes
和 bits
而不是 binary
和 bitstring
因为它似乎更能表达意图。)