我试图做一个简单的视图,它只发出id以" rating - "开头的文档。我似乎无法调用任何"字符串:"在couchdb中完成所有功能。真的不确定如何解决这个问题......我所见过的每个例子都不会比较价值的一部分,总是价值。在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中尝试了一些东西,虽然我偶尔也没有得到语法错误,但我无法在couchdb上运行任何东西。这是整个观点,我试图从ecmascript中适应。正如你所看到的那样......我仍然坚持第1行lol。
地图
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;
}
这种观点的表现太慢了。它不会&#39;逐渐成为一个问题,但我必须为6.3M行编制索引,并且在3个节点集群上需要大约12小时,总共12个核心。肯定是cpu绑定。
修改
感谢Hynek,我已经能够移植我的地图功能了。我确定它的效率非常低,但它的效率似乎比它的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.
答案 0 :(得分:2)
问题是binary:split/2
结果不是array
,因此您无法使用array:get/2
。好吧,任何经验丰富的厄兰格都会这样写:
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. binding紧密配合。首先,它是表达式中的匹配运算符。您可以代替任何表达式编写匹配表达式:
<Pattern> = <Expression>
在上面的示例中,它是第3和第5个表达式中最右边的=
和其他表达式中的唯一=
。在第一个表达式之后,X
已经绑定变量值为1
,左边是模式。模式可以是简单值或可变或复合模式。变量可以绑定或不绑定。如果变量未绑定,则绑定到该值。如果变量已经绑定,则匹配匹配。如果在匹配表达式中使用匹配失败引发badmatch
异常(类error
),它在shell中以人类可读的格式显示为第8个表达式,因为我不知道R17-ish 。无论如何,您无法在匹配表达式中更改=
运算符的左侧和右侧,因为变量绑定只能在模式中发生,例如:左{* 1}}的操作数,如第7个表达式所示。
但是你也可以在模式中使用=
,并且在那里它可以作为统一使用,它在数学上更相似,你可以自由地左右交换。经验丰富的错误用于以我在我的示例中使用的方式编写模式,原因很简单。如果您阅读模式,您想知道该值应该首先是什么样的,并且您想要将值绑定到第二个不太重要的位置,或者稍后在读取代码时需要知道的内容。
模式不仅仅出现在匹配表达式中,而且还出现在函数子句中(实例中=
实际上也是模式)以及{Doc}
,case
,try
表达式和列表推导生成器。模式匹配是Erlang语言本身最强大和最酷的功能之一,在我的例子中,我只展示了它的一瞥。您可以将复杂的数据结构解构为receive
之类的元组。您可以将匹配二进制文件设置为显示此部分{_, <<"rating-", _/bytes>> = Id}
。
二进制文件的模式匹配使用Bit Syntax,这是一个强大的工具,它使得在Erlang中实现二进制协议变得简单而有趣,但在这里我用它来使代码比ecmascript对应代码更高效,更清晰。在这里,我只是清楚地表明我对以前缀<<"rating-", _/bytes>>
开头的二进制文件感兴趣并继续任何事情(任何二进制文件a.k.a字节)rating-
。 (我更喜欢在_/bytes
和bytes
上使用bits
和binary
,因为它似乎更能表达意图。)