Skip to content

Commit a91592f

Browse files
committed
fix: Hide the format of selector fields inside mango_selector
In the previous commit we changed lots of code to deal with normalized selector fields now being per-parsed lists, not binaries, i.e. instead of `<<"a.b">>` a field now looks like `[<<"a">>, <<"b">>]`. This commit winds most of that back and instead makes the functions in `mango_selector` hide the fact the field is now pre-parsed from callers. So for example, the code that deals with matching selectors to indexes can continue to pass the unparsed form into `mango_selector` functions, and `mango_selector` will take care of parsing/serializing inputs as necessary.
1 parent 4a6fc5b commit a91592f

7 files changed

Lines changed: 94 additions & 105 deletions

src/mango/src/mango_cursor.erl

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,8 @@ extract_selector_hints(Selector) ->
286286
UnindexableFields = sets:subtract(AllFields, IndexableFields),
287287
{[
288288
{type, IndexType},
289-
{indexable_fields,
290-
lists:sort([mango_util:join_field(F) || F <- sets:to_list(IndexableFields)])},
291-
{unindexable_fields,
292-
lists:sort([mango_util:join_field(F) || F <- sets:to_list(UnindexableFields)])}
289+
{indexable_fields, lists:sort(sets:to_list(IndexableFields))},
290+
{unindexable_fields, lists:sort(sets:to_list(UnindexableFields))}
293291
]}
294292
end,
295293
lists:map(Populate, Modules).
@@ -1114,56 +1112,56 @@ extract_selector_hints_test_() ->
11141112
t_extract_selector_hints_view(_) ->
11151113
meck:expect(dreyfus, available, [], meck:val(false)),
11161114
meck:expect(nouveau, enabled, [], meck:val(false)),
1117-
meck:expect(mango_selector, fields, [selector], meck:val([["field1"], ["field2"], ["field3"]])),
1118-
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val([["field2"]])),
1115+
meck:expect(mango_selector, fields, [selector], meck:val(["field1", "field2", "field3"])),
1116+
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val(["field2"])),
11191117
Hints =
11201118
[
11211119
{[
11221120
{type, json},
1123-
{indexable_fields, [<<"field2">>]},
1124-
{unindexable_fields, [<<"field1">>, <<"field3">>]}
1121+
{indexable_fields, ["field2"]},
1122+
{unindexable_fields, ["field1", "field3"]}
11251123
]}
11261124
],
11271125
?assertEqual(Hints, extract_selector_hints(selector)).
11281126

11291127
t_extract_selector_hints_text(_) ->
11301128
meck:expect(dreyfus, available, [], meck:val(true)),
11311129
meck:expect(nouveau, enabled, [], meck:val(false)),
1132-
meck:expect(mango_selector, fields, [selector], meck:val([["field1"], ["field2"], ["field3"]])),
1133-
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val([["field2"]])),
1134-
meck:expect(mango_idx_text, indexable_fields, [selector], meck:val([["field1"]])),
1130+
meck:expect(mango_selector, fields, [selector], meck:val(["field1", "field2", "field3"])),
1131+
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val(["field2"])),
1132+
meck:expect(mango_idx_text, indexable_fields, [selector], meck:val(["field1"])),
11351133
Hints =
11361134
[
11371135
{[
11381136
{type, json},
1139-
{indexable_fields, [<<"field2">>]},
1140-
{unindexable_fields, [<<"field1">>, <<"field3">>]}
1137+
{indexable_fields, ["field2"]},
1138+
{unindexable_fields, ["field1", "field3"]}
11411139
]},
11421140
{[
11431141
{type, text},
1144-
{indexable_fields, [<<"field1">>]},
1145-
{unindexable_fields, [<<"field2">>, <<"field3">>]}
1142+
{indexable_fields, ["field1"]},
1143+
{unindexable_fields, ["field2", "field3"]}
11461144
]}
11471145
],
11481146
?assertEqual(Hints, extract_selector_hints(selector)).
11491147

