Skip to content

Commit c0737c5

Browse files
davispgarrensmithrnewson
committed
Support partitioned queries in Mango
Co-authored-by: Garren Smith <garren.smith@gmail.com> Co-authored-by: Robert Newson <rnewson@apache.org>
1 parent d1842ad commit c0737c5

File tree

8 files changed

+174
-8
lines changed

8 files changed

+174
-8
lines changed

src/mango/src/mango_cursor.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ explain(#cursor{}=Cursor) ->
7171
{[
7272
{dbname, mango_idx:dbname(Idx)},
7373
{index, mango_idx:to_json(Idx)},
74+
{partitioned, mango_idx:partitioned(Idx)},
7475
{selector, Selector},
7576
{opts, {Opts}},
7677
{limit, Limit},

src/mango/src/mango_cursor_text.erl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ explain(Cursor) ->
7777
} = Cursor,
7878
[
7979
{'query', mango_selector_text:convert(Selector)},
80+
{partition, get_partition(Opts, null)},
8081
{sort, sort_query(Opts, Selector)}
8182
].
8283

@@ -93,6 +94,7 @@ execute(Cursor, UserFun, UserAcc) ->
9394
} = Cursor,
9495
QueryArgs = #index_query_args{
9596
q = mango_selector_text:convert(Selector),
97+
partition = get_partition(Opts, nil),
9698
sort = sort_query(Opts, Selector),
9799
raw_bookmark = true
98100
},
@@ -237,6 +239,13 @@ sort_query(Opts, Selector) ->
237239
end.
238240

239241

242+
get_partition(Opts, Default) ->
243+
case couch_util:get_value(partition, Opts) of
244+
<<>> -> Default;
245+
Else -> Else
246+
end.
247+
248+
240249
get_bookmark(Opts) ->
241250
case lists:keyfind(bookmark, 1, Opts) of
242251
{_, BM} when is_list(BM), BM /= [] ->

