couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j..@apache.org
Subject [couchdb] 06/07: Implement /db/_bulk_get endpoint
Date Wed, 01 Nov 2017 07:03:02 GMT
This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch 1.x.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit ab3c51ebb1fde2a773df8afd1b3f46755cfda472
Author: Alexander Shorin <kxepal@apache.org>
AuthorDate: Sat Dec 12 21:32:31 2015 +0300

    Implement /db/_bulk_get endpoint
    
    COUCHDB-2310
---
 share/doc/src/whatsnew/1.7.rst |   1 +
 src/couchdb/couch_httpd_db.erl | 194 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 191 insertions(+), 4 deletions(-)

diff --git a/share/doc/src/whatsnew/1.7.rst b/share/doc/src/whatsnew/1.7.rst
index 51e2ec1..75bdca7 100644
--- a/share/doc/src/whatsnew/1.7.rst
+++ b/share/doc/src/whatsnew/1.7.rst
@@ -30,6 +30,7 @@ API Changes
 
 - :issue:`1356`: Return username on :http:post:`/_session`.
 - :issue:`1876`: Fix duplicated Content-Type for show/update functions.
+- :issue:`2310`: Implement :http:post:`/{db}/_bulk_get`.
 - :issue:`2375`: :statuscode:`400` returned when invalid revision specified.
 - :issue:`2845`: :statuscode:`400` returned when `revs` is not a list.
 
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 9f77a80..8f8ba7f 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -355,6 +355,36 @@ db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>]}=Req,
Db) ->
 db_req(#httpd{path_parts=[_,<<"_bulk_docs">>]}=Req, _Db) ->
     send_method_not_allowed(Req, "POST");
 
+
+db_req(#httpd{method='POST', path_parts=[_, <<"_bulk_get">>]}=Req, Db) ->
+    couch_stats_collector:increment({httpd, bulk_requests}),
+    couch_httpd:validate_ctype(Req, "application/json"),
+    {JsonProps} = couch_httpd:json_body_obj(Req),
+    case couch_util:get_value(<<"docs">>, JsonProps) of
+        undefined ->
+            throw({bad_request, <<"Missing JSON list of 'docs'.">>});
+        Docs ->
+            #doc_query_args{
+                options = Options
+            } = bulk_get_parse_doc_query(Req),
+
+            {ok, Resp} = start_json_response(Req, 200),
+            send_chunk(Resp, <<"{\"results\": [">>),
+
+            lists:foldl(fun(Doc, Sep) ->
+                {DocId, Results, Options1} = bulk_get_open_doc_revs(Db, Doc,
+                                                                    Options),
+                bulk_get_send_docs_json(Resp, DocId, Results, Options1, Sep),
+                <<",">>
+            end, <<"">>, Docs),
+
+            send_chunk(Resp, <<"]}">>),
+            end_json_response(Resp)
+    end;
+db_req(#httpd{path_parts=[_, <<"_bulk_get">>]}=Req, _Db) ->
+    send_method_not_allowed(Req, "POST");
+
+
 db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) ->
     couch_httpd:validate_ctype(Req, "application/json"),
     {IdsRevs} = couch_httpd:json_body_obj(Req),
@@ -1067,8 +1097,10 @@ get_md5_header(Req) ->
     end.
 
 parse_doc_query(Req) ->
-    lists:foldl(fun({Key,Value}, Args) ->
-        case {Key, Value} of
+    lists:foldl(fun parse_doc_query/2, #doc_query_args{}, couch_httpd:qs(Req)).
+
+parse_doc_query({Key, Value}, Args) ->
+    case {Key, Value} of
         {"attachments", "true"} ->
             Options = [attachments | Args#doc_query_args.options],
             Args#doc_query_args{options=Options};
@@ -1112,8 +1144,7 @@ parse_doc_query(Req) ->
             Args#doc_query_args{options=Options};
         _Else -> % unknown key value pair, ignore.
             Args
-        end
-    end, #doc_query_args{}, couch_httpd:qs(Req)).
+    end.
 
 parse_changes_query(Req, Db) ->
     ChangesArgs = lists:foldl(fun({Key, Value}, Args) ->
@@ -1226,3 +1257,158 @@ validate_attachment_name(Name) ->
         false -> throw({bad_request, <<"Attachment name is not UTF-8 encoded">>})
     end.
 
+
+%% /db/_bulk_get stuff
+
+bulk_get_parse_doc_query(Req) ->
+    lists:foldl(fun({Key, Value}, Args) ->
+        ok = validate_query_param(Key),
+        parse_doc_query({Key, Value}, Args)
+    end, #doc_query_args{}, couch_httpd:qs(Req)).
+
+
+validate_query_param("open_revs"=Key) ->
+    throw_bad_query_param(Key);
+validate_query_param("new_edits"=Key) ->
+    throw_bad_query_param(Key);
+validate_query_param("w"=Key) ->
+    throw_bad_query_param(Key);
+validate_query_param("rev"=Key) ->
+    throw_bad_query_param(Key);
+validate_query_param("atts_since"=Key) ->
+    throw_bad_query_param(Key);
+validate_query_param(_) ->
+    ok.
+
+throw_bad_query_param(Key) when is_list(Key) ->
+    throw_bad_query_param(?l2b(Key));
+throw_bad_query_param(Key) when is_binary(Key) ->
+    Msg = <<"\"", Key/binary, "\" query parameter is not acceptable">>,
+    throw({bad_request, Msg}).
+
+
+bulk_get_open_doc_revs(Db, {Props}, Options) ->
+    bulk_get_open_doc_revs1(Db, Props, Options, {}).
+
+
+bulk_get_open_doc_revs1(Db, Props, Options, {}) ->
+    case parse_field(<<"id">>, couch_util:get_value(<<"id">>, Props))
of
+        {error, {DocId, Error, Reason}} ->
+            {DocId, {error, {null, Error, Reason}}, Options};
+
+        {ok, undefined} ->
+            Error = {null, bad_request, <<"document id missed">>},
+            {null, {error, Error}, Options};
+
+        {ok, DocId} ->
+            bulk_get_open_doc_revs1(Db, Props, Options, {DocId})
+    end;
+bulk_get_open_doc_revs1(Db, Props, Options, {DocId}) ->
+    RevStr = couch_util:get_value(<<"rev">>, Props),
+
+    case parse_field(<<"rev">>, RevStr) of
+        {error, {RevStr, Error, Reason}} ->
+            {DocId, {error, {RevStr, Error, Reason}}, Options};
+
+        {ok, undefined} ->
+            bulk_get_open_doc_revs1(Db, Props, Options, {DocId, all});
+
+        {ok, Rev} ->
+            bulk_get_open_doc_revs1(Db, Props, Options, {DocId, [Rev]})
+    end;
+bulk_get_open_doc_revs1(Db, Props, Options, {DocId, Revs}) ->
+    AttsSinceStr = couch_util:get_value(<<"atts_since">>, Props),
+
+    case parse_field(<<"atts_since">>, AttsSinceStr) of
+        {error, {BadAttsSinceRev, Error, Reason}} ->
+            {DocId, {error, {BadAttsSinceRev, Error, Reason}}, Options};
+
+        {ok, []} ->
+            bulk_get_open_doc_revs1(Db, Props, Options, {DocId, Revs, Options});
+
+        {ok, RevList} ->
+            Options1 = [{atts_since, RevList}, attachments | Options],
+            bulk_get_open_doc_revs1(Db, Props, Options, {DocId, Revs, Options1})
+    end;
+bulk_get_open_doc_revs1(Db, Props, _, {DocId, Revs, Options}) ->
+    case couch_db:open_doc_revs(Db, DocId, Revs, Options) of
+        {ok, []} ->
+            RevStr = couch_util:get_value(<<"rev">>, Props),
+            Error = {RevStr, <<"not_found">>, <<"missing">>},
+            {DocId, {error, Error}, Options};
+        Results ->
+            {DocId, Results, Options}
+    end.
+
+
+parse_field(<<"id">>, undefined) ->
+    {ok, undefined};
+parse_field(<<"id">>, Value) ->
+    try
+        ok = couch_doc:validate_docid(Value),
+        {ok, Value}
+    catch
+        throw:{Error, Reason} ->
+            {error, {Value, Error, Reason}}
+    end;
+parse_field(<<"rev">>, undefined) ->
+    {ok, undefined};
+parse_field(<<"rev">>, Value) ->
+    try
+        Rev = couch_doc:parse_rev(Value),
+        {ok, Rev}
+    catch
+        throw:{bad_request=Error, Reason} ->
+            {error, {Value, Error, Reason}}
+    end;
+parse_field(<<"atts_since">>, undefined) ->
+    {ok, []};
+parse_field(<<"atts_since">>, []) ->
+    {ok, []};
+parse_field(<<"atts_since">>, Value) when is_list(Value) ->
+    parse_atts_since(Value, []);
+parse_field(<<"atts_since">>, Value) ->
+    {error, {Value, bad_request, <<"att_since value must be array of revs">>}}.
+
+
+parse_atts_since([], Acc) ->
+    {ok, lists:reverse(Acc)};
+parse_atts_since([RevStr | Rest], Acc) ->
+    case parse_field(<<"rev">>, RevStr) of
+        {ok, Rev} ->
+            parse_atts_since(Rest, [Rev | Acc]);
+        {error, _}=Error ->
+            Error
+    end.
+
+
+bulk_get_send_docs_json(Resp, DocId, Results, Options, Sep) ->
+    Id = ?JSON_ENCODE(DocId),
+    send_chunk(Resp, [Sep, <<"{\"id\": ">>, Id, <<", \"docs\": [">>]),
+    bulk_get_send_docs_json1(Resp, DocId, Results, Options),
+    send_chunk(Resp, <<"]}">>).
+
+bulk_get_send_docs_json1(Resp, DocId, {error, {Rev, Error, Reason}}, _) ->
+    send_chunk(Resp, [bulk_get_json_error(DocId, Rev, Error, Reason)]);
+bulk_get_send_docs_json1(_Resp, _DocId, {ok, []}, _) ->
+    ok;
+bulk_get_send_docs_json1(Resp, DocId, {ok, Docs}, Options) ->
+    lists:foldl(fun(Result, AccSeparator) ->
+        case Result of
+            {ok, Doc} ->
+                JsonDoc = couch_doc:to_json_obj(Doc, Options),
+                Json = ?JSON_ENCODE({[{ok, JsonDoc}]}),
+                send_chunk(Resp, [AccSeparator, Json]);
+            {{Error, Reason}, RevId} ->
+                RevStr = couch_doc:rev_to_str(RevId),
+                Json = bulk_get_json_error(DocId, RevStr, Error, Reason),
+                send_chunk(Resp, [AccSeparator, Json])
+        end,
+        <<",">>
+    end, <<"">>, Docs).
+
+bulk_get_json_error(DocId, Rev, Error, Reason) ->
+    ?JSON_ENCODE({[{error, {[{<<"id">>, DocId},
+                             {<<"rev">>, Rev},
+                             {<<"error">>, Error},
+                             {<<"reason">>, Reason}]}}]}).

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

Mime
View raw message