summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjiangphcn <jiangph@cn.ibm.com>2018-03-15 15:17:59 +0800
committerjiangphcn <jiangph@cn.ibm.com>2018-03-15 15:17:59 +0800
commit43a05664e9bdc809a50b85af6e7e8ad815eee7ea (patch)
tree8c8a306964a5a0a5d31fe7e0cc91b459d7100a79
parent2c43e6201e1c67418ed2dc945ce04945e764da0a (diff)
downloadcouchdb-issue-822-queries-test.tar.gz
issue-822
-rw-r--r--src/chttpd/src/chttpd_db.erl38
-rw-r--r--src/chttpd/src/chttpd_view.erl16
-rw-r--r--src/chttpd/test/chttpd_db_test.erl197
3 files changed, 249 insertions, 2 deletions
diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 2c3ec6356..38c0325cf 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -524,6 +524,21 @@ db_req(#httpd{method='GET',path_parts=[_,OP]}=Req, Db) when ?IS_ALL_DOCS(OP) ->
throw({bad_request, "`keys` parameter must be an array."})
end;
+db_req(#httpd{method='POST',
+ path_parts=[_, OP, <<"queries">>]}=Req, Db) when ?IS_ALL_DOCS(OP) ->
+ 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, OP, Queries);
+ undefined ->
+ throw({bad_request,
+ <<"POST body must include `queries` parameter.">>})
+ end;
+
+db_req(#httpd{path_parts=[_, _OP, <<"queries">>]}=Req,
+ _Db) when ?IS_ALL_DOCS(_OP) ->
+ send_method_not_allowed(Req, "POST");
+
db_req(#httpd{method='POST',path_parts=[_,OP]}=Req, Db) when ?IS_ALL_DOCS(OP) ->
chttpd:validate_ctype(Req, "application/json"),
{Fields} = chttpd:json_body_obj(Req),
@@ -642,6 +657,29 @@ 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/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl
index 0aaa849a5..a31e68bf2 100644
--- a/src/chttpd/src/chttpd_view.erl
+++ b/src/chttpd/src/chttpd_view.erl
@@ -49,6 +49,22 @@ design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
fun couch_mrview_http:view_cb/2, VAcc, Args),
{ok, Resp#vacc.resp}.
+handle_view_req(#httpd{method='POST',
+ path_parts=[_, _, _, _, ViewName, <<"queries">>]}=Req, Db, DDoc) ->
+ chttpd:validate_ctype(Req, "application/json"),
+ Props = couch_httpd:json_body_obj(Req),
+ case couch_mrview_util:get_view_queries(Props) of
+ Queries when Queries =/= undefined ->
+ multi_query_view(Req, Db, DDoc, ViewName, Queries);
+ undefined ->
+ throw({bad_request,
+ <<"POST body must include `queries` parameter.">>})
+ end;
+
+handle_view_req(#httpd{path_parts=[_, _, _, _, _, <<"queries">>]}=Req,
+ _Db, _DDoc) ->
+ chttpd:send_method_not_allowed(Req, "POST");
+
handle_view_req(#httpd{method='GET',
path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
couch_stats:increment_counter([couchdb, httpd, view_reads]),
diff --git a/src/chttpd/test/chttpd_db_test.erl b/src/chttpd/test/chttpd_db_test.erl
index bed0f9eb1..bfd11a3ff 100644
--- a/src/chttpd/test/chttpd_db_test.erl
+++ b/src/chttpd/test/chttpd_db_test.erl
@@ -21,7 +21,8 @@
-define(CONTENT_JSON, {"Content-Type", "application/json"}).
-define(DESTHEADER1, {"Destination", "foo%E5%95%8Abar"}).
-define(DESTHEADER2, {"Destination", "foo%2Fbar%23baz%3Fpow%3Afiz"}).
-
+-define(DDOC, "{\"_id\": \"_design/bar\", \"views\": {\"baz\":
+ {\"map\": \"function(doc) {emit(doc._id, doc._id);}\"}}}").
-define(FIXTURE_TXT, ?ABS_PATH(?FILE)).
-define(i2l(I), integer_to_list(I)).
@@ -71,7 +72,18 @@ all_test_() ->
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_return_correct_id_on_doc_copy/1,
- fun should_return_400_for_bad_engine/1
+ fun should_return_400_for_bad_engine/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_succeed_on_design_docs_with_queries_keys/1,
+ fun should_succeed_on_design_docs_with_queries_limit_skip/1,
+ fun should_succeed_on_design_docs_with_multiple_queries/1,
+ fun should_succeed_on_local_docs_with_queries_keys/1,
+ fun should_succeed_on_local_docs_with_multiple_queries/1,
+ fun should_succeed_on_view_with_queries_keys/1,
+ fun should_succeed_on_view_with_queries_limit_skip/1,
+ fun should_succeed_on_view_with_multiple_queries/1
]
}
}
@@ -265,3 +277,184 @@ should_return_400_for_bad_engine(_) ->
{ok, Status, _, _} = test_request:put(Url, [?CONTENT_JSON, ?AUTH], "{}"),
?assertEqual(400, Status)
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/queries/",
+ [?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/queries/",
+ [?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/queries/",
+ [?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_succeed_on_design_docs_with_queries_keys(Url) ->
+ ?_test(begin
+ [create_doc(Url, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = "{\"queries\": [{\"keys\": [ \"_design/ddoc3\",
+ \"_design/ddoc8\"]}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++
+ "/_design_docs/queries/", [?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_design_docs_with_queries_limit_skip(Url) ->
+ ?_test(begin
+ [create_doc(Url, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = "{\"queries\": [{\"limit\": 5, \"skip\": 2}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++
+ "/_design_docs/queries/", [?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_design_docs_with_multiple_queries(Url) ->
+ ?_test(begin
+ [create_doc(Url, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = "{\"queries\": [{\"keys\": [ \"_design/ddoc3\",
+ \"_design/ddoc8\"]}, {\"limit\": 5, \"skip\": 2}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++
+ "/_design_docs/queries/", [?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_succeed_on_local_docs_with_queries_keys(Url) ->
+ ?_test(begin
+ [create_doc(Url, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = "{\"queries\": [{\"keys\":
+ [ \"_local/doc3\", \"_local/doc8\"]}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++ "/_local_docs/queries/",
+ [?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_local_docs_with_multiple_queries(Url) ->
+ ?_test(begin
+ [create_doc(Url, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = "{\"queries\":[
+ {\"keys\": [\"_local/doc3\"]},
+ {\"keys\": [ \"_local/doc6\", \"_local/ddoc9\"]}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++
+ "/_local_docs/queries/", [?CONTENT_JSON, ?AUTH], QueryDoc),
+ ?assertEqual(200, RC),
+ {ResultJson} = ?JSON_DECODE(RespBody),
+ ResultJsonBody = couch_util:get_value(<<"results">>, ResultJson),
+ {InnerJson1} = lists:nth(1, ResultJsonBody),
+ ?assertEqual(1, length(couch_util:get_value(<<"rows">>, InnerJson1))),
+ {InnerJson2} = lists:nth(2, ResultJsonBody),
+ ?assertEqual(2, length(couch_util:get_value(<<"rows">>, InnerJson2)))
+ end).
+
+
+should_succeed_on_view_with_queries_keys(Url) ->
+ ?_test(begin
+ [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ {ok, _, _, _} = test_request:put(Url ++ "/_design/bar",
+ [?CONTENT_JSON, ?AUTH], ?DDOC),
+ QueryDoc = "{\"queries\": [{\"keys\": [ \"testdoc3\",
+ \"testdoc8\"]}]}",
+ {ok, _, _, RespBody} = test_request:post(Url ++ "/_design/bar/"
+ ++ "_view/baz/queries/", [?CONTENT_JSON, ?AUTH], QueryDoc),
+ {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_view_with_queries_limit_skip(Url) ->
+ ?_test(begin
+ [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ {ok, _, _, _} = test_request:put(Url ++ "/_design/bar",
+ [?CONTENT_JSON, ?AUTH], ?DDOC),
+ QueryDoc = "{\"queries\": [{\"limit\": 5, \"skip\": 2}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++ "/_design/bar/"
+ ++ "_view/baz/queries/", [?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_view_with_multiple_queries(Url) ->
+ ?_test(begin
+ [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ {ok, _, _, _} = test_request:put(Url ++ "/_design/bar",
+ [?CONTENT_JSON, ?AUTH], ?DDOC),
+ QueryDoc = "{\"queries\": [{\"keys\": [ \"testdoc3\",
+ \"testdoc8\"]}, {\"limit\": 5, \"skip\": 2}]}",
+ {ok, RC, _, RespBody} = test_request:post(Url ++ "/_design/bar/"
+ ++ "_view/baz/queries/", [?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).