You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by wo...@apache.org on 2018/01/18 01:27:44 UTC

[couchdb] branch master updated: Add support for queries in /{db}/_all_docs POST Fixes #820

This is an automated email from the ASF dual-hosted git repository.

wohali pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 0fd9509  Add support for queries in /{db}/_all_docs POST Fixes #820
0fd9509 is described below

commit 0fd950905a76f3eaab685fc361bd3f38471761d5
Author: jiangphcn <ji...@cn.ibm.com>
AuthorDate: Thu Nov 30 17:52:08 2017 +0800

    Add support for queries in /{db}/_all_docs POST
    Fixes #820
---
 src/chttpd/src/chttpd_db.erl               | 42 ++++++++++++---
 src/chttpd/test/chttpd_db_test.erl         | 87 +++++++++++++++++++++++++++++-
 src/couch_mrview/src/couch_mrview_util.erl |  4 +-
 test/javascript/tests/basics.js            |  2 +-
 test/javascript/tests/view_errors.js       |  2 +-
 5 files changed, 124 insertions(+), 13 deletions(-)

diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index dbbb454..e621d65 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -520,14 +520,18 @@ db_req(#httpd{method='GET',path_parts=[_,OP]}=Req, Db) when ?IS_ALL_DOCS(OP) ->
 
 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),
-    case couch_util:get_value(<<"keys">>, Fields, nil) of
-    Keys when is_list(Keys) ->
-        all_docs_view(Req, Db, Keys, OP);
-    nil ->
-        all_docs_view(Req, Db, undefined, OP);
-    _ ->
-        throw({bad_request, "`keys` body member must be an array."})
+    Props = chttpd:json_body_obj(Req),
+    Keys = couch_mrview_util:get_view_keys(Props),
+    Queries = couch_mrview_util:get_view_queries(Props),
+    case {Queries, Keys} of
+        {Queries, undefined} when is_list(Queries) ->
+            multi_all_docs_view(Req, Db, OP, Queries);
+        {undefined, Keys} when is_list(Keys) ->
+            all_docs_view(Req, Db, Keys, OP);
+        {undefined, undefined} ->
+            all_docs_view(Req, Db, undefined, OP);
+        {_, _} ->
+            throw({bad_request, "`keys` and `queries` are mutually exclusive"})
     end;
 
 db_req(#httpd{path_parts=[_,OP]}=Req, _Db) when ?IS_ALL_DOCS(OP) ->
@@ -636,6 +640,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 a83e33a..f673293 100644
--- a/src/chttpd/test/chttpd_db_test.erl
+++ b/src/chttpd/test/chttpd_db_test.erl
@@ -65,7 +65,12 @@ all_test_() ->
                     fun should_return_200_for_del_att_with_rev/1,
                     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_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_succeed_on_design_docs_with_multiple_queries/1,
+                    fun should_fail_on_multiple_queries_with_keys_and_queries/1
                 ]
             }
         }
@@ -218,6 +223,86 @@ should_not_return_update_seq_when_unset_on_all_docs(Url) ->
     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/",
+            [?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/",
+            [?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/",
+            [?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_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/",
+            [?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_fail_on_multiple_queries_with_keys_and_queries(Url) ->
+    ?_test(begin
+        [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+        QueryDoc = "{\"queries\": [{\"keys\": [ \"testdoc3\", \"testdoc8\"]}],
+            \"keys\": [ \"testdoc4\", \"testdoc9\"]}",
+        {ok, RC, _, RespBody} = test_request:post(Url ++ "/_all_docs/",
+            [?CONTENT_JSON, ?AUTH], QueryDoc),
+        ?assertEqual(400, RC),
+        ?assertMatch({[
+            {<<"error">>,<<"bad_request">>},
+            {<<"reason">>,<<"`keys` and `queries` are mutually exclusive">>}]},
+            ?JSON_DECODE(RespBody))
+    end).
+
+
 attachment_doc() ->
     {ok, Data} = file:read_file(?FIXTURE_TXT),
     {[
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index d26df94..bc6686b 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -1161,7 +1161,7 @@ get_view_keys({Props}) ->
         Keys when is_list(Keys) ->
             Keys;
         _ ->
-            throw({bad_request, "`keys` member must be a array."})
+            throw({bad_request, "`keys` member must be an array."})
     end.
 
 
@@ -1172,7 +1172,7 @@ get_view_queries({Props}) ->
         Queries when is_list(Queries) ->
             Queries;
         _ ->
-            throw({bad_request, "`queries` member must be a array."})
+            throw({bad_request, "`queries` member must be an array."})
     end.
 
 
diff --git a/test/javascript/tests/basics.js b/test/javascript/tests/basics.js
index a36b303..7959951 100644
--- a/test/javascript/tests/basics.js
+++ b/test/javascript/tests/basics.js
@@ -268,7 +268,7 @@ couchTests.basics = function(debug) {
   T(xhr.status == 400);
   result = JSON.parse(xhr.responseText);
   T(result.error == "bad_request");
-  T(result.reason == "`keys` body member must be an array.");
+  T(result.reason == "`keys` member must be an array.");
 
   // oops, the doc id got lost in code nirwana
   xhr = CouchDB.request("DELETE", "/" + db_name + "/?rev=foobarbaz");
diff --git a/test/javascript/tests/view_errors.js b/test/javascript/tests/view_errors.js
index dd60292..f135b74 100644
--- a/test/javascript/tests/view_errors.js
+++ b/test/javascript/tests/view_errors.js
@@ -169,7 +169,7 @@ couchTests.view_errors = function(debug) {
       T(xhr.status == 400);
       result = JSON.parse(xhr.responseText);
       T(result.error == "bad_request");
-      T(result.reason == "`keys` member must be a array.");
+      T(result.reason == "`keys` member must be an array.");
 
       // if the reduce grows to fast, throw an overflow error
       var path = "/" + db_name + "/_design/testbig/_view/reduce_too_big";

-- 
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].