summaryrefslogtreecommitdiff
path: root/src/mango/src/mango_selector.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mango/src/mango_selector.erl')
-rw-r--r--src/mango/src/mango_selector.erl225
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.