summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/couchdb/local_dev.ini2
-rw-r--r--share/www/script/test/list_views.js8
-rw-r--r--src/couchdb/couch_httpd_db.erl26
-rw-r--r--src/couchdb/couch_httpd_show.erl89
-rw-r--r--src/couchdb/couch_httpd_view.erl268
5 files changed, 184 insertions, 209 deletions
diff --git a/etc/couchdb/local_dev.ini b/etc/couchdb/local_dev.ini
index 11a703b0..84764e45 100644
--- a/etc/couchdb/local_dev.ini
+++ b/etc/couchdb/local_dev.ini
@@ -12,7 +12,7 @@
;bind_address = 127.0.0.1
[log]
-level = debug
+level = info
[update_notification]
;unique notifier name=/full/path/to/exe -with "cmd line arg"
diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js
index 0bf03900..663a5930 100644
--- a/share/www/script/test/list_views.js
+++ b/share/www/script/test/list_views.js
@@ -248,15 +248,15 @@ couchTests.list_views = function(debug) {
// aborting iteration
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
- T(xhr.responseText.match(/^head 0 1 2 tail$/));
+ T(xhr.responseText.match(/^head 0 1 2 tail$/) && "basic stop");
xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView");
- T(xhr.responseText.match(/^head 0 1 2 tail$/));
+ T(xhr.responseText.match(/^head 0 1 2 tail$/) && "stop 2");
// aborting iteration with reduce
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true");
- T(xhr.responseText.match(/^head 0 1 2 tail$/));
+ T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop");
xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true");
- T(xhr.responseText.match(/^head 0 1 2 tail$/));
+ T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop 2");
// empty list
var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/basicView");
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index b129d37e..f603def0 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -457,21 +457,21 @@ all_docs_view(Req, Db, Keys) ->
true -> StartDocId
end,
FoldAccInit = {Limit, SkipCount, undefined, []},
-
- PassedEndFun =
- case Dir of
- fwd ->
- fun(ViewKey, _ViewId) ->
- couch_db_updater:less_docid(EndKey, ViewKey)
- end;
- rev->
- fun(ViewKey, _ViewId) ->
- couch_db_updater:less_docid(ViewKey, EndKey)
- end
- end,
-
+
case Keys of
nil ->
+ PassedEndFun =
+ case Dir of
+ fwd ->
+ fun(ViewKey, _ViewId) ->
+ couch_db_updater:less_docid(EndKey, ViewKey)
+ end;
+ rev->
+ fun(ViewKey, _ViewId) ->
+ couch_db_updater:less_docid(ViewKey, EndKey)
+ end
+ end,
+
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db,
TotalRowCount, #view_fold_helper_funs{
reduce_count = fun couch_db:enum_docs_reduce_to_count/1,
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index c38522df..49dd88bc 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -117,10 +117,10 @@ send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys) ->
end
end.
-make_map_start_resp_fun(QueryServer, Req, Db, CurrentEtag) ->
- fun(Req2, _Etag, TotalViewCount, Offset) ->
+make_map_start_resp_fun(QueryServer, Db) ->
+ fun(Req, CurrentEtag, TotalViewCount, Offset, _Acc) ->
ExternalResp = couch_query_servers:render_list_head(QueryServer,
- Req2, Db, TotalViewCount, Offset),
+ Req, Db, TotalViewCount, Offset),
JsonResp = apply_etag(ExternalResp, CurrentEtag),
#extern_resp_args{
code = Code,
@@ -134,8 +134,7 @@ make_map_start_resp_fun(QueryServer, Req, Db, CurrentEtag) ->
end.
make_map_send_row_fun(QueryServer, Req) ->
- fun(Resp, Db2, {{Key, DocId}, Value},
- RowFront, _IncludeDocs) ->
+ fun(Resp, Db2, {{Key, DocId}, Value}, _IncludeDocs, RowFront) ->
try
JsonResp = couch_query_servers:render_list_row(QueryServer,
Req, Db2, {{Key, DocId}, Value}),
@@ -144,17 +143,14 @@ make_map_send_row_fun(QueryServer, Req) ->
data = RowBody
} = couch_httpd_external:parse_external_response(JsonResp),
case StopIter of
- true -> stop;
+ true -> {stop, ""};
_ ->
- RowFront2 = case RowFront of
- nil -> [];
- _ -> RowFront
- end,
- Chunk = RowFront2 ++ binary_to_list(RowBody),
+ Chunk = RowFront ++ binary_to_list(RowBody),
case Chunk of
- [] -> {ok, Resp};
+ [] -> ok;
_ -> send_chunk(Resp, Chunk)
- end
+ end,
+ {ok, ""}
end
catch
throw:Error ->
@@ -182,7 +178,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
% pass it into the view fold with closures
{ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
- StartListRespFun = make_map_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
+ StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
SendListRowFun = make_map_send_row_fun(QueryServer, Req),
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount,
@@ -192,7 +188,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
send_row = SendListRowFun
}),
FoldAccInit = {Limit, SkipCount, undefined, []},
- FoldResult = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
+ {ok, FoldResult} = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
end);
@@ -213,11 +209,11 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
% pass it into the view fold with closures
{ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
- StartListRespFun = make_map_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
+ StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
SendListRowFun = make_map_send_row_fun(QueryServer, Req),
FoldAccInit = {Limit, SkipCount, undefined, []},
- FoldResult = lists:foldl(
+ {ok, FoldResult} = lists:foldl(
fun(Key, {ok, FoldAcc}) ->
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs#view_query_args{
start_key = Key,
@@ -234,7 +230,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
end).
make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag) ->
- fun(Req2, _Etag, _, _) ->
+ fun(Req2, _Etag, _Acc) ->
JsonResp = couch_query_servers:render_reduce_head(QueryServer,
Req2, Db),
JsonResp2 = apply_etag(JsonResp, CurrentEtag),
@@ -258,18 +254,15 @@ make_reduce_send_row_fun(QueryServer, Req, Db) ->
stop = StopIter,
data = RowBody
} = couch_httpd_external:parse_external_response(JsonResp),
- RowFront2 = case RowFront of
- nil -> [];
- _ -> RowFront
- end,
case StopIter of
- true -> stop;
+ true -> {stop, ""};
_ ->
- Chunk = RowFront2 ++ binary_to_list(RowBody),
+ Chunk = RowFront ++ binary_to_list(RowBody),
case Chunk of
- [] -> {ok, Resp};
+ [] -> ok;
_ -> send_chunk(Resp, Chunk)
- end
+ end,
+ {ok, ""}
end
catch
throw:Error ->
@@ -307,7 +300,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
send_row = SendListRowFun
}),
FoldAccInit = {Limit, SkipCount, undefined, []},
- FoldResult = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId},
+ {ok, FoldResult} = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId},
{EndKey, EndDocId}, GroupRowsFun, RespFun,
FoldAccInit),
finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
@@ -341,7 +334,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
send_row = SendListRowFun
}),
FoldAccInit = {Limit, SkipCount, undefined, []},
- FoldResult = lists:foldl(
+ {ok, FoldResult} = lists:foldl(
fun(Key, {ok, FoldAcc}) ->
couch_view:fold_reduce(View, Dir, {Key, StartDocId},
{Key, EndDocId}, GroupRowsFun, RespFun, FoldAcc)
@@ -350,29 +343,29 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
end).
finish_list(Req, Db, QueryServer, Etag, FoldResult, StartListRespFun, TotalRows) ->
- case FoldResult of
- {ok, Acc} ->
- JsonTail = couch_query_servers:render_list_tail(QueryServer, Req, Db),
- #extern_resp_args{
- data = Tail
- } = couch_httpd_external:parse_external_response(JsonTail),
- {Resp, BeginBody} = case Acc of
+ {Resp, BeginBody} = case FoldResult of
{_, _, undefined, _} ->
- {ok, Resp2, BeginBody2} = StartListRespFun(Req, Etag, TotalRows, null),
+ {ok, Resp2, BeginBody2} = render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows),
{Resp2, BeginBody2};
- {_, _, Resp2, _} ->
- {Resp2, ""}
- end,
- Chunk = BeginBody ++ binary_to_list(Tail),
- case Chunk of
- [] -> ok;
- _ -> send_chunk(Resp, Chunk)
- end,
- send_chunk(Resp, []);
- Error ->
- throw(Error)
- end.
+ {_, _, Resp0, _} ->
+ {Resp0, ""}
+ end,
+ JsonTail = couch_query_servers:render_list_tail(QueryServer, Req, Db),
+ #extern_resp_args{
+ data = Tail
+ } = couch_httpd_external:parse_external_response(JsonTail),
+ Chunk = BeginBody ++ binary_to_list(Tail),
+ case Chunk of
+ [] -> ok;
+ _ -> send_chunk(Resp, Chunk)
+ end,
+ send_chunk(Resp, []).
+render_head_for_empty_list(StartListRespFun, Req, Etag, null) ->
+ StartListRespFun(Req, Etag, []);
+render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows) ->
+ StartListRespFun(Req, Etag, TotalRows, null, []).
+
send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db) ->
% compute etag with no doc
Headers = MReq:get(headers),
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 0d5dc094..7c13cdf2 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -177,91 +177,28 @@ output_reduce_view(Req, View, Group, QueryArgs, Keys) ->
CurrentEtag = view_group_etag(Group),
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
{ok, GroupRowsFun, RespFun} = make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, #reduce_fold_helper_funs{}),
- {Resp, _} = lists:foldl(
- fun(Key, {Resp, AccSeparator}) ->
- FoldAccInit = {Limit, Skip, Resp, AccSeparator},
- {_, {_, _, Resp2, NewAcc}} = couch_view:fold_reduce(View, Dir, {Key, StartDocId},
+ {Resp, _RedAcc3} = lists:foldl(
+ fun(Key, {Resp, RedAcc}) ->
+ % run the reduce once for each key in keys, with limit etc reapplied for each key
+ FoldAccInit = {Limit, Skip, Resp, RedAcc},
+ {_, {_, _, Resp2, RedAcc2}} = couch_view:fold_reduce(View, Dir, {Key, StartDocId},
{Key, EndDocId}, GroupRowsFun, RespFun, FoldAccInit),
% Switch to comma
- {Resp2, NewAcc}
+ {Resp2, RedAcc2}
end,
{undefined, []}, Keys), % Start with no comma
finish_reduce_fold(Req, Resp)
end).
-
-make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) ->
- #reduce_fold_helper_funs{
- start_response = StartRespFun,
- send_row = SendRowFun
- } = apply_default_helper_funs(HelperFuns),
-
- GroupRowsFun =
- fun({_Key1,_}, {_Key2,_}) when GroupLevel == 0 ->
- true;
- ({Key1,_}, {Key2,_})
- when is_integer(GroupLevel) and is_list(Key1) and is_list(Key2) ->
- lists:sublist(Key1, GroupLevel) == lists:sublist(Key2, GroupLevel);
- ({Key1,_}, {Key2,_}) ->
- Key1 == Key2
- end,
- RespFun = fun(_Key, _Red, {AccLimit, AccSkip, Resp, AccSeparator}) when AccSkip > 0 ->
- {ok, {AccLimit, AccSkip - 1, Resp, AccSeparator}};
- (_Key, _Red, {0, 0, Resp, AccSeparator}) ->
- {stop, {0, 0, Resp, AccSeparator}};
- (_Key, Red, {AccLimit, 0, Resp, AccSeparator}) when GroupLevel == 0 ->
- {ok, Resp2, RowSep} = case Resp of
- undefined -> StartRespFun(Req, Etag, null, null);
- _ -> {ok, Resp, nil}
- end,
- RowResult = case SendRowFun(Resp2, {null, Red}, RowSep) of
- stop -> stop;
- _ -> ok
- end,
- {RowResult, {AccLimit - 1, 0, Resp2, AccSeparator}};
- (Key, Red, {AccLimit, 0, Resp, AccSeparator})
- when is_integer(GroupLevel)
- andalso is_list(Key) ->
- {ok, Resp2, RowSep} = case Resp of
- undefined -> StartRespFun(Req, Etag, null, null);
- _ -> {ok, Resp, nil}
- end,
- RowResult = case SendRowFun(Resp2, {lists:sublist(Key, GroupLevel), Red}, RowSep) of
- stop -> stop;
- _ -> ok
- end,
- {RowResult, {AccLimit - 1, 0, Resp2, AccSeparator}};
- (Key, Red, {AccLimit, 0, Resp, AccSeparator}) ->
- {ok, Resp2, RowSep} = case Resp of
- undefined -> StartRespFun(Req, Etag, null, null);
- _ -> {ok, Resp, nil}
- end,
- RowResult = case SendRowFun(Resp2, {Key, Red}, RowSep) of
- stop -> stop;
- _ -> ok
- end,
- {RowResult, {AccLimit - 1, 0, Resp2, AccSeparator}}
- end,
- {ok, GroupRowsFun, RespFun}.
-
-
reverse_key_default(nil) -> {};
reverse_key_default({}) -> nil;
reverse_key_default(Key) -> Key.
get_stale_type(Req) ->
- QueryList = couch_httpd:qs(Req),
- case proplists:get_value("stale", QueryList, nil) of
- "ok" -> ok;
- Else -> Else
- end.
+ list_to_atom(couch_httpd:qs_value(Req, "stale", "nil")).
get_reduce_type(Req) ->
- QueryList = couch_httpd:qs(Req),
- case proplists:get_value("reduce", QueryList, true) of
- "false" -> false;
- _ -> true
- end.
+ list_to_atom(couch_httpd:qs_value(Req, "reduce", "true")).
parse_view_params(Req, Keys, ViewType, IgnoreType) ->
QueryList = couch_httpd:qs(Req),
@@ -425,8 +362,7 @@ validate_view_query(extra, {Key, _}, Args) ->
Args
end.
-make_view_fold_fun(Req, QueryArgs, Etag, Db,
- TotalViewCount, HelperFuns) ->
+make_view_fold_fun(Req, QueryArgs, Etag, Db, TotalViewCount, HelperFuns) ->
#view_query_args{
end_key = EndKey,
end_docid = EndDocId,
@@ -446,36 +382,92 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db,
include_docs = IncludeDocs
} = QueryArgs,
- fun({{Key, DocId}, Value}, OffsetReds,
- {AccLimit, AccSkip, Resp, AccRevRows}) ->
+ fun({{Key, DocId}, Value}, OffsetReds, {AccLimit, AccSkip, Resp, RowFunAcc}) ->
PassedEnd = PassedEndFun(Key, DocId),
case {PassedEnd, AccLimit, AccSkip, Resp} of
{true, _, _, _} ->
% The stop key has been passed, stop looping.
- {stop, {AccLimit, AccSkip, Resp, AccRevRows}};
+ {stop, {AccLimit, AccSkip, Resp, RowFunAcc}};
{_, 0, _, _} ->
% we've done "limit" rows, stop foldling
- {stop, {0, 0, Resp, AccRevRows}};
+ {stop, {0, 0, Resp, RowFunAcc}};
{_, _, AccSkip, _} when AccSkip > 0 ->
- {ok, {AccLimit, AccSkip - 1, Resp, AccRevRows}};
+ % just keep skipping
+ {ok, {AccLimit, AccSkip - 1, Resp, RowFunAcc}};
{_, _, _, undefined} ->
+ % rendering the first row, first we start the response
Offset = ReduceCountFun(OffsetReds),
- {ok, Resp2, BeginBody} = StartRespFun(Req, Etag,
- TotalViewCount, Offset),
- case SendRowFun(Resp2, Db,
- {{Key, DocId}, Value}, BeginBody, IncludeDocs) of
- stop -> {stop, {AccLimit - 1, 0, Resp2, AccRevRows}};
- _ -> {ok, {AccLimit - 1, 0, Resp2, AccRevRows}}
- end;
+ {ok, Resp2, RowFunAcc0} = StartRespFun(Req, Etag,
+ TotalViewCount, Offset, RowFunAcc),
+ {Go, RowFunAcc2} = SendRowFun(Resp2, Db, {{Key, DocId}, Value},
+ IncludeDocs, RowFunAcc0),
+ {Go, {AccLimit - 1, 0, Resp2, RowFunAcc2}};
{_, AccLimit, _, Resp} when (AccLimit > 0) ->
- case SendRowFun(Resp, Db,
- {{Key, DocId}, Value}, nil, IncludeDocs) of
- stop -> {stop, {AccLimit - 1, 0, Resp, AccRevRows}};
- _ -> {ok, {AccLimit - 1, 0, Resp, AccRevRows}}
- end
+ % rendering all other rows
+ {Go, RowFunAcc2} = SendRowFun(Resp, Db, {{Key, DocId}, Value},
+ IncludeDocs, RowFunAcc),
+ {Go, {AccLimit - 1, 0, Resp, RowFunAcc2}}
end
end.
+make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) ->
+ #reduce_fold_helper_funs{
+ start_response = StartRespFun,
+ send_row = SendRowFun
+ } = apply_default_helper_funs(HelperFuns),
+
+ GroupRowsFun =
+ fun({_Key1,_}, {_Key2,_}) when GroupLevel == 0 ->
+ true;
+ ({Key1,_}, {Key2,_})
+ when is_integer(GroupLevel) and is_list(Key1) and is_list(Key2) ->
+ lists:sublist(Key1, GroupLevel) == lists:sublist(Key2, GroupLevel);
+ ({Key1,_}, {Key2,_}) ->
+ Key1 == Key2
+ end,
+
+ RespFun = fun
+ (_Key, _Red, {AccLimit, AccSkip, Resp, RowAcc}) when AccSkip > 0 ->
+ % keep skipping
+ {ok, {AccLimit, AccSkip - 1, Resp, RowAcc}};
+ (_Key, _Red, {0, _AccSkip, Resp, RowAcc}) ->
+ % we've exhausted limit rows, stop
+ {stop, {0, _AccSkip, Resp, RowAcc}};
+
+ (_Key, Red, {AccLimit, 0, undefined, RowAcc0}) when GroupLevel == 0 ->
+ % we haven't started responding yet and group=false
+ {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0),
+ {Go, RowAcc2} = SendRowFun(Resp2, {null, Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};
+ (_Key, Red, {AccLimit, 0, Resp, RowAcc}) when GroupLevel == 0 ->
+ % group=false but we've already started the response
+ {Go, RowAcc2} = SendRowFun(Resp, {null, Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp, RowAcc2}};
+
+ (Key, Red, {AccLimit, 0, undefined, RowAcc0})
+ when is_integer(GroupLevel), is_list(Key) ->
+ % group_level and we haven't responded yet
+ {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0),
+ {Go, RowAcc2} = SendRowFun(Resp2, {lists:sublist(Key, GroupLevel), Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};
+ (Key, Red, {AccLimit, 0, Resp, RowAcc})
+ when is_integer(GroupLevel), is_list(Key) ->
+ % group_level and we've already started the response
+ {Go, RowAcc2} = SendRowFun(Resp, {lists:sublist(Key, GroupLevel), Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp, RowAcc2}};
+
+ (Key, Red, {AccLimit, 0, undefined, RowAcc0}) ->
+ % group=true and we haven't responded yet
+ {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0),
+ {Go, RowAcc2} = SendRowFun(Resp2, {Key, Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};
+ (Key, Red, {AccLimit, 0, Resp, RowAcc}) ->
+ % group=true and we've already started the response
+ {Go, RowAcc2} = SendRowFun(Resp, {Key, Red}, RowAcc),
+ {Go, {AccLimit - 1, 0, Resp, RowAcc2}}
+ end,
+ {ok, GroupRowsFun, RespFun}.
+
apply_default_helper_funs(#view_fold_helper_funs{
passed_end = PassedEnd,
start_response = StartResp,
@@ -487,7 +479,7 @@ apply_default_helper_funs(#view_fold_helper_funs{
end,
StartResp2 = case StartResp of
- undefined -> fun json_view_start_resp/4;
+ undefined -> fun json_view_start_resp/5;
_ -> StartResp
end,
@@ -507,7 +499,7 @@ apply_default_helper_funs(#reduce_fold_helper_funs{
send_row = SendRow
}=Helpers) ->
StartResp2 = case StartResp of
- undefined -> fun json_reduce_start_resp/4;
+ undefined -> fun json_reduce_start_resp/3;
_ -> StartResp
end,
@@ -551,31 +543,24 @@ make_passed_end_fun(rev, EndKey, EndDocId, InclusiveEnd) ->
end
end.
-json_view_start_resp(Req, Etag, TotalViewCount, Offset) ->
+json_view_start_resp(Req, Etag, TotalViewCount, Offset, _Acc) ->
{ok, Resp} = start_json_response(Req, 200, [{"Etag", Etag}]),
BeginBody = io_lib:format("{\"total_rows\":~w,\"offset\":~w,\"rows\":[\r\n",
[TotalViewCount, Offset]),
{ok, Resp, BeginBody}.
-send_json_view_row(Resp, Db, {{Key, DocId}, Value}, RowFront, IncludeDocs) ->
+send_json_view_row(Resp, Db, {{Key, DocId}, Value}, IncludeDocs, RowFront) ->
JsonObj = view_row_obj(Db, {{Key, DocId}, Value}, IncludeDocs),
- RowFront2 = case RowFront of
- nil -> ",\r\n";
- _ -> RowFront
- end,
- send_chunk(Resp, RowFront2 ++ ?JSON_ENCODE(JsonObj)).
+ send_chunk(Resp, RowFront ++ ?JSON_ENCODE(JsonObj)),
+ {ok, ",\r\n"}.
-json_reduce_start_resp(Req, Etag, _, _) ->
+json_reduce_start_resp(Req, Etag, _Acc0) ->
{ok, Resp} = start_json_response(Req, 200, [{"Etag", Etag}]),
- BeginBody = "{\"rows\":[\r\n",
- {ok, Resp, BeginBody}.
+ {ok, Resp, "{\"rows\":[\r\n"}.
send_json_reduce_row(Resp, {Key, Value}, RowFront) ->
- RowFront2 = case RowFront of
- nil -> ",\r\n";
- _ -> RowFront
- end,
- send_chunk(Resp, RowFront2 ++ ?JSON_ENCODE({[{key, Key}, {value, Value}]})).
+ send_chunk(Resp, RowFront ++ ?JSON_ENCODE({[{key, Key}, {value, Value}]})),
+ {ok, ",\r\n"}.
view_group_etag(Group) ->
view_group_etag(Group, nil).
@@ -587,41 +572,38 @@ view_group_etag(#group{sig=Sig,current_seq=CurrentSeq}, Extra) ->
% track of the last Db seq that caused an index change.
couch_httpd:make_etag({Sig, CurrentSeq, Extra}).
-view_row_obj(Db, {{Key, DocId}, Value}, IncludeDocs) ->
- case DocId of
- error ->
- {[{key, Key}, {error, Value}]};
- _ ->
- case IncludeDocs of
- true ->
- Rev = case Value of
- {Props} ->
- case proplists:get_value(<<"_rev">>, Props) of
- undefined ->
- nil;
- Rev0 ->
- couch_doc:parse_rev(Rev0)
- end;
- _ ->
- nil
- end,
- ?LOG_DEBUG("Include Doc: ~p ~p", [DocId, Rev]),
- case (catch couch_httpd_db:couch_doc_open(Db, DocId, Rev, [])) of
- {{not_found, missing}, _RevId} ->
- {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
- {not_found, missing} ->
- {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
- {not_found, deleted} ->
- {[{id, DocId}, {key, Key}, {value, Value}]};
- Doc ->
- JsonDoc = couch_doc:to_json_obj(Doc, []),
- {[{id, DocId}, {key, Key}, {value, Value}, {doc, JsonDoc}]}
- end;
- _ ->
- {[{id, DocId}, {key, Key}, {value, Value}]}
- end
+% the view row has an error
+view_row_obj(_Db, {{Key, error}, Value}, _IncludeDocs) ->
+ {[{key, Key}, {error, Value}]};
+% include docs in the view output
+view_row_obj(Db, {{Key, DocId}, {Props}}, true) ->
+ Rev = case proplists:get_value(<<"_rev">>, Props) of
+ undefined ->
+ nil;
+ Rev0 ->
+ couch_doc:parse_rev(Rev0)
+ end,
+ view_row_with_doc(Db, {{Key, DocId}, {Props}}, Rev);
+view_row_obj(Db, {{Key, DocId}, Value}, true) ->
+ view_row_with_doc(Db, {{Key, DocId}, Value}, nil);
+% the normal case for rendering a view row
+view_row_obj(_Db, {{Key, DocId}, Value}, _IncludeDocs) ->
+ {[{id, DocId}, {key, Key}, {value, Value}]}.
+
+view_row_with_doc(Db, {{Key, DocId}, Value}, Rev) ->
+ ?LOG_DEBUG("Include Doc: ~p ~p", [DocId, Rev]),
+ case (catch couch_httpd_db:couch_doc_open(Db, DocId, Rev, [])) of
+ {{not_found, missing}, _RevId} ->
+ {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
+ {not_found, missing} ->
+ {[{id, DocId}, {key, Key}, {value, Value}, {error, missing}]};
+ {not_found, deleted} ->
+ {[{id, DocId}, {key, Key}, {value, Value}]};
+ Doc ->
+ JsonDoc = couch_doc:to_json_obj(Doc, []),
+ {[{id, DocId}, {key, Key}, {value, Value}, {doc, JsonDoc}]}
end.
-
+
finish_view_fold(Req, TotalRows, FoldResult) ->
case FoldResult of
{ok, {_, _, undefined, _}} ->
@@ -631,9 +613,9 @@ finish_view_fold(Req, TotalRows, FoldResult) ->
{total_rows, TotalRows},
{rows, []}
]});
- {ok, {_, _, Resp, AccRevRows}} ->
+ {ok, {_, _, Resp, _}} ->
% end the view
- send_chunk(Resp, AccRevRows ++ "\r\n]}"),
+ send_chunk(Resp, "\r\n]}"),
end_json_response(Resp);
Error ->
throw(Error)