Skip to content

Commit

Permalink
fixup: extend allow_fallback to always exclude all_docs
Browse files Browse the repository at this point in the history
  • Loading branch information
pgj committed Oct 18, 2024
1 parent 21d6e7b commit c92c389
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 31 deletions.
22 changes: 12 additions & 10 deletions src/docs/src/api/database/find.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
.. http:post:: /{db}/_find
:synopsis: Find documents within a given database.

Find documents using a declarative JSON querying syntax.
Queries will use custom indexes, specified using the :ref:`_index <api/db/find/index>`
endpoint, if available.
Otherwise, they use the built-in :ref:`_all_docs <api/db/all_docs>` index, which
can be arbitrarily slow.
Find documents using a declarative JSON querying syntax. Queries
will use custom indexes, specified using the :ref:`_index
<api/db/find/index>` endpoint, if available. Otherwise, when
allowed, they use the built-in :ref:`_all_docs <api/db/all_docs>`
index, which can be arbitrarily slow.

:param db: Database name

Expand Down Expand Up @@ -51,11 +51,13 @@
fallback occurs, the details are given in the ``warning``
field of the response. *Optional*
:<json boolean allow_fallback: Tell if it is allowed to fall back
to a valid index when requesting a query to use a specific
index that is not deemed usable. Default is ``true``. This
is meant to be used in combination with ``use_index`` and
setting ``allow_fallback`` to ``false`` can make the query
fail if the user-specified index is not suitable. *Optional*
to another valid index. This can happen on running a query
with an index specified by ``use_index`` which is not deemed
usable, or when only the built-in :ref:`_all_docs
<api/db/all_docs>` index would be picked in lack of indexes
available to support the query. Disabling this fallback logic
causes the endpoint immediately return an error in such cases.
Default is ``true``. *Optional*
:<json boolean conflicts: Include conflicted documents if ``true``.
Intended use is to easily find conflicted documents, without an
index or view. Default is ``false``. *Optional*
Expand Down
27 changes: 12 additions & 15 deletions src/mango/src/mango_cursor.erl
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,19 @@
create(Db, Selector0, Opts, Kind) ->
Selector = mango_selector:normalize(Selector0),
{UsableIndexes, Trace} = mango_idx:get_usable_indexes(Db, Selector, Opts, Kind),
UseIndex = use_index(Opts),
case maybe_filter_indexes_by_ddoc(UsableIndexes, Opts) of
[] ->
% use_index doesn't match a valid index - determine how
% this shall be handled by the further settings
case allow_fallback(Opts) of
case allow_fallback(Opts) orelse (UseIndex == [] andalso length(UsableIndexes) > 1) of
true ->
% fall back to a valid index
create_cursor(Db, {UsableIndexes, Trace}, Selector, Opts);
false ->
% return an error
% no usable index but all_docs, no fallback allowed: return an error
Details =
case use_index(Opts) of
case UseIndex of
[] -> [];
[DesignId] -> [ddoc_name(DesignId)];
[DesignId, ViewName] -> [ddoc_name(DesignId), ViewName]
Expand Down Expand Up @@ -598,8 +599,8 @@ create_test_() ->
?TDEF_FE(t_create_regular, 10),
?TDEF_FE(t_create_user_specified_index, 10),
?TDEF_FE(t_create_invalid_user_specified_index, 10),
?TDEF_FE(t_create_invalid_user_specified_index_no_fallback_1, 10),
?TDEF_FE(t_create_invalid_user_specified_index_no_fallback_2, 10)
?TDEF_FE(t_create_invalid_user_specified_index_no_fallback, 10),
?TDEF_FE(t_create_no_suitable_index_no_fallback, 10)
]
}.

Expand Down Expand Up @@ -690,7 +691,7 @@ t_create_invalid_user_specified_index(_) ->
),
?assertEqual(view_cursor, create(db, selector, Options, target)).

t_create_invalid_user_specified_index_no_fallback_1(_) ->
t_create_invalid_user_specified_index_no_fallback(_) ->
IndexSpecial = #idx{type = <<"special">>, def = all_docs},
IndexView1 = #idx{type = <<"json">>, ddoc = <<"_design/view_idx1">>},
IndexView2 = #idx{type = <<"json">>, ddoc = <<"_design/view_idx2">>},
Expand Down Expand Up @@ -721,21 +722,17 @@ t_create_invalid_user_specified_index_no_fallback_1(_) ->
Exception = {mango_error, mango_cursor, {invalid_index, UseIndex}},
?assertThrow(Exception, create(db, selector, Options, target)).

