From cd39ebe7d12d999324ff2cc9842567b34dc4d4c7 Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Sun, 14 Jun 2009 18:45:49 +0000 Subject: merge list-iterator branch to trunk. changes JavaScript _list API git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@784601 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_httpd_show.erl | 181 ++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 105 deletions(-) (limited to 'src/couchdb/couch_httpd_show.erl') diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl index 49dd88bc..ef5d41d1 100644 --- a/src/couchdb/couch_httpd_show.erl +++ b/src/couchdb/couch_httpd_show.erl @@ -117,47 +117,6 @@ send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys) -> end end. -make_map_start_resp_fun(QueryServer, Db) -> - fun(Req, CurrentEtag, TotalViewCount, Offset, _Acc) -> - ExternalResp = couch_query_servers:render_list_head(QueryServer, - Req, Db, TotalViewCount, Offset), - JsonResp = apply_etag(ExternalResp, CurrentEtag), - #extern_resp_args{ - code = Code, - data = BeginBody, - ctype = CType, - headers = ExtHeaders - } = couch_httpd_external:parse_external_response(JsonResp), - JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), - {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), - {ok, Resp, binary_to_list(BeginBody)} - end. - -make_map_send_row_fun(QueryServer, Req) -> - fun(Resp, Db2, {{Key, DocId}, Value}, _IncludeDocs, RowFront) -> - try - JsonResp = couch_query_servers:render_list_row(QueryServer, - Req, Db2, {{Key, DocId}, Value}), - #extern_resp_args{ - stop = StopIter, - data = RowBody - } = couch_httpd_external:parse_external_response(JsonResp), - case StopIter of - true -> {stop, ""}; - _ -> - Chunk = RowFront ++ binary_to_list(RowBody), - case Chunk of - [] -> ok; - _ -> send_chunk(Resp, Chunk) - end, - {ok, ""} - end - catch - throw:Error -> - send_chunked_error(Resp, Error), - throw({already_sent, Resp, Error}) - end - end. output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) -> #view_query_args{ @@ -179,7 +138,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc), StartListRespFun = make_map_start_resp_fun(QueryServer, Db), - SendListRowFun = make_map_send_row_fun(QueryServer, Req), + SendListRowFun = make_map_send_row_fun(QueryServer), FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount, #view_fold_helper_funs{ @@ -189,7 +148,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer }), FoldAccInit = {Limit, SkipCount, undefined, []}, {ok, FoldResult} = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit), - finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount) + finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount) end); output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) -> @@ -210,7 +169,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc), StartListRespFun = make_map_start_resp_fun(QueryServer, Db), - SendListRowFun = make_map_send_row_fun(QueryServer, Req), + SendListRowFun = make_map_send_row_fun(QueryServer), FoldAccInit = {Limit, SkipCount, undefined, []}, {ok, FoldResult} = lists:foldl( @@ -226,49 +185,64 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer }), couch_view:fold(View, {Key, StartDocId}, Dir, FoldlFun, FoldAcc) end, {ok, FoldAccInit}, Keys), - finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount) + finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount) end). -make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag) -> - fun(Req2, _Etag, _Acc) -> - JsonResp = couch_query_servers:render_reduce_head(QueryServer, - Req2, Db), - JsonResp2 = apply_etag(JsonResp, CurrentEtag), - #extern_resp_args{ - code = Code, - data = BeginBody, - ctype = CType, - headers = ExtHeaders - } = couch_httpd_external:parse_external_response(JsonResp2), - JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), - {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), - {ok, Resp, binary_to_list(BeginBody)} +make_map_start_resp_fun(QueryServer, Db) -> + fun(Req, Etag, TotalRows, Offset, _Acc) -> + Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}]}, + start_list_resp(QueryServer, Req, Db, Head, Etag) end. -make_reduce_send_row_fun(QueryServer, Req, Db) -> - fun(Resp, {Key, Value}, RowFront) -> - try - JsonResp = couch_query_servers:render_reduce_row(QueryServer, - Req, Db, {Key, Value}), - #extern_resp_args{ - stop = StopIter, - data = RowBody - } = couch_httpd_external:parse_external_response(JsonResp), - case StopIter of - true -> {stop, ""}; - _ -> - Chunk = RowFront ++ binary_to_list(RowBody), - case Chunk of - [] -> ok; - _ -> send_chunk(Resp, Chunk) - end, - {ok, ""} - end - catch - throw:Error -> - send_chunked_error(Resp, Error), - throw({already_sent, Resp, Error}) +make_reduce_start_resp_fun(QueryServer, _Req, Db, _CurrentEtag) -> + fun(Req2, Etag, _Acc) -> + start_list_resp(QueryServer, Req2, Db, {[]}, Etag) + end. + +start_list_resp(QueryServer, Req, Db, Head, Etag) -> + [<<"start">>,Chunks,JsonResp] = couch_query_servers:render_list_head(QueryServer, + Req, Db, Head), + JsonResp2 = apply_etag(JsonResp, Etag), + #extern_resp_args{ + code = Code, + ctype = CType, + headers = ExtHeaders + } = couch_httpd_external:parse_external_response(JsonResp2), + JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), + {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), + {ok, Resp, ?b2l(?l2b(Chunks))}. + +make_map_send_row_fun(QueryServer) -> + fun(Resp, Db, Row, _IncludeDocs, RowFront) -> + send_list_row(Resp, QueryServer, Db, Row, RowFront) + end. + +make_reduce_send_row_fun(QueryServer, Db) -> + fun(Resp, Row, RowFront) -> + send_list_row(Resp, QueryServer, Db, Row, RowFront) + end. + +send_list_row(Resp, QueryServer, Db, Row, RowFront) -> + try + [Go,Chunks] = couch_query_servers:render_list_row(QueryServer, Db, Row), + Chunk = RowFront ++ ?b2l(?l2b(Chunks)), + send_non_empty_chunk(Resp, Chunk), + case Go of + <<"chunks">> -> + {ok, ""}; + <<"end">> -> + {stop, stop} end + catch + throw:Error -> + send_chunked_error(Resp, Error), + throw({already_sent, Resp, Error}) + end. + +send_non_empty_chunk(Resp, Chunk) -> + case Chunk of + [] -> ok; + _ -> send_chunk(Resp, Chunk) end. output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) -> @@ -291,7 +265,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q CurrentEtag = couch_httpd_view:view_group_etag(Group, {Lang, ListSrc, Accept}), couch_httpd:etag_respond(Req, CurrentEtag, fun() -> StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag), - SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db), + SendListRowFun = make_reduce_send_row_fun(QueryServer, Db), {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, @@ -303,7 +277,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q {ok, FoldResult} = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId}, {EndKey, EndDocId}, GroupRowsFun, RespFun, FoldAccInit), - finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null) + finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null) end); output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) -> @@ -325,7 +299,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q couch_httpd:etag_respond(Req, CurrentEtag, fun() -> StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag), - SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db), + SendListRowFun = make_reduce_send_row_fun(QueryServer, Db), {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, @@ -339,33 +313,30 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q couch_view:fold_reduce(View, Dir, {Key, StartDocId}, {Key, EndDocId}, GroupRowsFun, RespFun, FoldAcc) end, {ok, FoldAccInit}, Keys), - finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null) + finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null) end). -finish_list(Req, Db, QueryServer, Etag, FoldResult, StartListRespFun, TotalRows) -> - {Resp, BeginBody} = case FoldResult of +finish_list(Req, QueryServer, Etag, FoldResult, StartFun, TotalRows) -> + case FoldResult of {_, _, undefined, _} -> - {ok, Resp2, BeginBody2} = render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows), - {Resp2, BeginBody2}; - {_, _, 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) + {ok, Resp, BeginBody} = + render_head_for_empty_list(StartFun, Req, Etag, TotalRows), + [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer), + Chunk = BeginBody ++ ?b2l(?l2b(Chunks)), + send_non_empty_chunk(Resp, Chunk); + {_, _, Resp, stop} -> + ok; + {_, _, Resp, _} -> + [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer), + send_non_empty_chunk(Resp, ?b2l(?l2b(Chunks))) end, send_chunk(Resp, []). render_head_for_empty_list(StartListRespFun, Req, Etag, null) -> - StartListRespFun(Req, Etag, []); + StartListRespFun(Req, Etag, []); % for reduce 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), @@ -373,7 +344,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db Accept = proplists:get_value('Accept', Hlist), CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept}), couch_httpd:etag_respond(Req, CurrentEtag, fun() -> - ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, + [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc, DocId, nil, Req, Db), JsonResp = apply_etag(ExternalResp, CurrentEtag), couch_httpd_external:send_external_response(Req, JsonResp) @@ -387,7 +358,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=Revs}=Doc, #httpd{mochi_r CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, Revs, Accept}), % We know our etag now couch_httpd:etag_respond(Req, CurrentEtag, fun() -> - ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, + [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc, DocId, Doc, Req, Db), JsonResp = apply_etag(ExternalResp, CurrentEtag), couch_httpd_external:send_external_response(Req, JsonResp) -- cgit v1.2.3