diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl index de5c79c66e0..d1dd026b198 100644 --- a/src/chttpd/src/chttpd_db.erl +++ b/src/chttpd/src/chttpd_db.erl @@ -533,6 +533,18 @@ db_req(#httpd{method='POST',path_parts=[_,OP]}=Req, Db) when ?IS_ALL_DOCS(OP) -> db_req(#httpd{path_parts=[_,OP]}=Req, _Db) when ?IS_ALL_DOCS(OP) -> send_method_not_allowed(Req, "GET,HEAD,POST"); +db_req(#httpd{method='POST', path_parts=[_, <<"_all_docs_view_query">>]}=Req, Db) -> + Props = chttpd:json_body_obj(Req), + case couch_mrview_util:get_view_queries(Props) of + Queries when Queries =/= undefined -> + multi_all_docs_view(Req, Db, <<"_all_docs">>, Queries); + undefined -> + throw({bad_request, <<"POST body must include `queries` parameter.">>}) + end; + +db_req(#httpd{path_parts=[_, <<"_all_docs_view_quiery">>]}=Req, _Db) -> + send_method_not_allowed(Req, "POST"); + db_req(#httpd{method='POST',path_parts=[_,<<"_missing_revs">>]}=Req, Db) -> chttpd:validate_ctype(Req, "application/json"), {JsonDocIdRevs} = chttpd:json_body_obj(Req), @@ -636,6 +648,28 @@ db_req(#httpd{path_parts=[_, DocId]}=Req, Db) -> db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) -> db_attachment_req(Req, Db, DocId, FileNameParts). +multi_all_docs_view(Req, Db, OP, Queries) -> + Args0 = couch_mrview_http:parse_params(Req, undefined), + Args1 = Args0#mrargs{view_type=map}, + ArgQueries = lists:map(fun({Query}) -> + QueryArg1 = couch_mrview_http:parse_params(Query, undefined, + Args1, [decoded]), + QueryArgs2 = couch_mrview_util:validate_args(QueryArg1), + set_namespace(OP, QueryArgs2) + end, Queries), + Options = [{user_ctx, Req#httpd.user_ctx}], + VAcc0 = #vacc{db=Db, req=Req, prepend="\r\n"}, + FirstChunk = "{\"results\":[", + {ok, Resp0} = chttpd:start_delayed_json_response(VAcc0#vacc.req, 200, [], FirstChunk), + VAcc1 = VAcc0#vacc{resp=Resp0}, + VAcc2 = lists:foldl(fun(Args, Acc0) -> + {ok, Acc1} = fabric:all_docs(Db, Options, + fun couch_mrview_http:view_cb/2, Acc0, Args), + Acc1 + end, VAcc1, ArgQueries), + {ok, Resp1} = chttpd:send_delayed_chunk(VAcc2#vacc.resp, "\r\n]}"), + chttpd:end_delayed_json_response(Resp1). + all_docs_view(Req, Db, Keys, OP) -> Args0 = couch_mrview_http:parse_params(Req, Keys), Args1 = Args0#mrargs{view_type=map}, diff --git a/src/chttpd/test/chttpd_db_test.erl b/src/chttpd/test/chttpd_db_test.erl index 2071ca5025e..08d9eaf0d88 100644 --- a/src/chttpd/test/chttpd_db_test.erl +++ b/src/chttpd/test/chttpd_db_test.erl @@ -70,6 +70,9 @@ all_test_() -> fun should_return_409_for_put_att_nonexistent_rev/1, fun should_return_update_seq_when_set_on_all_docs/1, fun should_not_return_update_seq_when_unset_on_all_docs/1, + fun should_succeed_on_all_docs_with_queries_keys/1, + fun should_succeed_on_all_docs_with_queries_limit_skip/1, + fun should_succeed_on_all_docs_with_multiple_queries/1, fun should_return_correct_id_on_doc_copy/1 ] } @@ -222,6 +225,52 @@ should_not_return_update_seq_when_unset_on_all_docs(Url) -> couch_util:get_value(<<"offset">>, ResultJson)) end). +should_succeed_on_all_docs_with_queries_keys(Url) -> + ?_test(begin + [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)], + QueryDoc = "{\"queries\": [{\"keys\": [ \"testdoc3\", \"testdoc8\"]}]}", + {ok, RC, _, RespBody} = test_request:post(Url ++ "/_all_docs_view_query/", + [?CONTENT_JSON, ?AUTH], QueryDoc), + ?assertEqual(200, RC), + {ResultJson} = ?JSON_DECODE(RespBody), + ResultJsonBody = couch_util:get_value(<<"results">>, ResultJson), + {InnerJson} = lists:nth(1, ResultJsonBody), + ?assertEqual(2, length(couch_util:get_value(<<"rows">>, InnerJson))) + end). + + +should_succeed_on_all_docs_with_queries_limit_skip(Url) -> + ?_test(begin + [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)], + QueryDoc = "{\"queries\": [{\"limit\": 5, \"skip\": 2}]}", + {ok, RC, _, RespBody} = test_request:post(Url ++ "/_all_docs_view_query/", + [?CONTENT_JSON, ?AUTH], QueryDoc), + ?assertEqual(200, RC), + {ResultJson} = ?JSON_DECODE(RespBody), + ResultJsonBody = couch_util:get_value(<<"results">>, ResultJson), + {InnerJson} = lists:nth(1, ResultJsonBody), + ?assertEqual(2, couch_util:get_value(<<"offset">>, InnerJson)), + ?assertEqual(5, length(couch_util:get_value(<<"rows">>, InnerJson))) + end). + + +should_succeed_on_all_docs_with_multiple_queries(Url) -> + ?_test(begin + [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)], + QueryDoc = "{\"queries\": [{\"keys\": [ \"testdoc3\", \"testdoc8\"]}, + {\"limit\": 5, \"skip\": 2}]}", + {ok, RC, _, RespBody} = test_request:post(Url ++ "/_all_docs_view_query/", + [?CONTENT_JSON, ?AUTH], QueryDoc), + ?assertEqual(200, RC), + {ResultJson} = ?JSON_DECODE(RespBody), + ResultJsonBody = couch_util:get_value(<<"results">>, ResultJson), + {InnerJson1} = lists:nth(1, ResultJsonBody), + ?assertEqual(2, length(couch_util:get_value(<<"rows">>, InnerJson1))), + {InnerJson2} = lists:nth(2, ResultJsonBody), + ?assertEqual(2, couch_util:get_value(<<"offset">>, InnerJson2)), + ?assertEqual(5, length(couch_util:get_value(<<"rows">>, InnerJson2))) + end). + should_return_correct_id_on_doc_copy(Url) -> ?_test(begin