summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabor Pali <gabor.pali@ibm.com>2023-03-27 15:20:08 +0200
committerNick Vatamaniuc <nickva@users.noreply.github.com>2023-04-18 23:51:32 -0400
commitcc500adc1d5a44710059dabc5c54e4f48607e846 (patch)
tree715ee635a1f7b02c92187c908775b6d3c490f520
parentf5371aab79d6d241a6b28d04c0e67482804a9cf7 (diff)
downloadcouchdb-cc500adc1d5a44710059dabc5c54e4f48607e846.tar.gz
mango: mark fields with the `$exists` operator indexable
This is required to make index selection work better with covering indexes. The `$exists` operator prescribes the presence of the given field so that if an index has the field, it shall be considered because it implies true. Without this change, it will not happen, but covering indexes can work if the index is manually picked.
-rw-r--r--src/mango/src/mango_idx_view.erl94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/mango/src/mango_idx_view.erl b/src/mango/src/mango_idx_view.erl
index 641b8140a..e3db24fbb 100644
--- a/src/mango/src/mango_idx_view.erl
+++ b/src/mango/src/mango_idx_view.erl
@@ -300,6 +300,10 @@ indexable({[{<<"$gt">>, _}]}) ->
true;
indexable({[{<<"$gte">>, _}]}) ->
true;
+% This is required to improve index selection for covering indexes.
+% Making `$exists` indexable should not cause problems in other cases.
+indexable({[{<<"$exists">>, _}]}) ->
+ true;
% All other operators are currently not indexable.
% This is also a subtle assertion that we don't
% call indexable/1 on a field name.
@@ -542,6 +546,96 @@ covers(Idx, Fields) ->
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
+indexable_fields_of(Selector) ->
+ indexable_fields(test_util:as_selector(Selector)).
+
+indexable_fields_empty_test() ->
+ ?assertEqual([], indexable_fields_of(#{})).
+
+indexable_fields_and_test() ->
+ Selector = #{<<"$and">> => [#{<<"field1">> => undefined, <<"field2">> => undefined}]},
+ ?assertEqual([<<"field1">>, <<"field2">>], indexable_fields_of(Selector)).
+
+indexable_fields_or_test() ->
+ Selector = #{<<"$or">> => [#{<<"field1">> => undefined, <<"field2">> => undefined}]},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_nor_test() ->
+ Selector = #{<<"$nor">> => [#{<<"field1">> => undefined, <<"field2">> => undefined}]},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_all_test() ->
+ Selector = #{<<"field">> => #{<<"$all">> => []}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_elemMatch_test() ->
+ Selector = #{<<"field">> => #{<<"$elemMatch">> => #{}}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_allMatch_test() ->
+ Selector = #{<<"field">> => #{<<"$allMatch">> => #{}}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_keyMapMatch_test() ->
+ Selector = #{<<"field">> => #{<<"$keyMapMatch">> => #{}}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_in_test() ->
+ Selector = #{<<"field">> => #{<<"$in">> => []}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_nin_test() ->
+ Selector = #{<<"field">> => #{<<"$nin">> => []}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_not_test() ->
+ Selector = #{<<"field">> => #{<<"$not">> => #{}}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_lt_test() ->
+ Selector = #{<<"field">> => #{<<"$lt">> => undefined}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_lte_test() ->
+ Selector = #{<<"field">> => #{<<"$lte">> => undefined}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_eq_test() ->
+ Selector = #{<<"field">> => #{<<"$eq">> => undefined}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_ne_test() ->
+ Selector = #{<<"field">> => #{<<"$ne">> => undefined}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_gte_test() ->
+ Selector = #{<<"field">> => #{<<"$gte">> => undefined}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_gt_test() ->
+ Selector = #{<<"field">> => #{<<"$gt">> => undefined}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_mod_test() ->
+ Selector = #{<<"field">> => #{<<"$mod">> => [0, 0]}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_regex_test() ->
+ Selector = #{<<"field">> => #{<<"$regex">> => undefined}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_exists_test() ->
+ Selector = #{<<"field">> => #{<<"$exists">> => true}},
+ ?assertEqual([<<"field">>], indexable_fields_of(Selector)).
+
+indexable_fields_type_test() ->
+ Selector = #{<<"field">> => #{<<"$type">> => undefined}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
+indexable_fields_size_test() ->
+ Selector = #{<<"field">> => #{<<"$size">> => 0}},
+ ?assertEqual([], indexable_fields_of(Selector)).
+
covers_all_fields_test() ->
?assertNot(covers(undefined, all_fields)).