src/mango/src/mango_cursor_view.erl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ explain(Cursor) ->
7171
{include_docs, Args#mrargs.include_docs},
7272
{view_type, Args#mrargs.view_type},
7373
{reduce, Args#mrargs.reduce},
74+
{partition, couch_mrview_util:get_extra(Args, partition, null)},
7475
{start_key, maybe_replace_max_json(Args#mrargs.start_key)},
7576
{end_key, maybe_replace_max_json(Args#mrargs.end_key)},
7677
{direction, Args#mrargs.direction},
@@ -398,6 +399,11 @@ apply_opts([{update, false} | Rest], Args) ->
398399
update = false
399400
},
400401
apply_opts(Rest, NewArgs);
402+
apply_opts([{partition, <<>>} | Rest], Args) ->
403+
apply_opts(Rest, Args);
404+
apply_opts([{partition, Partition} | Rest], Args) when is_binary(Partition) ->
405+
NewArgs = couch_mrview_util:set_extra(Args, partition, Partition),
406+
apply_opts(Rest, NewArgs);
401407
apply_opts([{_, _} | Rest], Args) ->
402408
% Ignore unknown options
403409
apply_opts(Rest, Args).

src/mango/src/mango_error.erl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ info(mango_idx, {invalid_index_type, BadType}) ->
104104
<<"invalid_index">>,
105105
fmt("Invalid type for index: ~s", [BadType])
106106
};
107+
info(mango_idx, {partitoned_option_mismatch, BadDDoc}) ->
108+
{
109+
400,
110+
<<"invalid_partitioned_option">>,
111+
fmt("Requested partitioned option does not match existing value on"
112+
" design document ~s", [BadDDoc])
113+
};
107114
info(mango_idx, invalid_query_ddoc_language) ->
108115
{
109116
400,

src/mango/src/mango_httpd.erl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ handle_index_req(#httpd{path_parts=[_, _, _DDocId0, _Type, _Name]}=Req, _Db) ->
170170

171171
handle_explain_req(#httpd{method='POST'}=Req, Db) ->
172172
chttpd:validate_ctype(Req, "application/json"),
173-
{ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)),
173+
Body = maybe_set_partition(Req),
174+
{ok, Opts0} = mango_opts:validate_find(Body),
174175
{value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
175176
Resp = mango_crud:explain(Db, Sel, Opts),
176177
chttpd:send_json(Req, Resp);
@@ -181,7 +182,8 @@ handle_explain_req(Req, _Db) ->
181182

182183
handle_find_req(#httpd{method='POST'}=Req, Db) ->
183184
chttpd:validate_ctype(Req, "application/json"),
184-
{ok, Opts0} = mango_opts:validate_find(chttpd:json_body_obj(Req)),
185+
Body = maybe_set_partition(Req),
186+
{ok, Opts0} = mango_opts:validate_find(Body),
185187
{value, {selector, Sel}, Opts} = lists:keytake(selector, 1, Opts0),
186188
{ok, Resp0} = start_find_resp(Req),
187189
{ok, AccOut} = run_find(Resp0, Db, Sel, Opts),
@@ -224,6 +226,23 @@ get_idx_del_opts(Req) ->
224226
end.
225227

226228

229+
maybe_set_partition(Req) ->
230+
{Props} = chttpd:json_body_obj(Req),
231+
case chttpd:qs_value(Req, "partition", undefined) of
232+
undefined ->
233+
{Props};
234+
Partition ->
235+
case couch_util:get_value(<<"partition">>, Props) of
236+
undefined ->
237+
{[{<<"partition">>, ?l2b(Partition)} | Props]};
238+
Partition ->
239+
{Props};
240+
OtherPartition ->
241+
?MANGO_ERROR({bad_partition, OtherPartition})
242+
end
243+
end.
244+
245+
227246
convert_to_design_id(DDocId) ->
228247
case DDocId of
229248
<<"_design/", _/binary>> -> DDocId;

src/mango/src/mango_idx.erl

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
name/1,
3434
type/1,
3535
def/1,
36+
partitioned/1,
3637
opts/1,
3738
columns/1,
3839
is_usable/3,
@@ -59,8 +60,10 @@ list(Db) ->
5960

6061
get_usable_indexes(Db, Selector, Opts) ->
6162
ExistingIndexes = mango_idx:list(Db),
62-
63-
GlobalIndexes = mango_cursor:remove_indexes_with_partial_filter_selector(ExistingIndexes),
63+
MatchingPartitionIndexes = filter_partition_indexes(ExistingIndexes, Opts),
64+
GlobalIndexes = mango_cursor:remove_indexes_with_partial_filter_selector(
65+
MatchingPartitionIndexes
66+
),
6467
UserSpecifiedIndex = mango_cursor:maybe_filter_indexes_by_ddoc(ExistingIndexes, Opts),
6568
UsableIndexes0 = lists:usort(GlobalIndexes ++ UserSpecifiedIndex),
6669

@@ -110,6 +113,7 @@ new(Db, Opts) ->
110113
name = IdxName,
111114
type = Type,
112115
def = Def,
116+
partitioned = get_idx_partitioned(Opts),
113117
opts = filter_opts(Opts)
114118
}}.
115119

@@ -121,10 +125,11 @@ validate_new(Idx, Db) ->
121125

122126
add(DDoc, Idx) ->
123127
Mod = idx_mod(Idx),
124-
{ok, NewDDoc} = Mod:add(DDoc, Idx),
128+
{ok, NewDDoc1} = Mod:add(DDoc, Idx),
129+
NewDDoc2 = set_ddoc_partitioned(NewDDoc1, Idx),
125130
% Round trip through JSON for normalization
126-
Body = ?JSON_DECODE(?JSON_ENCODE(NewDDoc#doc.body)),
127-
{ok, NewDDoc#doc{body = Body}}.
131+
Body = ?JSON_DECODE(?JSON_ENCODE(NewDDoc2#doc.body)),
132+
{ok, NewDDoc2#doc{body = Body}}.
128133

129134

130135
remove(DDoc, Idx) ->
@@ -176,7 +181,8 @@ from_ddoc(Db, {Props}) ->
176181
lists:map(fun(Idx) ->
177182
Idx#idx{
178183
dbname = DbName,
179-
ddoc = DDoc
184+
ddoc = DDoc,
185+
partitioned = set_idx_partitioned(Db, Props)
180186
}
181187
end, Idxs).
182188

@@ -213,6 +219,10 @@ def(#idx{def=Def}) ->
213219
Def.
214220

215221

222+
partitioned(#idx{partitioned=Partitioned}) ->
223+
Partitioned.
224+
225+
216226
opts(#idx{opts=Opts}) ->
217227
Opts.
218228

@@ -329,6 +339,87 @@ gen_name(Idx, Opts0) ->
329339
mango_util:enc_hex(Sha).
330340

331341

342+
get_idx_partitioned(Opts) ->
343+
case proplists:get_value(partitioned, Opts) of
344+
B when is_boolean(B) ->
345+
B;
346+
default ->
347+
undefined
348+
end.
349+
350+
351+
set_ddoc_partitioned(DDoc, Idx) ->
352+
% We have to verify that the new index being added
353+
% to this design document either matches the current
354+
% ddoc's design options *or* this is a new design doc
355+
#doc{
356+
id = DDocId,
357+
revs = Revs,
358+
body = {BodyProps}
359+
} = DDoc,
360+
OldDOpts = couch_util:get_value(<<"options">>, BodyProps),
361+
OldOpt = case OldDOpts of
362+
{OldDOptProps} when is_list(OldDOptProps) ->
363+
couch_util:get_value(<<"partitioned">>, OldDOptProps);
364+
_ ->
365+
undefined
366+
end,
367+
% If new matches old we're done
368+
if Idx#idx.partitioned == OldOpt -> DDoc; true ->
369+
% If we're creating a ddoc then we can set the options
370+
case Revs == {0, []} of
371+
true when Idx#idx.partitioned /= undefined ->
372+
set_ddoc_partitioned_option(DDoc, Idx#idx.partitioned);
373+
true when Idx#idx.partitioned == undefined ->
374+
DDoc;
375+
false ->
376+
?MANGO_ERROR({partitioned_option_mismatch, DDocId})
377+
end
378+
end.
379+
380+
381+
set_ddoc_partitioned_option(DDoc, Partitioned) ->
382+
#doc{
383+
body = {BodyProps}
384+
} = DDoc,
385+
NewProps = case couch_util:get_value(<<"options">>, BodyProps) of
386+
{Existing} when is_list(Existing) ->
387+
Opt = {<<"partitioned">>, Partitioned},
388+
New = lists:keystore(<<"partitioned">>, 1, Existing, Opt),
389+
lists:keystore(<<"options">>, 1, BodyProps, {<<"options">>, New});
390+
undefined ->
391+
New = {<<"options">>, {[{<<"partitioned">>, Partitioned}]}},
392+
lists:keystore(<<"options">>, 1, BodyProps, New)
393+
end,
394+
DDoc#doc{body = {NewProps}}.
395+
396+
397+
set_idx_partitioned(Db, DDocProps) ->
398+
Default = fabric_util:is_partitioned(Db),
399+
case couch_util:get_value(<<"options">>, DDocProps) of
400+
{DesignOpts} ->
401+
case couch_util:get_value(<<"partitioned">>, DesignOpts) of
402+
P when is_boolean(P) ->
403+
P;
404+
undefined ->
405+
Default
406+
end;
407+
undefined ->
408+
Default
409+
end.
410+
411+
412+
filter_partition_indexes(Indexes, Opts) ->
413+
PFilt = case couch_util:get_value(partition, Opts) of
414+
<<>> ->
415+
fun(#idx{partitioned = P}) -> not P end;
416+
Partition when is_binary(Partition) ->
417+
fun(#idx{partitioned = P}) -> P end
418+
end,
419+
Filt = fun(Idx) -> type(Idx) == <<"special">> orelse PFilt(Idx) end,
420+
lists:filter(Filt, Indexes).
421+
422+
332423
filter_opts([]) ->
333424
[];
334425
filter_opts([{user_ctx, _} | Rest]) ->
@@ -341,6 +432,8 @@ filter_opts([{type, _} | Rest]) ->
341432
filter_opts(Rest);
342433
filter_opts([{w, _} | Rest]) ->
343434
filter_opts(Rest);
435+
filter_opts([{partitioned, _} | Rest]) ->
436+
filter_opts(Rest);
344437
filter_opts([Opt | Rest]) ->
345438
[Opt | filter_opts(Rest)].
346439

src/mango/src/mango_idx.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
name,
1717
type,
1818
def,
19+
partitioned,
1920
opts
2021
}).

src/mango/src/mango_opts.erl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
validate_sort/1,
3535
validate_fields/1,
3636
validate_bulk_delete/1,
37+
validate_partitioned/1,
3738

3839
default_limit/0
3940
]).
@@ -70,6 +71,12 @@ validate_idx_create({Props}) ->
7071
{optional, true},
7172
{default, 2},
7273
{validator, fun is_pos_integer/1}
74+
]},
75+
{<<"partitioned">>, [
76+
{tag, partitioned},
77+
{optional, true},
78+
{default, default},
79+
{validator, fun validate_partitioned/1}
7380
]}
7481
],
7582
validate(Props, Opts).
@@ -117,6 +124,12 @@ validate_find({Props}) ->
117124
{default, []},
118125
{validator, fun validate_fields/1}
119126
]},
127+
{<<"partition">>, [
128+
{tag, partition},
129+
{optional, true},
130+
{default, <<>>},
131+
{validator, fun validate_partition/1}
132+
]},
120133
{<<"r">>, [
121134
{tag, r},
122135
{optional, true},
@@ -296,6 +309,23 @@ validate_fields(Value) ->
296309
mango_fields:new(Value).
297310

298311

312+
validate_partitioned(true) ->
313+
{ok, true};
314+
validate_partitioned(false) ->
315+
{ok, false};
316+
validate_partitioned(default) ->
317+
{ok, default};
318+
validate_partitioned(Else) ->
319+
?MANGO_ERROR({invalid_partitioned_value, Else}).
320+
321+
322+
validate_partition(<<>>) ->
323+
{ok, <<>>};
324+
validate_partition(Partition) ->
325+
couch_partition:validate_partition(Partition),
326+
{ok, Partition}.
327+
328+
299329
validate_opts([], Props, Acc) ->
300330
{Props, lists:reverse(Acc)};
301331
validate_opts([{Name, Desc} | Rest], Props, Acc) ->

0 commit comments

Comments
 (0)