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 32c6773c9be9d1bc80c603a47c609a3be4291929 Author: James Coglan <[email protected]> AuthorDate: Mon May 11 10:29:06 2026 +0100 fix: Normalize negations inside $allMatch and other operators --- src/mango/src/mango_selector.erl | 68 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/mango/src/mango_selector.erl b/src/mango/src/mango_selector.erl index 5d72ee581..e651f1565 100644 --- a/src/mango/src/mango_selector.erl +++ b/src/mango/src/mango_selector.erl @@ -299,9 +299,16 @@ norm_negations({[{<<"$or">>, Args}]}) -> norm_negations({[{<<"$elemMatch">>, Arg}]}) -> {[{<<"$elemMatch">>, norm_negations(Arg)}]}; norm_negations({[{<<"$allMatch">>, Arg}]}) -> - {[{<<"$allMatch">>, norm_negations(Arg)}]}; + Out = norm_negations(Arg), + {[{<<"$allMatch">>, Out}]}; norm_negations({[{<<"$keyMapMatch">>, Arg}]}) -> {[{<<"$keyMapMatch">>, norm_negations(Arg)}]}; +% Sometimes a top-level key will be a doc field because it +% can't be pushed down through its corresponding operator, +% e.g. {x: {$allMatch: S}}. Negation inside the $allMatch +% should still be normalized. +norm_negations({[{Field, Cond}]}) when is_list(Field) -> + {[{Field, norm_negations(Cond)}]}; % All other conditions can't introduce negations anywhere % further down the operator tree. norm_negations(Cond) -> @@ -1712,4 +1719,63 @@ match_nor_test() -> ?assertEqual(false, match_int(SelMulti, {[{<<"x">>, 9}]})), ?assertEqual(false, match_int(SelMulti, {[]})). +normalize_nor_test() -> + Sel1 = normalize( + {[ + {<<"x">>, + {[ + {<<"$nor">>, [ + {[{<<"$lt">>, 3}]}, {[{<<"$gt">>, 5}]} + ]} + ]}} + ]} + ), + ?assertEqual( + {[ + {<<"$and">>, [ + {[{[<<"x">>], {[{<<"$gte">>, 3}]}}]}, + {[{[<<"x">>], {[{<<"$lte">>, 5}]}}]} + ]} + ]}, + Sel1 + ), + ?assertEqual(true, match_int(Sel1, {[{<<"x">>, 4}]})), + ?assertEqual(false, match_int(Sel1, {[{<<"x">>, 40}]})), + + Sel2 = normalize( + {[ + {<<"x">>, + {[ + {<<"$allMatch">>, + {[ + {<<"y">>, + {[ + {<<"$nor">>, [ + {[{<<"$lt">>, 3}]}, {[{<<"$gt">>, 5}]} + ]} + ]}} + ]}} + ]}} + ]} + ), + ?assertEqual( + {[ + { + [<<"x">>], + {[ + {<<"$allMatch">>, + {[ + {<<"$and">>, [ + {[{[<<"y">>], {[{<<"$gte">>, 3}]}}]}, + {[{[<<"y">>], {[{<<"$lte">>, 5}]}}]} + ]} + ]}} + ]} + } + ]}, + Sel2 + ), + ?assertEqual(true, match_int(Sel2, {[{<<"x">>, [{[{<<"y">>, 4}]}]}]})), + ?assertEqual(false, match_int(Sel2, {[{<<"x">>, [{[{<<"y">>, 40}]}]}]})). + -endif.
