diff options
Diffstat (limited to 'src/mango/src/mango_selector.erl')
-rw-r--r-- | src/mango/src/mango_selector.erl | 225 |
1 files changed, 207 insertions, 18 deletions
diff --git a/src/mango/src/mango_selector.erl b/src/mango/src/mango_selector.erl index 4ff36945a..968dc3c74 100644 --- a/src/mango/src/mango_selector.erl +++ b/src/mango/src/mango_selector.erl @@ -578,36 +578,63 @@ match({[_, _ | _] = _Props} = Sel, _Value, _Cmp) -> % until we match then all or run out of selector to % match against. +has_required_fields(Selector, RequiredFields) -> + Remainder = has_required_fields_int(Selector, RequiredFields), + Remainder == []. + % Empty selector -has_required_fields({[]}, _) -> - false; +has_required_fields_int({[]}, Remainder) -> + Remainder; % No more required fields -has_required_fields(_, []) -> - true; +has_required_fields_int(_, []) -> + []; % No more selector -has_required_fields([], _) -> - false; +has_required_fields_int([], Remainder) -> + Remainder; -has_required_fields(Selector, RequiredFields) when not is_list(Selector) -> - has_required_fields([Selector], RequiredFields); +has_required_fields_int(Selector, RequiredFields) when not is_list(Selector) -> + has_required_fields_int([Selector], RequiredFields); -% We can "see" through $and operator. We ignore other -% combination operators because they can't be used to restrict -% an index. -has_required_fields([{[{<<"$and">>, Args}]}], RequiredFields) +% We can "see" through $and operator. Iterate +% through the list of child operators. +has_required_fields_int([{[{<<"$and">>, Args}]}], RequiredFields) + when is_list(Args) -> + has_required_fields_int(Args, RequiredFields); + +% We can "see" through $or operator. Required fields +% must be covered by all children. +has_required_fields_int([{[{<<"$or">>, Args}]} | Rest], RequiredFields) + when is_list(Args) -> + Remainder0 = lists:foldl(fun(Arg, Acc) -> + % for each child test coverage against the full + % set of required fields + Remainder = has_required_fields_int(Arg, RequiredFields), + + % collect the remaining fields across all children + Acc ++ Remainder + end, [], Args), + + % remove duplicate fields + Remainder1 = lists:usort(Remainder0), + has_required_fields_int(Rest, Remainder1); + +% Handle $and operator where it has peers. Required fields +% can be covered by any child. +has_required_fields_int([{[{<<"$and">>, Args}]} | Rest], RequiredFields) when is_list(Args) -> - has_required_fields(Args, RequiredFields); + Remainder = has_required_fields_int(Args, RequiredFields), + has_required_fields_int(Rest, Remainder); -has_required_fields([{[{Field, Cond}]} | Rest], RequiredFields) -> +has_required_fields_int([{[{Field, Cond}]} | Rest], RequiredFields) -> case Cond of % $exists:false is a special case - this is the only operator % that explicitly does not require a field to exist {[{<<"$exists">>, false}]} -> - has_required_fields(Rest, RequiredFields); + has_required_fields_int(Rest, RequiredFields); _ -> - has_required_fields(Rest, lists:delete(Field, RequiredFields)) + has_required_fields_int(Rest, lists:delete(Field, RequiredFields)) end. @@ -651,6 +678,28 @@ has_required_fields_and_true_test() -> Normalized = normalize(Selector), ?assertEqual(true, has_required_fields(Normalized, RequiredFields)). +has_required_fields_nested_and_true_test() -> + RequiredFields = [<<"A">>, <<"B">>], + Selector1 = {[{<<"$and">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$and">>, + [ + {[{<<"B">>, <<"foo">>}]} + ] + }]}, + Selector = {[{<<"$and">>, + [ + Selector1, + Selector2 + ] + }]}, + + Normalized = normalize(Selector), + ?assertEqual(true, has_required_fields(Normalized, RequiredFields)). + has_required_fields_and_false_test() -> RequiredFields = [<<"A">>, <<"C">>], Selector = {[{<<"$and">>, @@ -662,7 +711,7 @@ has_required_fields_and_false_test() -> Normalized = normalize(Selector), ?assertEqual(false, has_required_fields(Normalized, RequiredFields)). -has_required_fields_or_test() -> +has_required_fields_or_false_test() -> RequiredFields = [<<"A">>], Selector = {[{<<"$or">>, [ @@ -673,4 +722,144 @@ has_required_fields_or_test() -> Normalized = normalize(Selector), ?assertEqual(false, has_required_fields(Normalized, RequiredFields)). --endif.
\ No newline at end of file +has_required_fields_or_true_test() -> + RequiredFields = [<<"A">>, <<"B">>, <<"C">>], + Selector = {[{<<"A">>, "foo"}, + {<<"$or">>, + [ + {[{<<"B">>, <<"bar">>}]}, + {[{<<"B">>, <<"baz">>}]} + ] + }, + {<<"C">>, "qux"} + ]}, + Normalized = normalize(Selector), + ?assertEqual(true, has_required_fields(Normalized, RequiredFields)). + +has_required_fields_and_nested_or_true_test() -> + RequiredFields = [<<"A">>, <<"B">>], + Selector1 = {[{<<"$and">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$or">>, + [ + {[{<<"B">>, <<"foo">>}]}, + {[{<<"B">>, <<"foo">>}]} + ] + }]}, + Selector = {[{<<"$and">>, + [ + Selector1, + Selector2 + ] + }]}, + Normalized = normalize(Selector), + ?assertEqual(true, has_required_fields(Normalized, RequiredFields)), + + SelectorReverse = {[{<<"$and">>, + [ + Selector2, + Selector1 + ] + }]}, + NormalizedReverse = normalize(SelectorReverse), + ?assertEqual(true, has_required_fields(NormalizedReverse, RequiredFields)). + +has_required_fields_and_nested_or_false_test() -> + RequiredFields = [<<"A">>, <<"B">>], + Selector1 = {[{<<"$and">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$or">>, + [ + {[{<<"A">>, <<"foo">>}]}, + {[{<<"B">>, <<"foo">>}]} + ] + }]}, + Selector = {[{<<"$and">>, + [ + Selector1, + Selector2 + ] + }]}, + Normalized = normalize(Selector), + ?assertEqual(false, has_required_fields(Normalized, RequiredFields)), + + SelectorReverse = {[{<<"$and">>, + [ + Selector2, + Selector1 + ] + }]}, + + NormalizedReverse = normalize(SelectorReverse), + ?assertEqual(false, has_required_fields(NormalizedReverse, RequiredFields)). + +has_required_fields_or_nested_and_true_test() -> + RequiredFields = [<<"A">>], + Selector1 = {[{<<"$and">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$and">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector = {[{<<"$or">>, + [ + Selector1, + Selector2 + ] + }]}, + Normalized = normalize(Selector), + ?assertEqual(true, has_required_fields(Normalized, RequiredFields)). + +has_required_fields_or_nested_or_true_test() -> + RequiredFields = [<<"A">>], + Selector1 = {[{<<"$or">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$or">>, + [ + {[{<<"A">>, <<"bar">>}]} + ] + }]}, + Selector = {[{<<"$or">>, + [ + Selector1, + Selector2 + ] + }]}, + Normalized = normalize(Selector), + ?assertEqual(true, has_required_fields(Normalized, RequiredFields)). + +has_required_fields_or_nested_or_false_test() -> + RequiredFields = [<<"A">>], + Selector1 = {[{<<"$or">>, + [ + {[{<<"A">>, <<"foo">>}]} + ] + }]}, + Selector2 = {[{<<"$or">>, + [ + {[{<<"B">>, <<"bar">>}]} + ] + }]}, + Selector = {[{<<"$or">>, + [ + Selector1, + Selector2 + ] + }]}, + Normalized = normalize(Selector), + ?assertEqual(false, has_required_fields(Normalized, RequiredFields)). + +-endif. |