11501148
t_extract_selector_hints_nouveau(_) ->
11511149
meck:expect(dreyfus, available, [], meck:val(false)),
11521150
meck:expect(nouveau, enabled, [], meck:val(true)),
1153-
meck:expect(mango_selector, fields, [selector], meck:val([["field1"], ["field2"], ["field3"]])),
1154-
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val([["field2"]])),
1155-
meck:expect(mango_idx_nouveau, indexable_fields, [selector], meck:val([["field1"]])),
1151+
meck:expect(mango_selector, fields, [selector], meck:val(["field1", "field2", "field3"])),
1152+
meck:expect(mango_idx_view, indexable_fields, [selector], meck:val(["field2"])),
1153+
meck:expect(mango_idx_nouveau, indexable_fields, [selector], meck:val(["field1"])),
11561154
Hints =
11571155
[
11581156
{[
11591157
{type, json},
1160-
{indexable_fields, [<<"field2">>]},
1161-
{unindexable_fields, [<<"field1">>, <<"field3">>]}
1158+
{indexable_fields, ["field2"]},
1159+
{unindexable_fields, ["field1", "field3"]}
11621160
]},
11631161
{[
11641162
{type, nouveau},
1165-
{indexable_fields, [<<"field1">>]},
1166-
{unindexable_fields, [<<"field2">>, <<"field3">>]}
1163+
{indexable_fields, ["field1"]},
1164+
{unindexable_fields, ["field2", "field3"]}
11671165
]}
11681166
],
11691167
?assertEqual(Hints, extract_selector_hints(selector)).

src/mango/src/mango_cursor_special.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
Options :: cursor_options().
3434
create(Db, {Indexes, Trace0}, Selector, Opts) ->
3535
InitialRange = mango_idx_view:field_ranges(Selector),
36-
CatchAll = [{[<<"_id">>], {'$gt', null, '$lt', mango_json_max}}],
36+
CatchAll = [{<<"_id">>, {'$gt', null, '$lt', mango_json_max}}],
3737
% order matters here - we only want to use the catchall index
3838
% if no other range can fulfill the query (because we know)
3939
% catchall is the most expensive range

src/mango/src/mango_cursor_view.erl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ consider_index_coverage_positive_test() ->
882882
type = <<"json">>,
883883
def = {[{<<"fields">>, {[]}}]}
884884
},
885-
Fields = [[<<"_id">>]],
885+
Fields = [<<"_id">>],
886886
MRArgs =
887887
#mrargs{
888888
include_docs = true,
@@ -1010,29 +1010,29 @@ required_fields_all_fields_test() ->
10101010
?assertEqual(all_fields, required_fields(Cursor)).
10111011

