You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ro...@apache.org on 2023/07/23 12:43:05 UTC
[couchdb] 07/13: Fix: `_design_docs/queries` with `keys` should only return design docs
This is an automated email from the ASF dual-hosted git repository.
ronny pushed a commit to branch nouveau4win
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit dc27d4d92ce67d4f8083aafa5837620717e5e001
Author: jiahuili <li...@gmail.com>
AuthorDate: Wed Jul 19 12:24:56 2023 -0500
Fix: `_design_docs/queries` with `keys` should only return design docs
Querying design/local docs with `keys` should only return design/local docs.
Fixed: https://github.com/apache/couchdb/issues/2851
---
src/chttpd/test/eunit/chttpd_db_test.erl | 939 +++++++++++--------------------
src/fabric/src/fabric_view_all_docs.erl | 44 +-
2 files changed, 354 insertions(+), 629 deletions(-)
diff --git a/src/chttpd/test/eunit/chttpd_db_test.erl b/src/chttpd/test/eunit/chttpd_db_test.erl
index d6b980c07..6560cc949 100644
--- a/src/chttpd/test/eunit/chttpd_db_test.erl
+++ b/src/chttpd/test/eunit/chttpd_db_test.erl
@@ -29,31 +29,14 @@
setup() ->
Hashed = couch_passwords:hash_admin_password(?PASS),
ok = config:set("admins", ?USER, ?b2l(Hashed), _Persist = false),
- TmpDb = ?tempdb(),
- Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
- Port = mochiweb_socket_server:get(chttpd, port),
- Url = lists:concat(["http://", Addr, ":", Port, "/", ?b2l(TmpDb)]),
- create_db(Url),
- Url.
+ Db = ?tempdb(),
+ create_db(Db),
+ Db.
-teardown(Url) ->
- delete_db(Url),
+teardown(Db) ->
+ delete_db(Db),
ok = config:delete("admins", ?USER, _Persist = false).
-create_db(Url) ->
- {ok, Status, _, _} = test_request:put(Url, [?CONTENT_JSON, ?AUTH], "{}"),
- ?assert(Status =:= 201 orelse Status =:= 202).
-
-create_doc(Url, Id) ->
- test_request:put(
- Url ++ "/" ++ Id,
- [?CONTENT_JSON, ?AUTH],
- "{\"mr\": \"rockoartischocko\"}"
- ).
-
-delete_db(Url) ->
- {ok, 200, _, _} = test_request:delete(Url, [?AUTH]).
-
all_test_() ->
{
"chttpd db tests",
@@ -66,609 +49,327 @@ all_test_() ->
fun setup/0,
fun teardown/1,
[
- fun should_return_ok_true_on_bulk_update/1,
- fun should_return_201_new_edits_false_with_revs_on_bulk_update/1,
- fun should_return_400_new_edits_false_no_revs_on_bulk_update/1,
- fun should_return_201_new_edits_false_with_rev_on_doc_update/1,
- fun should_return_201_new_edits_false_with_revisions_on_doc_update/1,
- fun should_return_400_new_edits_false_no_revs_on_doc_update/1,
- fun should_return_ok_true_on_ensure_full_commit/1,
- fun should_return_404_for_ensure_full_commit_on_no_db/1,
- fun should_accept_live_as_an_alias_for_continuous/1,
- fun should_return_headers_after_starting_continious/1,
- fun should_return_404_for_delete_att_on_notadoc/1,
- fun should_return_409_for_del_att_without_rev/1,
- 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_return_correct_id_on_doc_copy/1,
- fun should_return_only_one_ok_on_doc_copy/1,
- fun should_return_400_for_bad_engine/1,
- fun should_not_change_db_proper_after_rewriting_shardmap/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_queries_limit_skip/1,
- fun should_succeed_on_local_docs_with_multiple_queries/1
+ ?TDEF_FE(t_return_ok_true_on_bulk_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_201_new_edits_false_with_revs_on_bulk_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_400_new_edits_false_no_revs_on_bulk_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_201_new_edits_false_with_rev_on_doc_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_201_new_edits_false_with_revisions_on_doc_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_400_new_edits_false_no_revs_on_doc_update, ?TIMEOUT),
+ ?TDEF_FE(t_return_ok_true_on_ensure_full_commit, ?TIMEOUT),
+ ?TDEF_FE(t_return_404_for_ensure_full_commit_on_no_db, ?TIMEOUT),
+ ?TDEF_FE(t_accept_live_as_an_alias_for_continuous, ?TIMEOUT),
+ ?TDEF_FE(t_return_headers_after_starting_continuous, ?TIMEOUT),
+ ?TDEF_FE(t_return_404_for_delete_att_on_notadoc, ?TIMEOUT),
+ ?TDEF_FE(t_return_409_for_del_att_without_rev, ?TIMEOUT),
+ ?TDEF_FE(t_return_200_for_del_att_with_rev, ?TIMEOUT),
+ ?TDEF_FE(t_return_409_for_put_att_nonexistent_rev, ?TIMEOUT),
+ ?TDEF_FE(t_return_update_seq_when_set_on_all_docs, ?TIMEOUT),
+ ?TDEF_FE(t_not_return_update_seq_when_unset_on_all_docs, ?TIMEOUT),
+ ?TDEF_FE(t_return_correct_id_on_doc_copy, ?TIMEOUT),
+ ?TDEF_FE(t_return_only_one_ok_on_doc_copy, ?TIMEOUT),
+ ?TDEF_FE(t_return_400_for_bad_engine, ?TIMEOUT),
+ ?TDEF_FE(t_not_change_db_proper_after_rewriting_shardmap, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_all_docs_with_queries_keys, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_all_docs_with_queries_limit_skip, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_all_docs_with_multiple_queries, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_design_docs_with_queries_keys, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_design_docs_with_queries_limit_skip, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_design_docs_with_multiple_queries, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_local_docs_with_queries_keys, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_local_docs_with_queries_limit_skip, ?TIMEOUT),
+ ?TDEF_FE(t_succeed_on_local_docs_with_multiple_queries, ?TIMEOUT)
]
}
}
}.
-should_return_ok_true_on_bulk_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_assertEqual(
- true,
- begin
- {ok, _, _, Body} = create_doc(Url, "testdoc"),
- {Json} = ?JSON_DECODE(Body),
- Ref = couch_util:get_value(<<"rev">>, Json, undefined),
- NewDoc = "{\"docs\": [{\"_rev\": \"" ++ ?b2l(Ref) ++ "\", \"_id\": \"testdoc\"}]}",
- {ok, _, _, ResultBody} = test_request:post(
- Url ++ "/_bulk_docs/",
- [?CONTENT_JSON, ?AUTH],
- NewDoc
- ),
- ResultJson = ?JSON_DECODE(ResultBody),
- {InnerJson} = lists:nth(1, ResultJson),
- couch_util:get_value(<<"ok">>, InnerJson, undefined)
- end
- )}.
-
-should_return_201_new_edits_false_with_revs_on_bulk_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(
- begin
- {ok, _, _, Body} = create_doc(Url, "dochasrev"),
- {Json} = ?JSON_DECODE(Body),
- Ref = couch_util:get_value(<<"rev">>, Json, undefined),
- NewDoc =
- "{\"docs\": [{\"_rev\": \"" ++ ?b2l(Ref) ++
- "\", \"_id\": \"dochasrev\"}], \"new_edits\": false}",
- {ok, Status, _, ResultBody} = test_request:post(
- Url ++
- "/_bulk_docs/",
- [?CONTENT_JSON, ?AUTH],
- NewDoc
- ),
- ?assertEqual(201, Status),
- ?assertEqual([], ?JSON_DECODE(ResultBody))
- end
- )}.
-
-should_return_400_new_edits_false_no_revs_on_bulk_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(
- begin
- {ok, _, _, _} = create_doc(Url, "docnorev"),
- NewDoc =
- "{\"docs\": [{\"_id\": \"docnorev\"}], " ++
- "\"new_edits\": false}",
- {ok, Status, _, ResultBody} = test_request:post(
- Url ++
- "/_bulk_docs/",
- [?CONTENT_JSON, ?AUTH],
- NewDoc
- ),
- {ResultJson} = ?JSON_DECODE(ResultBody),
- ?assertEqual(400, Status),
- ?assertEqual(
- <<"bad_request">>,
- couch_util:get_value(<<"error">>, ResultJson)
- )
- end
- )}.
-
-should_return_201_new_edits_false_with_rev_on_doc_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(
- begin
- Url1 = Url ++ "/docrev2?new_edits=false",
- Headers = [?CONTENT_JSON, ?AUTH],
- Body = "{\"foo\": \"bar\", \"_rev\": \"1-abc\"}",
- {ok, Status, _, ResultBody} = test_request:put(Url1, Headers, Body),
- {Props} = ?JSON_DECODE(ResultBody),
- ?assertEqual(201, Status),
- Id = couch_util:get_value(<<"id">>, Props),
- Rev = couch_util:get_value(<<"rev">>, Props),
- ?assertEqual(<<"docrev2">>, Id),
- ?assertEqual(<<"1-abc">>, Rev)
- end
- )}.
-
-should_return_201_new_edits_false_with_revisions_on_doc_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(
- begin
- Url1 = Url ++ "/docrev3?new_edits=false",
- Headers = [?CONTENT_JSON, ?AUTH],
- Body =
- "{\"foo\": \"bar\", " ++
- "\"_revisions\": {" ++
- "\"ids\": [\"abc\", \"def\"], " ++
- "\"start\": 2}} ",
- {ok, Status, _, ResultBody} = test_request:put(Url1, Headers, Body),
- {Props} = ?JSON_DECODE(ResultBody),
- Id = couch_util:get_value(<<"id">>, Props),
- Rev = couch_util:get_value(<<"rev">>, Props),
- ?assertEqual(201, Status),
- ?assertEqual(<<"docrev3">>, Id),
- ?assertEqual(<<"2-abc">>, Rev)
- end
- )}.
-
-should_return_400_new_edits_false_no_revs_on_doc_update(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(
- begin
- Url1 = Url ++ "/docnorev4?new_edits=false",
- Headers = [?CONTENT_JSON, ?AUTH],
- Body = "{\"foo\": \"bar\"}",
- {ok, Status, _, ResultBody} = test_request:put(Url1, Headers, Body),
- {Props} = ?JSON_DECODE(ResultBody),
- ?assertEqual(400, Status),
- Error = couch_util:get_value(<<"error">>, Props),
- ?assertEqual(<<"bad_request">>, Error)
+t_return_ok_true_on_bulk_update(Db) ->
+ {_, #{<<"rev">> := Rev}} = create_doc(Db, "testdoc"),
+ Body = #{<<"docs">> => [#{<<"_id">> => <<"testdoc">>, <<"_rev">> => Rev}]},
+ {_, [#{<<"ok">> := Res}]} = req(post, url(Db, "_bulk_docs"), Body),
+ ?assert(Res).
+
+t_return_201_new_edits_false_with_revs_on_bulk_update(Db) ->
+ {_, #{<<"rev">> := Rev}} = create_doc(Db, "dochasrev"),
+ Body = #{
+ <<"docs">> => [#{<<"_id">> => <<"dochasrev">>, <<"_rev">> => Rev}],
+ <<"new_edits">> => false
+ },
+ {Status, Response} = req(post, url(Db, "_bulk_docs"), Body),
+ ?assertEqual(201, Status),
+ ?assertEqual([], Response).
+
+t_return_400_new_edits_false_no_revs_on_bulk_update(Db) ->
+ create_doc(Db, "docnorev"),
+ NewDoc = #{<<"docs">> => [#{<<"_id">> => <<"docnorev">>}], <<"new_edits">> => false},
+ {Status, Response} = req(post, url(Db, "_bulk_docs"), NewDoc),
+ ?assertEqual(400, Status),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Response).
+
+t_return_201_new_edits_false_with_rev_on_doc_update(Db) ->
+ Body = #{<<"foo">> => <<"bar">>, <<"_rev">> => <<"1-abc">>},
+ {Status, #{<<"id">> := Id, <<"rev">> := Rev}} =
+ req(put, url(Db, "docrev2?new_edits=false"), Body),
+ ?assertEqual(201, Status),
+ ?assertEqual(<<"docrev2">>, Id),
+ ?assertEqual(<<"1-abc">>, Rev).
+
+t_return_201_new_edits_false_with_revisions_on_doc_update(Db) ->
+ Body = #{
+ <<"foo">> => <<"bar">>,
+ <<"_revisions">> => #{<<"ids">> => [<<"abc">>, <<"def">>], <<"start">> => 2}
+ },
+ {Status, #{<<"id">> := Id, <<"rev">> := Rev}} =
+ req(put, url(Db, "docrev3?new_edits=false"), Body),
+ ?assertEqual(201, Status),
+ ?assertEqual(<<"docrev3">>, Id),
+ ?assertEqual(<<"2-abc">>, Rev).
+
+t_return_400_new_edits_false_no_revs_on_doc_update(Db) ->
+ Body = #{<<"foo">> => <<"bar">>},
+ {Status, Response} = req(put, url(Db, "docrev3?new_edits=false"), Body),
+ ?assertEqual(400, Status),
+ ?assertMatch(#{<<"error">> := <<"bad_request">>}, Response).
+
+t_return_ok_true_on_ensure_full_commit(Db) ->
+ {Status, #{<<"ok">> := Res}} = req(post, url(Db, "_ensure_full_commit")),
+ ?assertEqual(201, Status),
+ ?assert(Res).
+
+t_return_404_for_ensure_full_commit_on_no_db(Db) ->
+ {Status, Response} = req(post, url(Db) ++ "-missing-db" ++ "/_ensure_full_commit"),
+ ?assertEqual(404, Status),
+ ?assertMatch(#{<<"error">> := <<"not_found">>}, Response).
+
+t_accept_live_as_an_alias_for_continuous(Db) ->
+ GetLastSeq =
+ fun(Chunks) ->
+ LastSeqBin = lists:last(Chunks),
+ {Result} =
+ try ?JSON_DECODE(LastSeqBin) of
+ Data -> Data
+ catch
+ _:_ ->
+ % should not happen, abort
+ ?assert(false)
+ end,
+ couch_util:get_value(<<"last_seq">>, Result, undefined)
+ end,
+ LastSeq1 = GetLastSeq(wait_non_empty_chunk(Db)),
+ create_doc(Db, "testdoc2"),
+ LastSeq2 = GetLastSeq(wait_non_empty_chunk(Db)),
+ ?assertNotEqual(LastSeq1, LastSeq2).
+
+t_return_headers_after_starting_continuous(Db) ->
+ {ok, _, _, Bin} = test_request:get(url(Db, "_changes?feed=live&timeout=1"), [?AUTH]),
+ Parts = binary:split(Bin, <<"\n">>, [global]),
+ %% we should receive at least one part even when timeout=1
+ ?assertNotEqual([], Parts).
+
+t_return_404_for_delete_att_on_notadoc(Db) ->
+ {Status, Response} = req(delete, url(Db, "notadoc/att.pdf")),
+ ?assertEqual(404, Status),
+ ?assertMatch(#{<<"error">> := <<"not_found">>, <<"reason">> := <<"missing">>}, Response),
+ {Status1, Response} = req(get, url(Db, "notadoc")),
+ ?assertEqual(404, Status1).
+
+t_return_409_for_del_att_without_rev(Db) ->
+ {Status, _} = req(put, url(Db, "testdoc3"), attachment_doc()),
+ ?assertEqual(201, Status),
+ {Status1, _} = req(delete, url(Db, "testdoc3/file.erl")),
+ ?assertEqual(409, Status1).
+
+t_return_200_for_del_att_with_rev(Db) ->
+ {Status, #{<<"rev">> := Rev}} = req(put, url(Db, "testdoc4"), attachment_doc()),
+ ?assertEqual(201, Status),
+ {Status1, _} = req(delete, url(Db, "testdoc4/file.erl?rev=" ++ Rev)),
+ ?assertEqual(200, Status1).
+
+t_return_409_for_put_att_nonexistent_rev(Db) ->
+ {Status, Response} = req(put, url(Db, "t_return_404/file.erl?rev=1-000"), attachment_doc()),
+ ?assertEqual(409, Status),
+ ?assertMatch(#{<<"error">> := <<"not_found">>, <<"reason">> := <<"missing_rev">>}, Response).
+
+t_return_update_seq_when_set_on_all_docs(Db) ->
+ [create_doc(Db, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 3)],
+ {Status, #{<<"update_seq">> := UpdateSeq, <<"offset">> := Offset}} =
+ req(get, url(Db, "_all_docs?update_seq=true&keys=[\"testdoc1\"]")),
+ ?assertEqual(200, Status),
+ ?assertNotEqual(undefined, UpdateSeq),
+ ?assertNotEqual(undefined, Offset).
+
+t_not_return_update_seq_when_unset_on_all_docs(Db) ->
+ [create_doc(Db, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 3)],
+ {Status, #{<<"offset">> := Offset} = Response} =
+ req(get, url(Db, "_all_docs?update_seq=false&keys=[\"testdoc1\"]")),
+ ?assertEqual(200, Status),
+ ?assertNot(maps:is_key(<<"update_seq">>, Response)),
+ ?assertNotEqual(undefined, Offset).
+
+t_return_correct_id_on_doc_copy(Db) ->
+ create_doc(Db, "testdoc"),
+ {_, #{<<"id">> := Id1}} = req(copy, url(Db, "testdoc"), [?CONTENT_JSON, ?AUTH, ?DESTHEADER1]),
+ {_, #{<<"id">> := Id2}} = req(copy, url(Db, "testdoc"), [?CONTENT_JSON, ?AUTH, ?DESTHEADER2]),
+ ?assertEqual(<<102, 111, 111, 229, 149, 138, 98, 97, 114>>, Id1),
+ ?assertEqual(<<"foo/bar#baz?pow:fiz">>, Id2).
+
+t_return_only_one_ok_on_doc_copy(Db) ->
+ create_doc(Db, "testdoc"),
+ {_, #{<<"ok">> := Res}} = req(copy, url(Db, "testdoc"), [?CONTENT_JSON, ?AUTH, ?DESTHEADER1]),
+ ?assert(Res).
+
+t_return_400_for_bad_engine(Db) ->
+ {Status, _} = req(put, url(Db) ++ "?engine=cowabunga"),
+ ?assertEqual(400, Status).
+
+t_not_change_db_proper_after_rewriting_shardmap(Db) ->
+ delete_db(Db),
+ {201, _} = req(put, url(Db) ++ "?partitioned=true&q=1"),
+ ShardDbName = ?l2b(config:get("mem3", "shards_db", "_dbs")),
+ {ok, ShardDb} = mem3_util:ensure_exists(ShardDbName),
+ {ok, #doc{body = {Props}}} = couch_db:open_doc(
+ ShardDb, Db, [ejson_body]
+ ),
+ Shards = mem3_util:build_shards(Db, Props),
+ {Prop2} = ?JSON_DECODE(?JSON_ENCODE({Props})),
+ Shards2 = mem3_util:build_shards(Db, Prop2),
+ ?assertEqual(Shards2, Shards).
+
+t_succeed_on_all_docs_with_queries_keys(Db) ->
+ helper_queries_with_keys(Db, "_all_docs", 3).
+
+t_succeed_on_design_docs_with_queries_keys(Db) ->
+ helper_queries_with_keys(Db, "_design_docs", 1).
+
+t_succeed_on_local_docs_with_queries_keys(Db) ->
+ helper_queries_with_keys(Db, "_local_docs", 1).
+
+helper_queries_with_keys(Db, Path, ExpectedRows) ->
+ [create_doc(Db, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 5)],
+ [create_doc(Db, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 5)],
+ [create_doc(Db, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 5)],
+ QueryDoc = #{
+ <<"queries">> => [
+ #{
+ <<"keys">> =>
+ [<<"testdoc1">>, <<"_design/ddoc3">>, <<"_local/doc5">>]
+ }
+ ]
+ },
+ {Status, #{<<"results">> := [#{<<"rows">> := Rows}]}} =
+ req(post, url(Db, Path ++ "/queries"), QueryDoc),
+ ?assertEqual(200, Status),
+ ?assertEqual(ExpectedRows, length(Rows)).
+
+t_succeed_on_all_docs_with_queries_limit_skip(Db) ->
+ helper_queries_with_limit_skip(Db, "testdoc", "_all_docs", 2, 5).
+
+t_succeed_on_design_docs_with_queries_limit_skip(Db) ->
+ helper_queries_with_limit_skip(Db, "_design/ddoc", "_design_docs", 2, 5).
+
+t_succeed_on_local_docs_with_queries_limit_skip(Db) ->
+ helper_queries_with_limit_skip(Db, "_local/doc", "_local_docs", null, 5).
+
+helper_queries_with_limit_skip(Db, Id, Path, ExpectedOffset, ExpectedRows) ->
+ [create_doc(Db, Id ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = #{<<"queries">> => [#{<<"limit">> => 5, <<"skip">> => 2}]},
+ {Status, #{<<"results">> := [#{<<"offset">> := Offset, <<"rows">> := Rows}]}} =
+ req(post, url(Db, Path ++ "/queries"), QueryDoc),
+ ?assertEqual(200, Status),
+ ?assertEqual(ExpectedOffset, Offset),
+ ?assertEqual(ExpectedRows, length(Rows)).
+
+t_succeed_on_all_docs_with_multiple_queries(Db) ->
+ helper_queries_with_keys_limit_skip(Db, "_all_docs", 2, 3, 5).
+
+t_succeed_on_design_docs_with_multiple_queries(Db) ->
+ helper_queries_with_keys_limit_skip(Db, "_design_docs", 2, 1, 5).
+
+t_succeed_on_local_docs_with_multiple_queries(Db) ->
+ helper_queries_with_keys_limit_skip(Db, "_local_docs", null, 1, 5).
+
+helper_queries_with_keys_limit_skip(Db, Path, ExpectedOffset, ExpectedRows1, ExpectedRows2) ->
+ [create_doc(Db, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ [create_doc(Db, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ [create_doc(Db, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
+ QueryDoc = #{
+ <<"queries">> => [
+ #{
+ <<"keys">> =>
+ [<<"testdoc1">>, <<"_design/ddoc3">>, <<"_local/doc5">>]
+ },
+ #{<<"limit">> => 5, <<"skip">> => 2}
+ ]
+ },
+ {Status, #{
+ <<"results">> := [
+ #{<<"rows">> := Rows1},
+ #{<<"offset">> := Offset2, <<"rows">> := Rows2}
+ ]
+ }} = req(post, url(Db, Path ++ "/queries"), QueryDoc),
+ ?assertEqual(200, Status),
+ ?assertEqual(ExpectedRows1, length(Rows1)),
+ ?assertEqual(ExpectedOffset, Offset2),
+ ?assertEqual(ExpectedRows2, length(Rows2)).
+
+%%%%%%%%%%%%%%%%%%%% Utility Functions %%%%%%%%%%%%%%%%%%%%
+url(Db) ->
+ Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
+ Port = mochiweb_socket_server:get(chttpd, port),
+ lists:concat(["http://", Addr, ":", Port, "/", ?b2l(Db)]).
+
+url(Db, Path) ->
+ url(Db) ++ "/" ++ Path.
+
+create_db(Db) ->
+ case req(put, url(Db)) of
+ {201, #{}} -> ok;
+ Error -> error({failed_to_create_test_db, Db, Error})
+ end.
+
+delete_db(Db) ->
+ case req(delete, url(Db)) of
+ {200, #{}} -> ok;
+ Error -> error({failed_to_delete_test_db, Db, Error})
+ end.
+
+create_doc(Db, Id) ->
+ req(put, url(Db, Id), #{<<"mr">> => <<"rockoartischocko">>}).
+
+req(Method, Url) ->
+ Headers = [?CONTENT_JSON, ?AUTH],
+ {ok, Code, _, Res} = test_request:request(Method, Url, Headers),
+ {Code, jiffy:decode(Res, [return_maps])}.
+
+req(copy, Url, Headers) ->
+ {ok, Code, _, Res} = test_request:request(copy, Url, Headers),
+ {Code, jiffy:decode(Res, [return_maps])};
+req(Method, Url, #{} = Body) ->
+ req(Method, Url, jiffy:encode(Body));
+req(Method, Url, Body) ->
+ Headers = [?CONTENT_JSON, ?AUTH],
+ {ok, Code, _, Res} = test_request:request(Method, Url, Headers, Body),
+ {Code, jiffy:decode(Res, [return_maps])}.
+
+wait_non_empty_chunk(Db) ->
+ test_util:wait(
+ fun() ->
+ {ok, _, _, Bin} = test_request:get(url(Db, "_changes?feed=live&timeout=1"), [?AUTH]),
+ Parts = binary:split(Bin, <<"\n">>, [global]),
+ case [P || P <- Parts, size(P) > 0] of
+ [] -> wait;
+ Chunks -> Chunks
end
- )}.
-
-should_return_ok_true_on_ensure_full_commit(Url0) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- Url = Url0 ++ "/_ensure_full_commit",
- {ok, RC, _, Body} = test_request:post(Url, [?CONTENT_JSON, ?AUTH], []),
- {Json} = ?JSON_DECODE(Body),
- ?assertEqual(201, RC),
- ?assert(couch_util:get_value(<<"ok">>, Json))
- end)}.
-
-should_return_404_for_ensure_full_commit_on_no_db(Url0) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- Url = Url0 ++ "-missing-db" ++ "/_ensure_full_commit",
- {ok, RC, _, Body} = test_request:post(Url, [?CONTENT_JSON, ?AUTH], []),
- {Json} = ?JSON_DECODE(Body),
- ?assertEqual(404, RC),
- ?assertEqual(<<"not_found">>, couch_util:get_value(<<"error">>, Json))
- end)}.
-
-should_accept_live_as_an_alias_for_continuous(Url) ->
- GetLastSeq = fun(Chunks) ->
- LastSeqBin = lists:last(Chunks),
- {Result} =
- try ?JSON_DECODE(LastSeqBin) of
- Data -> Data
- catch
- _:_ ->
- % should not happen, abort
- ?assert(false)
- end,
- couch_util:get_value(<<"last_seq">>, Result, undefined)
- end,
- {timeout, ?TIMEOUT,
- ?_test(begin
- LastSeq1 = GetLastSeq(wait_non_empty_chunk(Url)),
-
- {ok, _, _, _} = create_doc(Url, "testdoc2"),
-
- LastSeq2 = GetLastSeq(wait_non_empty_chunk(Url)),
-
- ?assertNotEqual(LastSeq1, LastSeq2)
- end)}.
-
-should_return_404_for_delete_att_on_notadoc(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, RC, _, RespBody} = test_request:delete(
- Url ++ "/notadoc/att.pdf",
- [?CONTENT_JSON, ?AUTH],
- []
- ),
- ?assertEqual(404, RC),
- ?assertEqual(
- {[
- {<<"error">>, <<"not_found">>},
- {<<"reason">>, <<"missing">>}
- ]},
- jiffy:decode(RespBody)
- ),
- {ok, RC1, _, _} = test_request:get(
- Url ++ "/notadoc",
- [?CONTENT_JSON, ?AUTH],
- []
- ),
- ?assertEqual(404, RC1)
- end)}.
-
-should_return_409_for_del_att_without_rev(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, RC, _, _} = test_request:put(
- Url ++ "/testdoc3",
- [?CONTENT_JSON, ?AUTH],
- jiffy:encode(attachment_doc())
- ),
- ?assertEqual(201, RC),
-
- {ok, RC1, _, _} = test_request:delete(
- Url ++ "/testdoc3/file.erl",
- [?CONTENT_JSON, ?AUTH],
- []
- ),
- ?assertEqual(409, RC1)
- end)}.
-
-should_return_200_for_del_att_with_rev(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, RC, _Headers, RespBody} = test_request:put(
- Url ++ "/testdoc4",
- [?CONTENT_JSON, ?AUTH],
- jiffy:encode(attachment_doc())
- ),
- ?assertEqual(201, RC),
-
- {ResultJson} = ?JSON_DECODE(RespBody),
- Rev = couch_util:get_value(<<"rev">>, ResultJson, undefined),
-
- {ok, RC1, _, _} = test_request:delete(
- Url ++ "/testdoc4/file.erl?rev=" ++ Rev,
- [?CONTENT_JSON, ?AUTH],
- []
- ),
- ?assertEqual(200, RC1)
- end)}.
-
-should_return_409_for_put_att_nonexistent_rev(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, RC, _Headers, RespBody} = test_request:put(
- Url ++ "/should_return_404/file.erl?rev=1-000",
- [?CONTENT_JSON, ?AUTH],
- jiffy:encode(attachment_doc())
- ),
- ?assertEqual(409, RC),
- ?assertMatch(
- {[
- {<<"error">>, <<"not_found">>},
- {<<"reason">>, <<"missing_rev">>}
- ]},
- ?JSON_DECODE(RespBody)
- )
- end)}.
-
-should_return_update_seq_when_set_on_all_docs(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 3)],
- {ok, RC, _, RespBody} = test_request:get(
- Url ++ "/_all_docs/" ++
- "?update_seq=true&keys=[\"testdoc1\"]",
- [?CONTENT_JSON, ?AUTH]
- ),
- ?assertEqual(200, RC),
- {ResultJson} = ?JSON_DECODE(RespBody),
- ?assertNotEqual(
- undefined,
- couch_util:get_value(<<"update_seq">>, ResultJson)
- ),
- ?assertNotEqual(
- undefined,
- couch_util:get_value(<<"offset">>, ResultJson)
- )
- end)}.
-
-should_not_return_update_seq_when_unset_on_all_docs(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 3)],
- {ok, RC, _, RespBody} = test_request:get(
- Url ++ "/_all_docs/" ++
- "?update_seq=false&keys=[\"testdoc1\"]",
- [?CONTENT_JSON, ?AUTH]
- ),
- ?assertEqual(200, RC),
- {ResultJson} = ?JSON_DECODE(RespBody),
- ?assertEqual(
- undefined,
- couch_util:get_value(<<"update_seq">>, ResultJson)
- ),
- ?assertNotEqual(
- undefined,
- couch_util:get_value(<<"offset">>, ResultJson)
- )
- end)}.
-
-should_return_correct_id_on_doc_copy(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, _, _, _} = create_doc(Url, "testdoc"),
- {_, _, _, ResultBody1} = test_request:copy(
- Url ++ "/testdoc/",
- [?CONTENT_JSON, ?AUTH, ?DESTHEADER1]
- ),
- {ResultJson1} = ?JSON_DECODE(ResultBody1),
- Id1 = couch_util:get_value(<<"id">>, ResultJson1),
-
- {_, _, _, ResultBody2} = test_request:copy(
- Url ++ "/testdoc/",
- [?CONTENT_JSON, ?AUTH, ?DESTHEADER2]
- ),
- {ResultJson2} = ?JSON_DECODE(ResultBody2),
- Id2 = couch_util:get_value(<<"id">>, ResultJson2),
- [
- ?assertEqual(<<102, 111, 111, 229, 149, 138, 98, 97, 114>>, Id1),
- ?assertEqual(<<"foo/bar#baz?pow:fiz">>, Id2)
- ]
- end)}.
-
-should_return_only_one_ok_on_doc_copy(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, _, _, _} = create_doc(Url, "testdoc"),
- {_, _, _, ResultBody} = test_request:copy(
- Url ++ "/testdoc",
- [?CONTENT_JSON, ?AUTH, ?DESTHEADER1]
- ),
- {ResultJson} = jiffy:decode(ResultBody),
- NumOks = length(lists:filter(fun({Key, _Value}) -> Key == <<"ok">> end, ResultJson)),
- [
- ?assertEqual(1, NumOks)
- ]
- end)}.
+ end
+ ).
attachment_doc() ->
{ok, Data} = file:read_file(?FIXTURE_TXT),
- {[
- {<<"_attachments">>,
- {[
- {<<"file.erl">>,
- {[
- {<<"content_type">>, <<"text/plain">>},
- {<<"data">>, base64:encode(Data)}
- ]}}
- ]}}
- ]}.
-
-should_return_400_for_bad_engine(_) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- TmpDb = ?tempdb(),
- Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
- Port = mochiweb_socket_server:get(chttpd, port),
- BaseUrl = lists:concat(["http://", Addr, ":", Port, "/", ?b2l(TmpDb)]),
- Url = BaseUrl ++ "?engine=cowabunga",
- {ok, Status, _, _} = test_request:put(Url, [?CONTENT_JSON, ?AUTH], "{}"),
- ?assertEqual(400, Status)
- end)}.
-
-should_not_change_db_proper_after_rewriting_shardmap(_) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- TmpDb = ?tempdb(),
- Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
- Port = mochiweb_socket_server:get(chttpd, port),
-
- BaseUrl = lists:concat(["http://", Addr, ":", Port, "/", ?b2l(TmpDb)]),
- Url = BaseUrl ++ "?partitioned=true&q=1",
- {ok, 201, _, _} = test_request:put(Url, [?CONTENT_JSON, ?AUTH], "{}"),
-
- ShardDbName = ?l2b(config:get("mem3", "shards_db", "_dbs")),
- {ok, ShardDb} = mem3_util:ensure_exists(ShardDbName),
- {ok, #doc{body = {Props}}} = couch_db:open_doc(
- ShardDb, TmpDb, [ejson_body]
- ),
- Shards = mem3_util:build_shards(TmpDb, Props),
-
- {Prop2} = ?JSON_DECODE(?JSON_ENCODE({Props})),
- Shards2 = mem3_util:build_shards(TmpDb, Prop2),
- ?assertEqual(Shards2, Shards),
- {ok, 200, _, _} = test_request:delete(BaseUrl, [?AUTH])
- end)}.
-
-should_succeed_on_all_docs_with_queries_keys(Url) ->
- {timeout, ?TIMEOUT,
- ?_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) ->
- {timeout, ?TIMEOUT,
- ?_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) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "testdoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc =
- "{\"queries\": [{\"keys\": [ \"testdoc3\", \"testdoc8\"]},\n"
- " {\"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) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc =
- "{\"queries\": [{\"keys\": [ \"_design/ddoc3\",\n"
- " \"_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) ->
- {timeout, ?TIMEOUT,
- ?_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) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "_design/ddoc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc =
- "{\"queries\": [{\"keys\": [ \"_design/ddoc3\",\n"
- " \"_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) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc =
- "{\"queries\": [{\"keys\":\n"
- " [ \"_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_queries_limit_skip(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc = "{\"queries\": [{\"limit\": 5, \"skip\": 2}]}",
- {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(5, length(couch_util:get_value(<<"rows">>, InnerJson)))
- end)}.
-
-should_succeed_on_local_docs_with_multiple_queries(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- [create_doc(Url, "_local/doc" ++ ?i2l(I)) || I <- lists:seq(1, 10)],
- QueryDoc =
- "{\"queries\": [{\"keys\": [ \"_local/doc3\",\n"
- " \"_local/doc8\"]}, {\"limit\": 5, \"skip\": 2}]}",
- {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(2, length(couch_util:get_value(<<"rows">>, InnerJson1))),
- {InnerJson2} = lists:nth(2, ResultJsonBody),
- ?assertEqual(5, length(couch_util:get_value(<<"rows">>, InnerJson2)))
- end)}.
-
-should_return_headers_after_starting_continious(Url) ->
- {timeout, ?TIMEOUT,
- ?_test(begin
- {ok, _, _, Bin} =
- test_request:get(Url ++ "/_changes?feed=live&timeout=1", [?AUTH]),
-
- Parts = binary:split(Bin, <<"\n">>, [global]),
- %% we should receive at least one part even when timeout=1
- ?assertNotEqual([], Parts)
- end)}.
-
-wait_non_empty_chunk(Url) ->
- test_util:wait(fun() ->
- {ok, _, _, Bin} =
- test_request:get(Url ++ "/_changes?feed=live&timeout=1", [?AUTH]),
-
- Parts = binary:split(Bin, <<"\n">>, [global]),
-
- case [P || P <- Parts, size(P) > 0] of
- [] -> wait;
- Chunks -> Chunks
- end
- end).
+ #{
+ <<"_attachments">> => #{
+ <<"file.erl">> => #{
+ <<"content_type">> => <<"text/plain">>,
+ <<"data">> => base64:encode(Data)
+ }
+ }
+ }.
diff --git a/src/fabric/src/fabric_view_all_docs.erl b/src/fabric/src/fabric_view_all_docs.erl
index 7821dcf72..a6786bff7 100644
--- a/src/fabric/src/fabric_view_all_docs.erl
+++ b/src/fabric/src/fabric_view_all_docs.erl
@@ -73,6 +73,15 @@ go(DbName, Options, QueryArgs, Callback, Acc0) ->
spawn_monitor(?MODULE, open_doc, [DbName, Options ++ DocOptions1, Key, IncludeDocs])
end,
MaxJobs = all_docs_concurrency(),
+ %% namespace can be _set_ to `undefined`, so we want simulate enum here
+ Namespace =
+ case couch_util:get_value(namespace, Extra) of
+ <<"_all_docs">> -> <<"_all_docs">>;
+ <<"_design">> -> <<"_design">>;
+ <<"_non_design">> -> <<"_non_design">>;
+ <<"_local">> -> <<"_local">>;
+ _ -> <<"_all_docs">>
+ end,
Keys1 =
case Dir of
fwd -> Keys0;
@@ -88,15 +97,7 @@ go(DbName, Options, QueryArgs, Callback, Acc0) ->
true -> lists:sublist(Keys2, Limit);
false -> Keys2
end,
- %% namespace can be _set_ to `undefined`, so we want simulate enum here
- Namespace =
- case couch_util:get_value(namespace, Extra) of
- <<"_all_docs">> -> <<"_all_docs">>;
- <<"_design">> -> <<"_design">>;
- <<"_non_design">> -> <<"_non_design">>;
- <<"_local">> -> <<"_local">>;
- _ -> <<"_all_docs">>
- end,
+ Keys4 = filter_keys_by_namespace(Keys3, Namespace),
Timeout = fabric_util:all_docs_timeout(),
{_, Ref} = spawn_monitor(fun() ->
exit(fabric:get_doc_count(DbName, Namespace))
@@ -112,7 +113,7 @@ go(DbName, Options, QueryArgs, Callback, Acc0) ->
end,
{ok, Acc1} = Callback({meta, Meta}, Acc0),
Resp = doc_receive_loop(
- Keys3, queue:new(), SpawnFun, MaxJobs, Callback, Acc1
+ Keys4, queue:new(), SpawnFun, MaxJobs, Callback, Acc1
),
case Resp of
{ok, Acc2} ->
@@ -361,3 +362,26 @@ cancel_read_pids(Pids) ->
{empty, _} ->
ok
end.
+
+filter_keys_by_namespace(Keys, Namespace) when Namespace =:= <<"_design">> ->
+ lists:filter(
+ fun(Key) ->
+ case Key of
+ <<?DESIGN_DOC_PREFIX, _/binary>> -> true;
+ _ -> false
+ end
+ end,
+ Keys
+ );
+filter_keys_by_namespace(Keys, Namespace) when Namespace =:= <<"_local">> ->
+ lists:filter(
+ fun(Key) ->
+ case Key of
+ <<?LOCAL_DOC_PREFIX, _/binary>> -> true;
+ _ -> false
+ end
+ end,
+ Keys
+ );
+filter_keys_by_namespace(Keys, _Namespace) ->
+ Keys.