t_create_invalid_user_specified_index_no_fallback_2(_) ->
t_create_no_suitable_index_no_fallback(_) ->
IndexSpecial = #idx{type = <<"special">>, def = all_docs},
IndexView1 = #idx{type = <<"json">>, ddoc = <<"_design/view_idx1">>},
IndexView2 = #idx{type = <<"json">>, ddoc = <<"_design/view_idx2">>},
IndexView3 = #idx{type = <<"json">>, ddoc = <<"_design/view_idx3">>},
UsableIndexes = [IndexSpecial, IndexView1, IndexView2, IndexView3],
IndexesOfType = [IndexView1, IndexView2, IndexView3],
UsableIndexes = [IndexSpecial],
IndexesOfType = [],
Trace1 = #{},
Trace2 =
#{
filtered_indexes => sets:from_list(UsableIndexes),
indexes_of_type => sets:from_list(IndexesOfType)
},
UseIndex = [],
Options = [{use_index, UseIndex}, {allow_fallback, false}],
Options = [{use_index, []}, {allow_fallback, false}],
meck:expect(mango_selector, normalize, [selector], meck:val(normalized_selector)),
meck:expect(
mango_idx,
Expand All @@ -749,7 +746,7 @@ t_create_invalid_user_specified_index_no_fallback_2(_) ->
[db, {IndexesOfType, Trace2}, normalized_selector, Options],
meck:val(view_cursor)
),
Exception = {mango_error, mango_cursor, {invalid_index, UseIndex}},
Exception = {mango_error, mango_cursor, {invalid_index, []}},
?assertThrow(Exception, create(db, selector, Options, target)).

enhance_candidates_test() ->
Expand Down
50 changes: 44 additions & 6 deletions src/mango/test/05-index-selection-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,29 +222,67 @@ def test_use_index_with_invalid_name(self):
raise AssertionError("did not fail on invalid index name")

def test_use_index_without_fallback(self):
with self.subTest(use_index="valid"):
with self.subTest(use_index="valid", fallback_available=None):
docs = self.db.find(
{"manager": True}, use_index="manager", allow_fallback=False
)
assert len(docs) > 0

with self.subTest(use_index="invalid"):
with self.subTest(use_index="invalid", fallback_available=True):
try:
self.db.find(
{"manager": True}, use_index="invalid", allow_fallback=False
)
except Exception as e:
self.assertEqual(e.response.status_code, 400)
else:
raise AssertionError("did not fail on invalid index")
raise AssertionError("did not fail on invalid index for use_index")

with self.subTest(use_index="empty"):
with self.subTest(use_index="empty", fallback_available=True):
try:
self.db.find({"manager": True}, use_index=[], allow_fallback=False)
docs = self.db.find(
{"manager": True}, use_index=[], allow_fallback=False
)
assert len(docs) > 0
except Exception as e:
raise AssertionError(
"fail due to missing use_index with suitable indexes"
)

with self.subTest(use_index="empty", fallback_available=False):
try:
self.db.find({"company": "foobar"}, use_index=[], allow_fallback=False)
except Exception as e:
self.assertEqual(e.response.status_code, 400)
else:
raise AssertionError(
"did not fail due to missing use_index without suitable indexes"
)

with self.subTest(use_index="invalid", fallback_available=False):
try:
self.db.find(
{"company": "foobar"}, use_index="invalid", allow_fallback=False
)
except Exception as e:
self.assertEqual(e.response.status_code, 400)
else:
raise AssertionError("did not fail due to missing use_index")
raise AssertionError("did not fail on invalid index for use_index")

def test_index_without_fallback(self):
try:
docs = self.db.find({"manager": True}, allow_fallback=False)
assert len(docs) > 0
except Exception as e:
raise AssertionError("fail on usable indexes")

def test_no_index_without_fallback(self):
try:
self.db.find({"company": "foobar"}, allow_fallback=False)
except Exception as e:
self.assertEqual(e.response.status_code, 400)
else:
raise AssertionError("did not fail on no usable indexes")


class JSONIndexSelectionTests(mango.UserDocsTests, IndexSelectionTests):
Expand Down

0 comments on commit c92c389

Please sign in to comment.