summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_httpd_show.erl
diff options
context:
space:
mode:
authorJohn Christopher Anderson <jchris@apache.org>2009-12-22 18:03:44 +0000
committerJohn Christopher Anderson <jchris@apache.org>2009-12-22 18:03:44 +0000
commitea3b1153e52ac1513da4d634eedefb05c261039c (patch)
tree858c5b3d81509bfe784b8d2d1252921cbf34aa54 /src/couchdb/couch_httpd_show.erl
parent22c551bb103072826c0299265670d1483c753dde (diff)
move query server to a design-doc based protocol, closes COUCHDB-589
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@893249 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/couchdb/couch_httpd_show.erl')
-rw-r--r--src/couchdb/couch_httpd_show.erl474
1 files changed, 199 insertions, 275 deletions
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index 5c95070a..467c0a42 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -12,8 +12,8 @@
-module(couch_httpd_show).
--export([handle_doc_show_req/2, handle_doc_update_req/2, handle_view_list_req/2,
- handle_doc_show/5, handle_view_list/7]).
+-export([handle_doc_show_req/3, handle_doc_update_req/3, handle_view_list_req/3,
+ handle_doc_show/5, handle_view_list/6, get_fun_key/3]).
-include("couch_db.hrl").
@@ -22,217 +22,245 @@
start_json_response/2,send_chunk/2,last_chunk/1,send_chunked_error/2,
start_chunked_response/3, send_error/4]).
+% /db/_design/foo/show/bar/docid
+% show converts a json doc to a response of any content-type.
+% it looks up the doc an then passes it to the query server.
+% then it sends the response from the query server to the http client.
handle_doc_show_req(#httpd{
- method='GET',
- path_parts=[_DbName, _Design, DesignName, _Show, ShowName, DocId]
- }=Req, Db) ->
- handle_doc_show(Req, DesignName, ShowName, DocId, Db);
+ path_parts=[_, _, _, _, ShowName, DocId]
+ }=Req, Db, DDoc) ->
+ % open the doc
+ Doc = couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]),
+ % we don't handle revs here b/c they are an internal api
+ % returns 404 if there is no doc with DocId
+ handle_doc_show(Req, Db, DDoc, ShowName, Doc);
handle_doc_show_req(#httpd{
- path_parts=[_DbName, _Design, DesignName, _Show, ShowName]
- }=Req, Db) ->
- handle_doc_show(Req, DesignName, ShowName, nil, Db);
+ path_parts=[_, _, _, _, ShowName]
+ }=Req, Db, DDoc) ->
+ % with no docid the doc is nil
+ handle_doc_show(Req, Db, DDoc, ShowName, nil);
-handle_doc_show_req(#httpd{method='GET'}=Req, _Db) ->
- send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>);
+handle_doc_show_req(Req, _Db, _DDoc) ->
+ send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>).
+
+handle_doc_show(Req, Db, DDoc, ShowName, Doc) ->
+ % get responder for ddoc/showname
+ CurrentEtag = show_etag(Req, Doc, DDoc, []),
+ couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
+ JsonReq = couch_httpd_external:json_req_obj(Req, Db),
+ JsonDoc = couch_query_servers:json_doc(Doc),
+ [<<"resp">>, ExternalResp] =
+ couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName], [JsonDoc, JsonReq]),
+ JsonResp = apply_etag(ExternalResp, CurrentEtag),
+ couch_httpd_external:send_external_response(Req, JsonResp)
+ end).
-handle_doc_show_req(Req, _Db) ->
- send_method_not_allowed(Req, "GET,POST,HEAD").
-handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db) ->
- send_method_not_allowed(Req, "POST,PUT,DELETE,ETC");
+show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) ->
+ Accept = couch_httpd:header_value(Req, "Accept"),
+ DocPart = case Doc of
+ nil -> nil;
+ Doc -> couch_httpd:doc_etag(Doc)
+ end,
+ couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept, UserCtx#user_ctx.roles, More}).
+
+get_fun_key(DDoc, Type, Name) ->
+ #doc{body={Props}} = DDoc,
+ Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
+ Src = couch_util:get_nested_json_value({Props}, [Type, Name]),
+ {Lang, Src}.
+
+% /db/_design/foo/update/bar/docid
+% updates a doc based on a request
+% handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) ->
+% % anything but GET
+% send_method_not_allowed(Req, "POST,PUT,DELETE,ETC");
handle_doc_update_req(#httpd{
- path_parts=[_DbName, _Design, DesignName, _Update, UpdateName, DocId]
- }=Req, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- UpdateSrc = couch_util:get_nested_json_value({Props}, [<<"updates">>, UpdateName]),
+ path_parts=[_, _, _, _, UpdateName, DocId]
+ }=Req, Db, DDoc) ->
Doc = try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts])
- catch
- _ -> nil
- end,
- send_doc_update_response(Lang, UpdateSrc, DocId, Doc, Req, Db);
+ catch
+ _ -> nil
+ end,
+ send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId);
handle_doc_update_req(#httpd{
- path_parts=[_DbName, _Design, DesignName, _Update, UpdateName]
- }=Req, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- UpdateSrc = couch_util:get_nested_json_value({Props}, [<<"updates">>, UpdateName]),
- send_doc_update_response(Lang, UpdateSrc, nil, nil, Req, Db);
+ path_parts=[_, _, _, _, UpdateName]
+ }=Req, Db, DDoc) ->
+ send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null);
-handle_doc_update_req(Req, _Db) ->
+handle_doc_update_req(Req, _Db, _DDoc) ->
send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>).
-
-
-handle_doc_show(Req, DesignName, ShowName, DocId, Db) ->
- DesignId = <<"_design/", DesignName/binary>>,
- #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []),
- Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>),
- ShowSrc = couch_util:get_nested_json_value({Props}, [<<"shows">>, ShowName]),
- Doc = case DocId of
- nil -> nil;
- _ ->
- try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts])
- catch
- _ -> nil
- end
+send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) ->
+ JsonReq = couch_httpd_external:json_req_obj(Req, Db, DocId),
+ JsonDoc = couch_query_servers:json_doc(Doc),
+ case couch_query_servers:ddoc_prompt(DDoc, [<<"updates">>, UpdateName], [JsonDoc, JsonReq]) of
+ [<<"up">>, {NewJsonDoc}, JsonResp] ->
+ Options = case couch_httpd:header_value(Req, "X-Couch-Full-Commit", "false") of
+ "true" ->
+ [full_commit];
+ _ ->
+ []
+ end,
+ NewDoc = couch_doc:from_json_obj({NewJsonDoc}),
+ Code = 201,
+ {ok, _NewRev} = couch_db:update_doc(Db, NewDoc, Options);
+ [<<"up">>, _Other, JsonResp] ->
+ Code = 200,
+ ok
end,
- send_doc_show_response(Lang, ShowSrc, DocId, Doc, Req, Db).
+ JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp),
+ % todo set location field
+ couch_httpd_external:send_external_response(Req, JsonResp2).
+
% view-list request with view and list from same design doc.
handle_view_list_req(#httpd{method='GET',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
- handle_view_list(Req, DesignName, ListName, DesignName, ViewName, Db, nil);
+ path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) ->
+ handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, nil);
% view-list request with view and list from different design docs.
handle_view_list_req(#httpd{method='GET',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewDesignName, ViewName]}=Req, Db) ->
- handle_view_list(Req, DesignName, ListName, ViewDesignName, ViewName, Db, nil);
+ path_parts=[_, _, _, _, ListName, ViewDesignName, ViewName]}=Req, Db, DDoc) ->
+ handle_view_list(Req, Db, DDoc, ListName, {ViewDesignName, ViewName}, nil);
-handle_view_list_req(#httpd{method='GET'}=Req, _Db) ->
+handle_view_list_req(#httpd{method='GET'}=Req, _Db, _DDoc) ->
send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
handle_view_list_req(#httpd{method='POST',
- path_parts=[_DbName, _Design, DesignName, _List, ListName, ViewName]}=Req, Db) ->
+ path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) ->
+ % {Props2} = couch_httpd:json_body(Req),
ReqBody = couch_httpd:body(Req),
{Props2} = ?JSON_DECODE(ReqBody),
Keys = proplists:get_value(<<"keys">>, Props2, nil),
- handle_view_list(Req#httpd{req_body=ReqBody}, DesignName, ListName, DesignName, ViewName, Db, Keys);
-
-handle_view_list_req(Req, _Db) ->
- send_method_not_allowed(Req, "GET,POST,HEAD").
+ handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, {DesignName, ViewName}, Keys);
-handle_view_list(Req, ListDesignName, ListName, ViewDesignName, ViewName, Db, Keys) ->
- ListDesignId = <<"_design/", ListDesignName/binary>>,
- #doc{body={ListProps}} = couch_httpd_db:couch_doc_open(Db, ListDesignId, nil, []),
- if
- ViewDesignName == ListDesignName ->
- ViewDesignId = ListDesignId;
- true ->
- ViewDesignId = <<"_design/", ViewDesignName/binary>>
- end,
+handle_view_list_req(#httpd{method='POST',
+ path_parts=[_, _, _, _, ListName, ViewDesignName, ViewName]}=Req, Db, DDoc) ->
+ % {Props2} = couch_httpd:json_body(Req),
+ ReqBody = couch_httpd:body(Req),
+ {Props2} = ?JSON_DECODE(ReqBody),
+ Keys = proplists:get_value(<<"keys">>, Props2, nil),
+ handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, {ViewDesignName, ViewName}, Keys);
- ListLang = proplists:get_value(<<"language">>, ListProps, <<"javascript">>),
- ListSrc = couch_util:get_nested_json_value({ListProps}, [<<"lists">>, ListName]),
- send_view_list_response(ListLang, ListSrc, ViewName, ViewDesignId, Req, Db, Keys).
-
-
-send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys) ->
- Stale = couch_httpd_view:get_stale_type(Req),
- Reduce = couch_httpd_view:get_reduce_type(Req),
- case couch_view:get_map_view(Db, DesignId, ViewName, Stale) of
- {ok, View, Group} ->
- QueryArgs = couch_httpd_view:parse_view_params(Req, Keys, map),
- output_map_list(Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys);
- {not_found, _Reason} ->
- case couch_view:get_reduce_view(Db, DesignId, ViewName, Stale) of
- {ok, ReduceView, Group} ->
- case Reduce of
- false ->
- QueryArgs = couch_httpd_view:parse_view_params(
- Req, Keys, map_red
- ),
- MapView = couch_view:extract_map_view(ReduceView),
- output_map_list(Req, Lang, ListSrc, MapView, Group, Db, QueryArgs, Keys);
- _ ->
- QueryArgs = couch_httpd_view:parse_view_params(
- Req, Keys, reduce
- ),
- output_reduce_list(Req, Lang, ListSrc, ReduceView, Group, Db, QueryArgs, Keys)
- end;
- {not_found, Reason} ->
- throw({not_found, Reason})
- end
- end.
+handle_view_list_req(#httpd{method='POST'}=Req, _Db, _DDoc) ->
+ send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>);
+handle_view_list_req(Req, _Db, _DDoc) ->
+ send_method_not_allowed(Req, "GET,POST,HEAD").
-output_map_list(#httpd{mochi_req=MReq, user_ctx=UserCtx}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) ->
+handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) ->
+ ViewDesignId = <<"_design/", ViewDesignName/binary>>,
+ {ViewType, View, Group, QueryArgs} = couch_httpd_view:load_view(Req, Db, {ViewDesignId, ViewName}, Keys),
+ Etag = list_etag(Req, Db, Group, {couch_httpd:doc_etag(DDoc), Keys}),
+ couch_httpd:etag_respond(Req, Etag, fun() ->
+ output_list(ViewType, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys)
+ end).
+
+list_etag(#httpd{user_ctx=UserCtx}=Req, Db, Group, More) ->
+ Accept = couch_httpd:header_value(Req, "Accept"),
+ couch_httpd_view:view_group_etag(Group, Db, {More, Accept, UserCtx#user_ctx.roles}).
+
+output_list(map, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->
+ output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys);
+output_list(reduce, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->
+ output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys).
+
+% next step:
+% use with_ddoc_proc/2 to make this simpler
+output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->
#view_query_args{
limit = Limit,
skip = SkipCount
} = QueryArgs,
+
+ FoldAccInit = {Limit, SkipCount, undefined, []},
{ok, RowCount} = couch_view:get_row_count(View),
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd_view:view_group_etag(Group, Db, {Lang, ListSrc, Accept, UserCtx}),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- % get the os process here
- % 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, Db),
- SendListRowFun = make_map_send_row_fun(QueryServer),
+ couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) ->
+
+ ListFoldHelpers = #view_fold_helper_funs{
+ reduce_count = fun couch_view:reduce_to_count/1,
+ start_response = StartListRespFun = make_map_start_resp_fun(QServer, Db, LName),
+ send_row = make_map_send_row_fun(QServer)
+ },
+
+ {ok, _, FoldResult} = case Keys of
+ nil ->
+ FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, Etag, Db, RowCount, ListFoldHelpers),
+ couch_view:fold(View, FoldlFun, FoldAccInit,
+ couch_httpd_view:make_key_options(QueryArgs));
+ Keys ->
+ lists:foldl(
+ fun(Key, {ok, _, FoldAcc}) ->
+ QueryArgs2 = QueryArgs#view_query_args{
+ start_key = Key,
+ end_key = Key
+ },
+ FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs2, Etag, Db, RowCount, ListFoldHelpers),
+ couch_view:fold(View, FoldlFun, FoldAcc,
+ couch_httpd_view:make_key_options(QueryArgs2))
+ end, {ok, nil, FoldAccInit}, Keys)
+ end,
+ finish_list(Req, QServer, Etag, FoldResult, StartListRespFun, RowCount)
+ end).
- FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount,
- #view_fold_helper_funs{
- reduce_count = fun couch_view:reduce_to_count/1,
- start_response = StartListRespFun,
- send_row = SendListRowFun
- }),
- FoldAccInit = {Limit, SkipCount, undefined, []},
- {ok, _, FoldResult} = couch_view:fold(View, FoldlFun, FoldAccInit,
- couch_httpd_view:make_key_options(QueryArgs)),
- finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
- end);
-output_map_list(#httpd{mochi_req=MReq, user_ctx=UserCtx}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) ->
+output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->
#view_query_args{
limit = Limit,
- skip = SkipCount
+ skip = SkipCount,
+ group_level = GroupLevel
} = QueryArgs,
- {ok, RowCount} = couch_view:get_row_count(View),
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd_view:view_group_etag(Group, Db, {Lang, ListSrc, Accept, UserCtx, Keys}),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- % get the os process here
- % 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, Db),
- SendListRowFun = make_map_send_row_fun(QueryServer),
+ couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) ->
+ StartListRespFun = make_reduce_start_resp_fun(QServer, Db, LName),
+ SendListRowFun = make_reduce_send_row_fun(QServer, Db),
+ {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
+ GroupLevel, QueryArgs, Etag,
+ #reduce_fold_helper_funs{
+ start_response = StartListRespFun,
+ send_row = SendListRowFun
+ }),
FoldAccInit = {Limit, SkipCount, undefined, []},
- {ok, _, FoldResult} = lists:foldl(
- fun(Key, {ok, _, FoldAcc}) ->
- QueryArgs2 = QueryArgs#view_query_args{
- start_key = Key,
- end_key = Key
- },
- FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs2, CurrentEtag, Db, RowCount,
- #view_fold_helper_funs{
- reduce_count = fun couch_view:reduce_to_count/1,
- start_response = StartListRespFun,
- send_row = SendListRowFun
- }),
- couch_view:fold(View, FoldlFun, FoldAcc,
- couch_httpd_view:make_key_options(QueryArgs2))
- end, {ok, nil, FoldAccInit}, Keys),
- finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
+ {ok, FoldResult} = case Keys of
+ nil ->
+ couch_view:fold_reduce(View, RespFun, FoldAccInit, [{key_group_fun, GroupRowsFun} |
+ couch_httpd_view:make_key_options(QueryArgs)]);
+ Keys ->
+ lists:foldl(
+ fun(Key, {ok, FoldAcc}) ->
+ couch_view:fold_reduce(View, RespFun, FoldAcc,
+ [{key_group_fun, GroupRowsFun} |
+ couch_httpd_view:make_key_options(
+ QueryArgs#view_query_args{start_key=Key, end_key=Key})]
+ )
+ end, {ok, FoldAccInit}, Keys)
+ end,
+ finish_list(Req, QServer, Etag, FoldResult, StartListRespFun, null)
end).
-make_map_start_resp_fun(QueryServer, Db) ->
+
+make_map_start_resp_fun(QueryServer, Db, LName) ->
fun(Req, Etag, TotalRows, Offset, _Acc) ->
Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}]},
- start_list_resp(QueryServer, Req, Db, Head, Etag)
+ start_list_resp(QueryServer, LName, Req, Db, Head, Etag)
end.
-make_reduce_start_resp_fun(QueryServer, _Req, Db, _CurrentEtag) ->
+make_reduce_start_resp_fun(QueryServer, Db, LName) ->
fun(Req2, Etag, _Acc) ->
- start_list_resp(QueryServer, Req2, Db, {[]}, Etag)
+ start_list_resp(QueryServer, LName, 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),
+start_list_resp(QServer, LName, Req, Db, Head, Etag) ->
+ JsonReq = couch_httpd_external:json_req_obj(Req, Db),
+ [<<"start">>,Chunks,JsonResp] = couch_query_servers:ddoc_proc_prompt(QServer,
+ [<<"lists">>, LName], [Head, JsonReq]),
JsonResp2 = apply_etag(JsonResp, Etag),
#extern_resp_args{
code = Code,
@@ -255,7 +283,7 @@ make_reduce_send_row_fun(QueryServer, Db) ->
send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDoc) ->
try
- [Go,Chunks] = couch_query_servers:render_list_row(QueryServer, Db, Row, IncludeDoc),
+ [Go,Chunks] = prompt_list_row(QueryServer, Db, Row, IncludeDoc),
Chunk = RowFront ++ ?b2l(?l2b(Chunks)),
send_non_empty_chunk(Resp, Chunk),
case Go of
@@ -270,78 +298,22 @@ send_list_row(Resp, QueryServer, Db, Row, RowFront, IncludeDoc) ->
throw({already_sent, Resp, Error})
end.
+
+prompt_list_row({Proc, _DDocId}, Db, {{Key, DocId}, Value}, IncludeDoc) ->
+ JsonRow = couch_httpd_view:view_row_obj(Db, {{Key, DocId}, Value}, IncludeDoc),
+ couch_query_servers:proc_prompt(Proc, [<<"list_row">>, JsonRow]);
+
+prompt_list_row({Proc, _DDocId}, _, {Key, Value}, _IncludeDoc) ->
+ JsonRow = {[{key, Key}, {value, Value}]},
+ couch_query_servers:proc_prompt(Proc, [<<"list_row">>, JsonRow]).
+
send_non_empty_chunk(Resp, Chunk) ->
case Chunk of
[] -> ok;
_ -> send_chunk(Resp, Chunk)
end.
-output_reduce_list(#httpd{mochi_req=MReq, user_ctx=UserCtx}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) ->
- #view_query_args{
- limit = Limit,
- skip = SkipCount,
- group_level = GroupLevel
- } = QueryArgs,
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd_view:view_group_etag(Group, Db, {Lang, ListSrc, Accept, UserCtx}),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- % get the os process here
- % pass it into the view fold with closures
- {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
- StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
- SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
-
- {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
- GroupLevel, QueryArgs, CurrentEtag,
- #reduce_fold_helper_funs{
- start_response = StartListRespFun,
- send_row = SendListRowFun
- }),
- FoldAccInit = {Limit, SkipCount, undefined, []},
- {ok, FoldResult} = couch_view:fold_reduce(View, RespFun, FoldAccInit,
- [{key_group_fun, GroupRowsFun} |
- couch_httpd_view:make_key_options(QueryArgs)]),
- finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
- end);
-
-output_reduce_list(#httpd{mochi_req=MReq, user_ctx=UserCtx}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) ->
- #view_query_args{
- limit = Limit,
- skip = SkipCount,
- group_level = GroupLevel
- } = QueryArgs,
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd_view:view_group_etag(Group, Db, {Lang, ListSrc, Accept, UserCtx, Keys}),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- % get the os process here
- % pass it into the view fold with closures
- {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
- StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
- SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
-
- {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
- GroupLevel, QueryArgs, CurrentEtag,
- #reduce_fold_helper_funs{
- start_response = StartListRespFun,
- send_row = SendListRowFun
- }),
- FoldAccInit = {Limit, SkipCount, undefined, []},
- {ok, FoldResult} = lists:foldl(
- fun(Key, {ok, FoldAcc}) ->
- couch_view:fold_reduce(View, RespFun, FoldAcc,
- [{key_group_fun, GroupRowsFun} |
- couch_httpd_view:make_key_options(
- QueryArgs#view_query_args{start_key=Key, end_key=Key})]
- )
- end, {ok, FoldAccInit}, Keys),
- finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
- end).
-
-finish_list(Req, QueryServer, Etag, FoldResult, StartFun, TotalRows) ->
+finish_list(Req, {Proc, _DDocId}, Etag, FoldResult, StartFun, TotalRows) ->
FoldResult2 = case FoldResult of
{Limit, SkipCount, Response, RowAcc} ->
{Limit, SkipCount, Response, RowAcc, nil};
@@ -352,16 +324,15 @@ finish_list(Req, QueryServer, Etag, FoldResult, StartFun, TotalRows) ->
{_, _, undefined, _, _} ->
{ok, Resp, BeginBody} =
render_head_for_empty_list(StartFun, Req, Etag, TotalRows),
- [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+ [<<"end">>, Chunks] = couch_query_servers:proc_prompt(Proc, [<<"list_end">>]),
Chunk = BeginBody ++ ?b2l(?l2b(Chunks)),
send_non_empty_chunk(Resp, Chunk);
{_, _, Resp, stop, _} ->
ok;
{_, _, Resp, _, _} ->
- [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+ [<<"end">>, Chunks] = couch_query_servers:proc_prompt(Proc, [<<"list_end">>]),
send_non_empty_chunk(Resp, ?b2l(?l2b(Chunks)))
end,
- couch_query_servers:stop_doc_map(QueryServer),
last_chunk(Resp).
@@ -370,53 +341,6 @@ render_head_for_empty_list(StartListRespFun, Req, Etag, null) ->
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, user_ctx=UserCtx}=Req, Db) ->
- % compute etag with no doc
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept, UserCtx}),
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- [<<"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)
- end);
-
-send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=Revs}=Doc, #httpd{mochi_req=MReq, user_ctx=UserCtx}=Req, Db) ->
- % calculate the etag
- Headers = MReq:get(headers),
- Hlist = mochiweb_headers:to_list(Headers),
- Accept = proplists:get_value('Accept', Hlist),
- CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, Revs, Accept, UserCtx}),
- % We know our etag now
- couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- [<<"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)
- end).
-
-send_doc_update_response(Lang, UpdateSrc, DocId, Doc, Req, Db) ->
- case couch_query_servers:render_doc_update(Lang, UpdateSrc,
- DocId, Doc, Req, Db) of
- [<<"up">>, {NewJsonDoc}, JsonResp] ->
- Options = case couch_httpd:header_value(Req, "X-Couch-Full-Commit", "false") of
- "true" ->
- [full_commit];
- _ ->
- []
- end,
- NewDoc = couch_doc:from_json_obj({NewJsonDoc}),
- Code = 201,
- % todo set location field
- {ok, _NewRev} = couch_db:update_doc(Db, NewDoc, Options);
- [<<"up">>, _Other, JsonResp] ->
- Code = 200,
- ok
- end,
- JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp),
- couch_httpd_external:send_external_response(Req, JsonResp2).
% Maybe this is in the proplists API
% todo move to couch_util