尝试基本的couchdb erlang视图

时间:2018-06-10 05:55:31

标签: erlang couchdb

我试图做一个简单的视图,它只发出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.

1 个答案:

答案 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}casetry表达式和列表推导生成器。模式匹配是Erlang语言本身最强大和最酷的功能之一,在我的例子中,我只展示了它的一瞥。您可以将复杂的数据结构解构为receive之类的元组。您可以将匹配二进制文件设置为显示此部分{_, <<"rating-", _/bytes>> = Id}

二进制文件的模式匹配使用Bit Syntax,这是一个强大的工具,它使得在Erlang中实现二进制协议变得简单而有趣,但在这里我用它来使代码比ecmascript对应代码更高效,更清晰。在这里,我只是清楚地表明我对以前缀<<"rating-", _/bytes>>开头的二进制文件感兴趣并继续任何事情(任何二进制文件a.k.a字节)rating-。 (我更喜欢在_/bytesbytes上使用bitsbinary,因为它似乎更能表达意图。)