Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emit Tuples From Erlang Views In CouchDB

CouchDB, version 0.10.0, using native erlang views.

I have a simple document of the form:

{
   "_id": "user-1",
   "_rev": "1-9ccf63b66b62d15d75daa211c5a7fb0d",
   "type": "user",
   "identifiers": [
       "ABC",
       "DEF",
       "123"
   ],
   "username": "monkey",
   "name": "Monkey Man"
}

And a basic javascript design document:

{
   "_id": "_design/user",
   "_rev": "1-94bd8a0dbce5e2efd699d17acea1db0b",
   "language": "javascript",
   "views": {
     "find_by_identifier": {
       "map": "function(doc) {
          if (doc.type == 'user') {
            doc.identifiers.forEach(function(identifier) {
              emit(identifier, {\"username\":doc.username,\"name\":doc.name});
            });
          }
       }"
     }
   }
}

which emits:

{"total_rows":3,"offset":0,"rows":[
{"id":"user-1","key":"ABC","value":{"username":"monkey","name":"Monkey Man"}},
{"id":"user-1","key":"DEF","value":{"username":"monkey","name":"Monkey Man"}},
{"id":"user-1","key":"123","value":{"username":"monkey","name":"Monkey Man"}}
]}

I'm looking into building an Erlang view that does the same thing. Best attempt so far is:

%% Map Function
fun({Doc}) ->
    case proplists:get_value(<<"type">>, Doc) of
    undefined ->
        ok;
    Type ->
        Identifiers = proplists:get_value(<<"identifiers">>, Doc),
        ID = proplists:get_value(<<"_id">>, Doc),
        Username = proplists:get_value(<<"username">>, Doc),
        Name = proplists:get_value(<<"name">>, Doc),
        lists:foreach(fun(Identifier) -> Emit(Identifier, [ID, Username, Name]) end, Identifiers);
    _ ->
        ok
    end
end.

which emits:

{"total_rows":3,"offset":0,"rows":[
{"id":"user-1","key":"ABC","value":["monkey","Monkey Man"]},
{"id":"user-1","key":"DEF","value":["monkey","Monkey Man"]},
{"id":"user-1","key":"123","value":["monkey","Monkey Man"]}
]}

The question is - how can I get those values out as tuples, instead of as arrays? I don't imagine I can (or would want to) use records, but using atoms in a tuple doesn't seem to work.

lists:foreach(fun(Identifier) -> Emit(Identifier, {id, ID, username, Username, name, Name}) end, Identifiers);

Fails with the following error:

{"error":"json_encode","reason":"{bad_term,{<<\"user-1\">>,<<\"monkey\">>,<<\"Monkey Man\">>}}"}

Thoughts? I know that Erlang sucks for this specific kind of thing (named access) and that I can do it by convention (id at first position, username next, real name last), but that makes the client side code pretty ugly.

like image 450
majelbstoat Avatar asked Mar 10 '10 05:03

majelbstoat


2 Answers

The JSON object {"foo":"bar","baz":1} is {[{<<"foo">>,<<"bar">>},{<<"baz">>,1}]}

In Erlang lingua it is a proplist wrapped in a tuple.

It's not pretty, but very efficient :)

To get a feel for it you can play with the JSON lib that ships with CouchDB:

  1. Start CouchDB with the -i (interactive) flag
  2. On the resulting erlang shell, type: couch_util:json_decode(<<"{\"foo\":\"bar\"}">>).
  3. Profit

// in later versions of CouchDB, this is ejson:decode()

like image 61
Jan Lehnardt Avatar answered Oct 11 '22 00:10

Jan Lehnardt


For test_suite_reports bd, that has tests field:

[
   {
       "name": "basics",
       "status": "success",
       "duration": 21795
   },
   {
       "name": "all_docs",
       "status": "success",
       "duration": 385
   } ...

I have wrote this to get name and status:

fun({Doc}) ->
  Name = fun(L) ->  proplists:get_value(<<"name">>, L, null) end,
  Status = fun(L) -> proplists:get_value(<<"status">>, L, null) end,
  Tests = proplists:get_value(<<"tests">>, Doc, null),
  lists:foreach(fun({L}) -> Emit(Name(L), Status(L)) end, Tests)
end.
like image 25
Denis Avatar answered Oct 11 '22 00:10

Denis