You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by jc...@apache.org on 2026/07/28 13:17:15 UTC
(couchdb) 13/16: fix: Selectors like {"$not":{"$allMatch":S}} (and "$elemMatch" and "$keyMapMatch") should be considered successful when applied to values of the wrong type
This is an automated email from the ASF dual-hosted git repository.
jcoglan pushed a commit to branch mango-match-failures
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 649a00d5aeac6bd474afe2c7222a27ba6b822ab2
Author: James Coglan <ja...@neighbourhood.ie>
AuthorDate: Mon Jul 20 12:08:39 2026 +0100
fix: Selectors like {"$not":{"$allMatch":S}} (and "$elemMatch" and "$keyMapMatch") should be considered successful when applied to values of the wrong type
---
src/mango/src/mango_selector.erl | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/src/mango/src/mango_selector.erl b/src/mango/src/mango_selector.erl
index a5796ed9e..dac0b2e14 100644
--- a/src/mango/src/mango_selector.erl
+++ b/src/mango/src/mango_selector.erl
@@ -500,9 +500,11 @@ match({[{<<"$elemMatch">>, _Arg}]}, [], #ctx{negate = false} = Ctx) ->
[#failure{op = elemMatch, type = empty_list, ctx = Ctx}];
match({[{<<"$elemMatch">>, _Arg}]}, [], #ctx{negate = true}) ->
[];
-match({[{<<"$elemMatch">>, Arg}]}, Values, #ctx{negate = true} = Ctx) ->
+match({[{<<"$elemMatch">>, Arg}]}, Values, #ctx{negate = true} = Ctx) when is_list(Values) ->
PosCtx = Ctx#ctx{negate = false},
match({[{<<"$allMatch">>, {[{<<"$not">>, Arg}]}}]}, Values, PosCtx);
+match({[{<<"$elemMatch">>, _Arg}]}, _Values, #ctx{negate = true}) ->
+ [];
match({[{<<"$elemMatch">>, Arg}]}, Values, #ctx{path = Path} = Ctx) when is_list(Values) ->
ValueFailures = [
match(Arg, V, Ctx#ctx{path = [Idx | Path]})
@@ -538,9 +540,11 @@ match({[{<<"$allMatch">>, _Arg}]}, [], #ctx{negate = false} = Ctx) ->
[#failure{op = allMatch, type = empty_list, ctx = Ctx}];
match({[{<<"$allMatch">>, _Arg}]}, [], #ctx{negate = true}) ->
[];
-match({[{<<"$allMatch">>, Arg}]}, Values, #ctx{negate = true} = Ctx) ->
+match({[{<<"$allMatch">>, Arg}]}, Values, #ctx{negate = true} = Ctx) when is_list(Values) ->
PosCtx = Ctx#ctx{negate = false},
match({[{<<"$elemMatch">>, {[{<<"$not">>, Arg}]}}]}, Values, PosCtx);
+match({[{<<"$allMatch">>, _Arg}]}, _Values, #ctx{negate = true}) ->
+ [];
match({[{<<"$allMatch">>, Arg}]}, [_ | _] = Values, #ctx{path = Path} = Ctx) ->
MatchValue = fun({Idx, V}) -> match(Arg, V, Ctx#ctx{path = [Idx | Path]}) end,
EnumValues = lists:enumerate(0, Values),
@@ -579,6 +583,8 @@ match({[{<<"$keyMapMatch">>, Arg}]}, Value, #ctx{negate = true, path = Path} = C
Keys = [Key || {Key, _} <- element(1, Value)],
MatchKey = fun(K) -> match(Arg, K, Ctx#ctx{path = [K | Path]}) end,
lists:flatmap(MatchKey, Keys);
+match({[{<<"$keyMapMatch">>, _Arg}]}, _Value, #ctx{negate = true}) ->
+ [];
match({[{<<"$keyMapMatch">>, Arg}]}, Value, #ctx{path = Path} = Ctx) when is_tuple(Value) ->
Keys = [Key || {Key, _} <- element(1, Value)],
KeyFailures = [match(Arg, K, Ctx#ctx{path = [K | Path]}) || K <- Keys],
@@ -2104,7 +2110,11 @@ match_allmatch_test() ->
?assertEqual(
[{[{<<"path">>, [<<"x">>, 0]}, {<<"message">>, <<"must not be equal to 0">>}]}],
FailsNeg
- ).
+ ),
+
+ % negating an expression that produces an error should produce a success response
+ ?assertEqual(match_int(SelNeg, {[{<<"x">>, 0}]}), true),
+ ?assertEqual(match_failures(SelNeg, {[{<<"x">>, 0}]}), []).
match_elemmatch_test() ->
check_selector({[{<<"$elemMatch">>, {[{<<"$eq">>, 0}]}}]}, [
@@ -2129,7 +2139,11 @@ match_elemmatch_test() ->
?assertEqual(
[{[{<<"path">>, [<<"x">>, 0]}, {<<"message">>, <<"must not be equal to 0">>}]}],
FailsNeg
- ).
+ ),
+
+ % negating an expression that produces an error should produce a success response
+ ?assertEqual(match_int(SelNeg, {[{<<"x">>, 0}]}), true),
+ ?assertEqual(match_failures(SelNeg, {[{<<"x">>, 0}]}), []).
match_keymapmatch_test() ->
check_selector({[{<<"$keyMapMatch">>, {[{<<"$regex">>, <<"^[a-z]+$">>}]}}]}, [
@@ -2157,7 +2171,11 @@ match_keymapmatch_test() ->
?assertEqual(
[{[{<<"path">>, [<<"x">>, <<"alfa">>]}, {<<"message">>, <<"must not begin with 'a'">>}]}],
FailsNeg
- ).
+ ),
+
+ % negating an expression that produces an error should produce a success response
+ ?assertEqual(match_int(SelNeg, {[{<<"x">>, 0}]}), true),
+ ?assertEqual(match_failures(SelNeg, {[{<<"x">>, 0}]}), []).
match_object_test() ->
Doc1 = {[]},