10121012
required_fields_disjoint_fields_test() ->
1013-
Fields1 = [[<<"field1">>], [<<"field2">>], [<<"field3">>]],
1013+
Fields1 = [<<"field1">>, <<"field2">>, <<"field3">>],
10141014
Selector1 = to_selector(#{}),
10151015
Cursor1 = #cursor{fields = Fields1, selector = Selector1},
1016-
?assertEqual([[<<"field1">>], [<<"field2">>], [<<"field3">>]], required_fields(Cursor1)),
1017-
Fields2 = [[<<"field1">>], [<<"field2">>]],
1016+
?assertEqual([<<"field1">>, <<"field2">>, <<"field3">>], required_fields(Cursor1)),
1017+
Fields2 = [<<"field1">>, <<"field2">>],
10181018
Selector2 = to_selector(#{<<"field3">> => undefined, <<"field4">> => undefined}),
10191019
Cursor2 = #cursor{fields = Fields2, selector = Selector2},
10201020
?assertEqual(
1021-
[[<<"field1">>], [<<"field2">>], [<<"field3">>], [<<"field4">>]], required_fields(Cursor2)
1021+
[<<"field1">>, <<"field2">>, <<"field3">>, <<"field4">>], required_fields(Cursor2)
10221022
).
10231023

10241024
required_fields_overlapping_fields_test() ->
1025-
Fields1 = [[<<"field1">>], [<<"field2">>], [<<"field3">>]],
1025+
Fields1 = [<<"field1">>, <<"field2">>, <<"field3">>],
10261026
Selector1 = to_selector(#{<<"field3">> => undefined, <<"field4">> => undefined}),
10271027
Cursor1 = #cursor{fields = Fields1, selector = Selector1},
10281028
?assertEqual(
1029-
[[<<"field1">>], [<<"field2">>], [<<"field3">>], [<<"field4">>]], required_fields(Cursor1)
1029+
[<<"field1">>, <<"field2">>, <<"field3">>, <<"field4">>], required_fields(Cursor1)
10301030
),
1031-
Fields2 = [[<<"field3">>], [<<"field1">>], [<<"field2">>]],
1031+
Fields2 = [<<"field3">>, <<"field1">>, <<"field2">>],
10321032
Selector2 = to_selector(#{<<"field4">> => undefined, <<"field1">> => undefined}),
10331033
Cursor2 = #cursor{fields = Fields2, selector = Selector2},
10341034
?assertEqual(
1035-
[[<<"field1">>], [<<"field2">>], [<<"field3">>], [<<"field4">>]], required_fields(Cursor2)
1035+
[<<"field1">>, <<"field2">>, <<"field3">>, <<"field4">>], required_fields(Cursor2)
10361036
).
10371037

10381038
explain_test() ->

src/mango/src/mango_idx_special.erl

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,13 @@ to_json(#idx{def = all_docs}) ->
5555
]}.
5656

5757
columns(#idx{def = all_docs}) ->
58-
[[<<"_id">>]].
59-
60-
parse_field(Field) when is_binary(Field) ->
61-
{ok, Path} = mango_util:parse_field(Field),
62-
Path;
63-
parse_field(Field) ->
64-
Field.
58+
[<<"_id">>].
6559

6660
is_usable(#idx{def = all_docs}, _Selector, []) ->
6761
{true, #{reason => []}};
68-
is_usable(#idx{def = all_docs} = Idx, Selector, SortFields0) ->
62+
is_usable(#idx{def = all_docs} = Idx, Selector, SortFields) ->
6963
Fields = mango_idx_view:indexable_fields(Selector),
70-
SortFields = [parse_field(F) || F <- SortFields0],
71-
SelectorHasRequiredFields = lists:member([<<"_id">>], Fields),
64+
SelectorHasRequiredFields = lists:member(<<"_id">>, Fields),
7265
CanUseSort = can_use_sort(Idx, SortFields, Selector),
7366
Reason =
7467
[field_mismatch || not SelectorHasRequiredFields] ++
@@ -124,7 +117,7 @@ is_usable_test() ->
124117
SortOrderMismatch = {false, #{reason => [sort_order_mismatch]}},
125118

126119
Index = #idx{def = all_docs},
127-
?assertEqual(FieldMismatch, usable(Index, #{}, [[<<"_id">>]])),
120+
?assertEqual(FieldMismatch, usable(Index, #{}, [<<"_id">>])),
128121
?assertEqual(SortOrderMismatch, usable(Index, #{<<"_id">> => 11}, [<<"field3">>])),
129122
?assertEqual(Usable, usable(Index, #{}, [])).
130123
-endif.

src/mango/src/mango_idx_view.erl

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -111,31 +111,24 @@ to_json(Idx) ->
111111
columns(Idx) ->
112112
{Props} = Idx#idx.def,
113113
{<<"fields">>, {Fields}} = lists:keyfind(<<"fields">>, 1, Props),
114-
[parse_field(F) || {F, _} <- Fields].
115-
116-
parse_field(Field) when is_binary(Field) ->
117-
{ok, Path} = mango_util:parse_field(Field),
118-
Path;
119-
parse_field(Field) ->
120-
Field.
114+
[Key || {Key, _} <- Fields].
121115

122116
-spec is_usable(#idx{}, selector(), [field()]) -> {boolean(), rejection_details()}.
123-
is_usable(Idx, Selector, SortFields0) ->
117+
is_usable(Idx, Selector, SortFields) ->
124118
% This index is usable if all of the columns are
125119
% restricted by the selector such that they are required to exist
126120
% and the selector is not a text search (so requires a text index)
127121
RequiredFields = columns(Idx),
128122

129123
% sort fields are required to exist in the results so
130124
% we don't need to check the selector for these
131-
SortFields = [parse_field(F) || F <- SortFields0],
132125
RequiredFields1 = ordsets:subtract(lists:usort(RequiredFields), lists:usort(SortFields)),
133126

134127
% _id and _rev are implicitly in every document so
135128
% we don't need to check the selector for these either
136129
RequiredFields2 = ordsets:subtract(
137130
RequiredFields1,
138-
[[<<"_id">>], [<<"_rev">>]]
131+
[<<"_id">>, <<"_rev">>]
139132
),
140133

141134
SelectorHasRequiredFields = mango_selector:has_required_fields(Selector, RequiredFields2),
@@ -278,23 +271,26 @@ validate_ddoc(VProps) ->
278271
% the equivalent of a multi-query. But that's for another
279272
% day.
280273

274+
indexable_fields(Selector) ->
275+
[mango_util:join_field(F) || F <- indexable_paths(Selector)].
276+
281277
% We can see through '$and' trivially
282-
indexable_fields({[{<<"$and">>, Args}]}) ->
283-
lists:usort(lists:flatmap(fun(A) -> indexable_fields(A) end, Args));
278+
indexable_paths({[{<<"$and">>, Args}]}) ->
279+
lists:usort(lists:flatmap(fun(A) -> indexable_paths(A) end, Args));
284280
% So far we can't see through any other operator
285-
indexable_fields({[{<<"$", _/binary>>, _}]}) ->
281+
indexable_paths({[{<<"$", _/binary>>, _}]}) ->
286282
[];
287283
% If we have a field with a terminator that is locatable
288284
% using an index then the field is a possible index
289-
indexable_fields({[{Field, Cond}]}) ->
285+
indexable_paths({[{Field, Cond}]}) ->
290286
case indexable(Cond) of
291287
true ->
292288
[Field];
293289
false ->
294290
[]
295291
end;
296292
% An empty selector
297-
indexable_fields({[]}) ->
293+
indexable_paths({[]}) ->
298294
[].
299295

300296
% Check if a condition is indexable. The logical
@@ -327,8 +323,8 @@ indexable({[{<<"$", _/binary>>, _}]}) ->
327323

328324
% For each field, return {Field, Range}
329325
field_ranges(Selector) ->
330-
Fields = indexable_fields(Selector),
331-
field_ranges(Selector, Fields).
326+
Fields = indexable_paths(Selector),
327+
[{mango_util:join_field(F), R} || {F, R} <- field_ranges(Selector, Fields)].
332328

333329
field_ranges(Selector, Fields) ->
334330
field_ranges(Selector, Fields, []).
@@ -555,13 +551,12 @@ can_use_sort([Col | RestCols], SortFields, Selector) ->
555551
-spec covers(#idx{}, fields()) -> boolean().
556552
covers(_, all_fields) ->
557553
false;
558-
covers(Idx, Fields0) ->
554+
covers(Idx, Fields) ->
559555
case mango_idx:def(Idx) of
560556
all_docs ->
561557
false;
562558
_ ->
563-
Fields = [parse_field(F) || F <- Fields0],
564-
Available = [[<<"_id">>] | columns(Idx)],
559+
Available = [<<"_id">> | columns(Idx)],
565560
sets:is_subset(couch_util:set_from_list(Fields), couch_util:set_from_list(Available))
566561
end.
567562

@@ -576,7 +571,7 @@ indexable_fields_empty_test() ->
576571

577572
indexable_fields_and_test() ->
578573
Selector = #{<<"$and">> => [#{<<"field1">> => undefined, <<"field2">> => undefined}]},
579-
?assertEqual([[<<"field1">>], [<<"field2">>]], indexable_fields_of(Selector)).
574+
?assertEqual([<<"field1">>, <<"field2">>], indexable_fields_of(Selector)).
580575

581576
indexable_fields_or_test() ->
582577
Selector = #{<<"$or">> => [#{<<"field1">> => undefined, <<"field2">> => undefined}]},
@@ -616,31 +611,31 @@ indexable_fields_not_test() ->
616611

617612
indexable_fields_lt_test() ->
618613
Selector = #{<<"field">> => #{<<"$lt">> => undefined}},
619-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
614+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
620615

621616
indexable_fields_lte_test() ->
622617
Selector = #{<<"field">> => #{<<"$lte">> => undefined}},
623-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
618+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
624619

625620
indexable_fields_eq_test() ->
626621
Selector = #{<<"field">> => #{<<"$eq">> => undefined}},
627-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
622+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
628623

629624
indexable_fields_ne_test() ->
630625
Selector = #{<<"field">> => #{<<"$ne">> => undefined}},
631626
?assertEqual([], indexable_fields_of(Selector)).
632627

633628
indexable_fields_gte_test() ->
634629
Selector = #{<<"field">> => #{<<"$gte">> => undefined}},
635-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
630+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
636631

637632
indexable_fields_beginswith_test() ->
638633
Selector = #{<<"field">> => #{<<"$beginsWith">> => undefined}},
639-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
634+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
640635

641636
indexable_fields_gt_test() ->
642637
Selector = #{<<"field">> => #{<<"$gt">> => undefined}},
643-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
638+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
644639

645640
indexable_fields_mod_test() ->
646641
Selector = #{<<"field">> => #{<<"$mod">> => [0, 0]}},
@@ -652,7 +647,7 @@ indexable_fields_regex_test() ->
652647

653648
indexable_fields_exists_test() ->
654649
Selector = #{<<"field">> => #{<<"$exists">> => true}},
655-
?assertEqual([[<<"field">>]], indexable_fields_of(Selector)).
650+
?assertEqual([<<"field">>], indexable_fields_of(Selector)).
656651

657652
indexable_fields_type_test() ->
658653
Selector = #{<<"field">> => #{<<"$type">> => undefined}},
@@ -715,7 +710,7 @@ is_usable_test() ->
715710
?assertEqual(
716711
SortOrderMismatch,
717712
usable(Index, #{<<"field1">> => <<"value1">>, <<"field2">> => 42}, [
718-
[<<"field3">>], [<<"field4">>]
713+
<<"field3">>, <<"field4">>
719714
])
720715
),
721716
?assertEqual(

0 commit comments

Comments
 (0)