@@ -85,6 +85,13 @@ match_int(Selector, D, Verbose) ->
8585% Convert each operator into a normalized version as well
8686% as convert an implicit operators into their explicit
8787% versions.
88+ % {$Op: {$data: Path}}
89+ norm_ops ({[{<<" $" , _ /binary >>, {[{<<" $data" >>, Path }]}}]} = Cond ) when is_binary (Path ) ->
90+ norm_data (Cond );
91+ % {Field: {$data: Path}}
92+ norm_ops ({[{Field , {[{<<" $data" >>, Path }]}}]}) when is_binary (Path ) ->
93+ Eq = norm_data ({[{<<" $eq" >>, {[{<<" $data" >>, Path }]}}]}),
94+ {[{Field , Eq }]};
8895norm_ops ({[{<<" $and" >>, Args }]}) when is_list (Args ) ->
8996 {[{<<" $and" >>, [norm_ops (A ) || A <- Args ]}]};
9097norm_ops ({[{<<" $and" >>, Arg }]}) ->
@@ -128,8 +135,8 @@ norm_ops({[{<<"$regex">>, Regex}]} = Cond) when is_binary(Regex) ->
128135 _ ->
129136 ? MANGO_ERROR ({bad_arg , '$regex' , Regex })
130137 end ;
131- norm_ops ({[{<<" $all" >>, Args }]}) when is_list (Args ) ->
132- {[{<< " $all " >>, Args }]} ;
138+ norm_ops ({[{<<" $all" >>, Args }]} = Cond ) when is_list (Args ) ->
139+ Cond ;
133140norm_ops ({[{<<" $all" >>, Arg }]}) ->
134141 ? MANGO_ERROR ({bad_arg , '$all' , Arg });
135142norm_ops ({[{<<" $elemMatch" >>, {_ } = Arg }]}) ->
@@ -144,8 +151,8 @@ norm_ops({[{<<"$keyMapMatch">>, {_} = Arg}]}) ->
144151 {[{<<" $keyMapMatch" >>, norm_ops (Arg )}]};
145152norm_ops ({[{<<" $keyMapMatch" >>, Arg }]}) ->
146153 ? MANGO_ERROR ({bad_arg , '$keyMapMatch' , Arg });
147- norm_ops ({[{<<" $size" >>, Arg }]}) when is_integer (Arg ), Arg >= 0 ->
148- {[{<< " $size " >>, Arg }]} ;
154+ norm_ops ({[{<<" $size" >>, Arg }]} = Cond ) when is_integer (Arg ), Arg >= 0 ->
155+ Cond ;
149156norm_ops ({[{<<" $size" >>, Arg }]}) ->
150157 ? MANGO_ERROR ({bad_arg , '$size' , Arg });
151158norm_ops ({[{<<" $text" >>, Arg }]}) when
@@ -205,6 +212,37 @@ norm_ops({[_, _ | _] = Props}) ->
205212norm_ops (Value ) ->
206213 {[{<<" $eq" >>, Value }]}.
207214
215+ % {$data: Path} may only be used as an argument to "leaf" operators that expect
216+ % a literal value as input. If it were combined with combinators like $and or
217+ % $allMatch it would allow the input document to inject its own selectors.
218+ -define (DATA_OPS , [
219+ <<" $eq" >>,
220+ <<" $ne" >>,
221+ <<" $lt" >>,
222+ <<" $lte" >>,
223+ <<" $gt" >>,
224+ <<" $gte" >>,
225+ <<" $in" >>,
226+ <<" $nin" >>,
227+ <<" $all" >>,
228+ <<" $type" >>,
229+ <<" $size" >>,
230+ <<" $mod" >>,
231+ <<" $regex" >>,
232+ <<" $beginsWith" >>
233+ ]).
234+
235+ norm_data ({[{Op , {[{<<" $data" >>, Field }]}}]}) when is_binary (Field ) ->
236+ case lists :member (Op , ? DATA_OPS ) of
237+ true ->
238+ {ok , Path } = mango_util :parse_field (Field , relative ),
239+ {[{Op , {[{<<" $data" >>, Path }]}}]};
240+ false ->
241+ ? MANGO_ERROR ({bad_arg , '$data' , Op })
242+ end ;
243+ norm_data ({[{_ , _ }]} = Cond ) ->
244+ Cond .
245+
208246% This takes a selector and normalizes all of the
209247% field names as far as possible. For instance:
210248%
@@ -899,6 +937,94 @@ fields({[]}) ->
899937 ]}
900938).
901939
940+ normalize_data_basic_test () ->
941+ Selector = normalize ({[{<<" a" >>, {[{<<" $data" >>, <<" b" >>}]}}]}),
942+ ? assertEqual (
943+ {[
944+ {
945+ <<" a" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [<<" b" >>]}]}}]}
946+ }
947+ ]},
948+ Selector
949+ ).
950+
951+ normalize_data_path_test () ->
952+ Selector = normalize ({[{<<" a" >>, {[{<<" $data" >>, <<" b.c.42.d" >>}]}}]}),
953+ ? assertEqual (
954+ {[
955+ {
956+ <<" a" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [<<" b" >>, <<" c" >>, <<" 42" >>, <<" d" >>]}]}}]}
957+ }
958+ ]},
959+ Selector
960+ ).
961+
962+ normalize_data_sibling_test () ->
963+ Selector = normalize ({[{<<" a" >>, {[{<<" $data" >>, <<" .b" >>}]}}]}),
964+ ? assertEqual (
965+ {[
966+ {
967+ <<" a" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [{[{<<" parent" >>, 1 }]}, <<" b" >>]}]}}]}
968+ }
969+ ]},
970+ Selector
971+ ).
972+
973+ normalize_data_parent_test () ->
974+ Selector = normalize ({[{<<" a" >>, {[{<<" $data" >>, <<" ..b" >>}]}}]}),
975+ ? assertEqual (
976+ {[
977+ {
978+ <<" a" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [{[{<<" parent" >>, 2 }]}, <<" b" >>]}]}}]}
979+ }
980+ ]},
981+ Selector
982+ ).
983+
984+ normalize_data_allowed_operator_test () ->
985+ Selector = normalize ({[{<<" a" >>, {[{<<" $lt" >>, {[{<<" $data" >>, <<" b" >>}]}}]}}]}),
986+ ? assertEqual (
987+ {[
988+ {
989+ <<" a" >>, {[{<<" $lt" >>, {[{<<" $data" >>, [<<" b" >>]}]}}]}
990+ }
991+ ]},
992+ Selector
993+ ).
994+
995+ normalize_data_allowed_operator_all_test () ->
996+ Selector = normalize ({[{<<" a" >>, {[{<<" $all" >>, {[{<<" $data" >>, <<" b" >>}]}}]}}]}),
997+ ? assertEqual (
998+ {[
999+ {
1000+ <<" a" >>, {[{<<" $all" >>, {[{<<" $data" >>, [<<" b" >>]}]}}]}
1001+ }
1002+ ]},
1003+ Selector
1004+ ).
1005+
1006+ normalize_data_disallowed_operator_test () ->
1007+ Selector = {[{<<" a" >>, {[{<<" $allMatch" >>, {[{<<" $data" >>, <<" b" >>}]}}]}}]},
1008+ Error = {mango_error , mango_selector , {bad_arg , '$data' , <<" $allMatch" >>}},
1009+ ? assertException (throw , Error , normalize (Selector )).
1010+
1011+ normalize_data_multi_field_test () ->
1012+ Selector = normalize (
1013+ {[
1014+ {<<" a" >>, {[{<<" $data" >>, <<" c" >>}]}},
1015+ {<<" b" >>, {[{<<" $data" >>, <<" c" >>}]}}
1016+ ]}
1017+ ),
1018+ ? assertEqual (
1019+ {[
1020+ {<<" $and" >>, [
1021+ {[{<<" a" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [<<" c" >>]}]}}]}}]},
1022+ {[{<<" b" >>, {[{<<" $eq" >>, {[{<<" $data" >>, [<<" c" >>]}]}}]}}]}
1023+ ]}
1024+ ]},
1025+ Selector
1026+ ).
1027+
9021028is_constant_field_basic_test () ->
9031029 Selector = normalize ({[{<<" A" >>, <<" foo" >>}]}),
9041030 Field = <<" A" >>,
0 commit comments