diff --git a/src/mrdb.erl b/src/mrdb.erl index b05dd6b..b6b186d 100644 --- a/src/mrdb.erl +++ b/src/mrdb.erl @@ -61,15 +61,17 @@ , get_batch/1 , snapshot/1 , release_snapshot/1 - , first/1 , first/2 - , next/2 , next/3 - , prev/2 , prev/3 - , last/1 , last/2 - , select/2 , select/3 + , first/1 , first/2 + , next/2 , next/3 + , prev/2 , prev/3 + , last/1 , last/2 + , select/2 , select/3 + , select_reverse/2, select_reverse/3 , select/1 - , fold/3 , fold/4 , fold/5 - , rdb_fold/4 , rdb_fold/5 - , rdb_rev_fold/4 , rdb_rev_fold/5 + , fold/3 , fold/4 , fold/5 + , fold_reverse/3 , fold_reverse/4, fold_reverse/5 + , rdb_fold/4 , rdb_fold/5 + , rdb_fold_reverse/4, rdb_fold_reverse/5 , write_info/3 , read_info/2 , read_info/1 @@ -541,7 +543,6 @@ re_throw(Cat, Err) -> throw -> throw(Err) end. - mnesia_compatible_aborts() -> mnesia_rocksdb_admin:get_cached_env(mnesia_compatible_aborts, false). @@ -662,10 +663,25 @@ maybe_tx_ctxt([#{activity := #{type := Type} = A} = C|_], R) -> inherit_ctxt(Ref, R) -> maps:merge(Ref, maps:with([snapshot, activity], R)). +%% @doc Create an iterator on table `Tab' for the duration of `Fun' +%% +%% The iterator is passed to the provided fun as `Fun(Iterator)', and is +%% closed once the fun terminates. +%% @equiv with_iterator(Tab, Fun, []) +%% @end -spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res )) -> Res. with_iterator(Tab, Fun) -> with_iterator(Tab, Fun, []). +%% @doc Create an iterator on table `Tab' with `ReadOptions' for the duration of `Fun' +%% +%% The iterator is passed to the provided fun as `Fun(Iterator)', and is +%% closed once the fun terminates. +%% +%% The iterator respects `mnesia_rocksdb' metadata, so accesses through the iterator +%% will return `{ok, Obj}' where `Obj' is the complete decoded object. +%% For rocksdb-level iterators, see {@link with_rdb_iterator/3}. +%% @end -spec with_iterator(ref_or_tab(), fun( (mrdb_iterator()) -> Res ), read_options()) -> Res. with_iterator(Tab, Fun, Opts) -> R = ensure_ref(Tab), @@ -1301,6 +1317,13 @@ select(Tab, Pat, Limit) when Limit == infinity; is_integer(Limit), Limit > 0 -> true = valid_limit(Limit), mrdb_select:select(ensure_ref(Tab), Pat, Limit). +select_reverse(Tab, Pat) -> + select_reverse(Tab, Pat, infinity). + +select_reverse(Tab, Pat, Limit) when Limit == infinity; is_integer(Limit), Limit > 0 -> + true = valid_limit(Limit), + mrdb_select:select_reverse(ensure_ref(Tab), Pat, Limit). + select(Cont) -> mrdb_select:select(Cont). @@ -1355,6 +1378,16 @@ fold(Tab, Fun, Acc, MatchSpec, Limit) -> true = valid_limit(Limit), mrdb_select:fold(ensure_ref(Tab), Fun, Acc, MatchSpec, Limit). +fold_reverse(Tab, Fun, Acc) -> + fold_reverse(Tab, Fun, Acc, [{'_', [], ['$_']}]). + +fold_reverse(Tab, Fun, Acc, MatchSpec) -> + fold_reverse(Tab, Fun, Acc, MatchSpec, infinity). + +fold_reverse(Tab, Fun, Acc, MatchSpec, Limit) -> + true = valid_limit(Limit), + mrdb_select:fold_reverse(ensure_ref(Tab), Fun, Acc, MatchSpec, Limit). + rdb_fold(Tab, Fun, Acc, Prefix) when is_function(Fun, 3) , is_binary(Prefix) -> rdb_fold(Tab, Fun, Acc, Prefix, infinity). @@ -1364,14 +1397,14 @@ rdb_fold(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3) true = valid_limit(Limit), mrdb_select:rdb_fold(ensure_ref(Tab), Fun, Acc, Prefix, Limit). -rdb_rev_fold(Tab, Fun, Acc, Prefix) when is_function(Fun, 3) +rdb_fold_reverse(Tab, Fun, Acc, Prefix) when is_function(Fun, 3) , is_binary(Prefix) -> - rdb_rev_fold(Tab, Fun, Acc, Prefix, infinity). + rdb_fold_reverse(Tab, Fun, Acc, Prefix, infinity). -rdb_rev_fold(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3) +rdb_fold_reverse(Tab, Fun, Acc, Prefix, Limit) when is_function(Fun, 3) , is_binary(Prefix) -> true = valid_limit(Limit), - mrdb_select:rdb_rev_fold(ensure_ref(Tab), Fun, Acc, Prefix, Limit). + mrdb_select:rdb_fold_reverse(ensure_ref(Tab), Fun, Acc, Prefix, Limit). valid_limit(L) -> case L of diff --git a/src/mrdb_select.erl b/src/mrdb_select.erl index fea6dca..2415746 100644 --- a/src/mrdb_select.erl +++ b/src/mrdb_select.erl @@ -3,10 +3,13 @@ -export([ select/3 %% (Ref, MatchSpec, Limit) , select/4 %% (Ref, MatchSpec, AccKeys, Limit) + , select_reverse/3 + , select_reverse/4 , select/1 %% (Cont) , fold/5 %% (Ref, Fun, Acc, MatchSpec, Limit) + , fold_reverse/5 , rdb_fold/5 %% (Ref, Fun, Acc, Prefix, Limit) - , rdb_rev_fold/5 %% (Ref, Fun, Acc, Prefix, Limit) + , rdb_fold_reverse/5 %% (Ref, Fun, Acc, Prefix, Limit) ]). -export([continuation_info/2]). @@ -26,18 +29,27 @@ , compiled_ms , limit , key_only = false % TODO: not used - , direction = forward % TODO: not used + , direction = forward }). select(Ref, MS, Limit) when is_map(Ref), is_list(MS) -> - select(Ref, MS, false, Limit). + select(Ref, MS, false, forward, Limit). -select(Ref, MS, AccKeys, Limit) +select(Ref, MS, AccKeys, Limit) -> + select(Ref, MS, AccKeys, forward, Limit). + +select_reverse(Ref, MS, Limit) -> + select(Ref, MS, false, reverse, Limit). + +select_reverse(Ref, MS, AccKeys, Limit) -> + select(Ref, MS, AccKeys, reverse, Limit). + +select(Ref, MS, AccKeys, Dir, Limit) when is_map(Ref), is_list(MS), is_boolean(AccKeys) -> - Sel = mk_sel(Ref, MS, Limit), + Sel = mk_sel(Ref, MS, Dir, Limit), mrdb:with_rdb_iterator(Ref, fun(I) -> i_select(I, Sel, AccKeys, []) end). -mk_sel(#{name := Tab} = Ref, MS, Limit) -> +mk_sel(#{name := Tab} = Ref, MS, Dir, Limit) -> Keypat = keypat(MS, keypos(Tab), Ref), #sel{tab = Tab, ref = Ref, @@ -45,6 +57,7 @@ mk_sel(#{name := Tab} = Ref, MS, Limit) -> ms = MS, compiled_ms = ets:match_spec_compile(MS), key_only = needs_key_only(MS), + direction = Dir, limit = Limit}. select(Cont) -> @@ -65,6 +78,12 @@ continuation_info_(direction, #sel{direction = Dir}) -> Dir; continuation_info_(_, _) -> undefined. fold(Ref, Fun, Acc, MS, Limit) -> + do_fold(Ref, Fun, Acc, MS, forward, Limit). + +fold_reverse(Ref, Fun, Acc, MS, Limit) -> + do_fold(Ref, Fun, Acc, MS, reverse, Limit). + +do_fold(Ref, Fun, Acc, MS, Dir, Limit) -> {AccKeys, F} = if is_function(Fun, 3) -> {true, fun({K, Obj}, Acc1) -> @@ -75,7 +94,7 @@ fold(Ref, Fun, Acc, MS, Limit) -> true -> mrdb:abort(invalid_fold_fun) end, - fold_(select(Ref, MS, AccKeys, Limit), F, Acc). + fold_(select(Ref, MS, AccKeys, Dir, Limit), F, Acc). fold_('$end_of_table', _, Acc) -> Acc; @@ -91,27 +110,29 @@ rdb_fold(Ref, Fun, Acc, Prefix, Limit) -> i_rdb_fold(MovRes, I, Prefix, Fun, Acc, Limit) end). -rdb_rev_fold(Ref, Fun, Acc, Prefix, Limit) -> +rdb_fold_reverse(Ref, Fun, Acc, Prefix, Limit) -> mrdb:with_rdb_iterator( Ref, fun(I) -> MovRes = rev_init_seek(I, Prefix), - i_rdb_rev_fold(MovRes, I, Prefix, Fun, Acc, Limit) + i_rdb_fold_reverse(MovRes, I, Prefix, Fun, Acc, Limit) end). -fwd_init_seek(I, <<>>) -> - rocksdb:iterator_move(I, first); -fwd_init_seek(I, Prefix) -> - rocksdb:iterator_move(I, {seek, Prefix}). +fwd_init_seek(I, Pfx) -> + rocksdb:iterator_move(I, fwd_init_seek_tgt(Pfx)). -rev_init_seek(I, <<>>) -> - rocksdb:iterator_move(I, last); -rev_init_seek(I, Prefix) -> - Tgt = case incr_prefix(Prefix) of - last -> last; - Pfx1 when is_binary(Pfx1) -> - {seek, Pfx1} - end, - rocksdb:iterator_move(I, Tgt). +fwd_init_seek_tgt(<<>> ) -> first; +fwd_init_seek_tgt(Prefix) -> {seek, Prefix}. + +rev_init_seek(I, Pfx) -> + rocksdb:iterator_move(I, rev_init_seek_tgt(Pfx)). + +rev_init_seek_tgt(<<>>) -> last; +rev_init_seek_tgt(Prefix) -> + case incr_prefix(Prefix) of + last -> last; + Pfx1 when is_binary(Pfx1) -> + {seek, Pfx1} + end. incr_prefix(<<>>) -> last; incr_prefix(Pfx) when is_binary(Pfx) -> @@ -134,31 +155,35 @@ i_rdb_fold({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 -> i_rdb_fold(_, _, _, _, Acc, _) -> Acc. -i_rdb_rev_fold({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 -> +i_rdb_fold_reverse({ok, K, V}, I, Pfx, Fun, Acc, Limit) when Limit > 0 -> case is_prefix(Pfx, K) of true -> - i_rdb_rev_fold(rocksdb:iterator_move(I, prev), I, Pfx, Fun, + i_rdb_fold_reverse(rocksdb:iterator_move(I, prev), I, Pfx, Fun, Fun(K, V, Acc), decr(Limit)); false when K > Pfx -> - i_rdb_rev_fold(rocksdb:iterator_move(I, prev), I, Pfx, Fun, Acc, Limit); + i_rdb_fold_reverse(rocksdb:iterator_move(I, prev), I, Pfx, Fun, Acc, Limit); false -> Acc end; -i_rdb_rev_fold(_, _, _, _, Acc, _) -> +i_rdb_fold_reverse(_, _, _, _, Acc, _) -> Acc. i_select(I, #sel{ keypat = Pfx , compiled_ms = MS , limit = Limit - , ref = #{vsn := Vsn, encoding := Enc} } = Sel, AccKeys, Acc) -> - StartKey = case {Pfx, Vsn, Enc} of - {<<>>, 1, {sext, _}} -> - <>; - {_, _, {term, _}} -> - <<>>; - _ -> - Pfx - end, + , direction = Dir + , ref = #{encoding := Enc} } = Sel0, AccKeys, Acc) -> + {StartKey, Sel} = case Enc of + {term, _} -> + %% No defined ordering - do forward select + {first, Sel0#sel{direction = forward}}; + _ -> + SK = case Dir of + forward -> fwd_init_seek_tgt(Pfx); + reverse -> rev_init_seek_tgt(Pfx) + end, + {SK, Sel0} + end, select_traverse(rocksdb:iterator_move(I, StartKey), Limit, Pfx, MS, I, Sel, AccKeys, Acc). @@ -236,7 +261,7 @@ map_vars([H|T], P) -> map_vars([], _) -> []. -select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel, +select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R, direction = Dir} = Sel, AccKeys, Acc) -> case is_prefix(Pfx, K) of true -> @@ -245,7 +270,7 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel, case ets:match_spec_run([Rec], MS) of [] -> select_traverse( - rocksdb:iterator_move(I, next), Limit, Pfx, MS, + rocksdb:iterator_move(I, next_or_prev(Dir)), Limit, Pfx, MS, I, Sel, AccKeys, Acc); [Match] -> Acc1 = if AccKeys -> @@ -255,6 +280,9 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel, end, traverse_continue(K, decr(Limit), Pfx, MS, I, Sel, AccKeys, Acc1) end; + false when Dir == reverse, K > Pfx -> + select_traverse(rocksdb:iterator_move(I, prev), Limit, Pfx, MS, + I, Sel, AccKeys, Acc); false when Limit == infinity -> lists:reverse(Acc); false -> @@ -263,6 +291,9 @@ select_traverse({ok, K, V}, Limit, Pfx, MS, I, #sel{ref = R} = Sel, select_traverse({error, _}, Limit, _, _, _, _, _, Acc) -> select_return(Limit, {lists:reverse(Acc), '$end_of_table'}). +next_or_prev(forward) -> next; +next_or_prev(reverse) -> prev. + select_return(infinity, {L, '$end_of_table'}) -> L; select_return(_, Ret) -> @@ -282,29 +313,34 @@ decr(I) when is_integer(I) -> decr(infinity) -> infinity. -traverse_continue(K, 0, Pfx, MS, _I, #sel{limit = Limit, ref = Ref} = Sel, AccKeys, Acc) -> +traverse_continue(K, 0, Pfx, MS, _I, #sel{limit = Limit, ref = Ref, direction = Dir} = Sel, AccKeys, Acc) -> {lists:reverse(Acc), fun(sel) -> Sel; (cont) -> mrdb:with_rdb_iterator( Ref, fun(NewI) -> - select_traverse(iterator_next(NewI, K), + select_traverse(iterator_next(NewI, K, Dir), Limit, Pfx, MS, NewI, Sel, AccKeys, []) end) end}; -traverse_continue(_K, Limit, Pfx, MS, I, Sel, AccKeys, Acc) -> - select_traverse(rocksdb:iterator_move(I, next), Limit, Pfx, MS, I, Sel, AccKeys, Acc). +traverse_continue(_K, Limit, Pfx, MS, I, #sel{direction = Dir} = Sel, AccKeys, Acc) -> + select_traverse(rocksdb:iterator_move(I, next_or_prev(Dir)), Limit, Pfx, MS, I, Sel, AccKeys, Acc). -iterator_next(I, K) -> - case rocksdb:iterator_move(I, K) of +iterator_next(I, K, Dir) -> + case i_move(I, K, Dir) of {ok, K, _} -> - rocksdb:iterator_move(I, next); + rocksdb:iterator_move(I, next_or_prev(Dir)); Other -> Other end. +i_move(I, K, reverse) -> + rocksdb:iterator_move(I, {seek_for_prev, K}); +i_move(I, K, forward) -> + rocksdb:iterator_move(I, K). + keypat([H|T], KeyPos, Ref) -> keypat(T, KeyPos, Ref, keypat_pfx(H, KeyPos, Ref)). diff --git a/test/mrdb_fold_SUITE.erl b/test/mrdb_fold_SUITE.erl index 6d36358..b2d330f 100644 --- a/test/mrdb_fold_SUITE.erl +++ b/test/mrdb_fold_SUITE.erl @@ -12,15 +12,26 @@ , end_per_testcase/2 ]). --export([ no_prefix_fwd_rdb_fold/1 +-export([ + no_prefix_fwd_rdb_fold/1 , prefixed_fwd_rdb_fold/1 , no_prefix_rev_rdb_fold/1 , prefixed_rev_rdb_fold/1 , prefixed_rev_rdb_fold_max/1 + , fwd_fold/1 + , filtered_fwd_fold/1 + , rev_fold/1 + , filtered_rev_fold/1 + , fwd_select/1 + , rev_select/1 + , select_cont/1 + , rev_select_cont/1 ]). -include_lib("common_test/include/ct.hrl"). +-record(r, {k, v}). + suite() -> []. @@ -29,26 +40,37 @@ all() -> groups() -> [ - {all_tests, [sequence], [ {group, rdb_fwd_fold} - , {group, rdb_rev_fold} ]} - , {rdb_fwd_fold, [sequence], [ no_prefix_fwd_rdb_fold - , prefixed_fwd_rdb_fold ]} - , {rdb_rev_fold, [sequence], [ no_prefix_rev_rdb_fold - , prefixed_rev_rdb_fold - , prefixed_rev_rdb_fold_max ]} + {all_tests, [sequence], [ {group, rdb_fold} + , {group, fold} + , {group, select} ]} + , {rdb_fold, [sequence], [ no_prefix_fwd_rdb_fold + , prefixed_fwd_rdb_fold + , no_prefix_rev_rdb_fold + , prefixed_rev_rdb_fold + , prefixed_rev_rdb_fold_max ]} + , {fold, [sequence], [ fwd_fold + , filtered_fwd_fold + , rev_fold + , filtered_rev_fold ]} + , {select, [sequence], [ fwd_select + , rev_select + , select_cont + , rev_select_cont ]} ]. init_per_suite(Config) -> + mnesia:stop(), + ok = mnesia_rocksdb_tlib:start_mnesia(reset), Config. end_per_suite(_Config) -> + mnesia:stop(), ok. -init_per_group(G, Config) -> - mnesia:stop(), - ok = mnesia_rocksdb_tlib:start_mnesia(reset), - create_tab(t, G, [{type, ordered_set}]), - fill_tab(t), +init_per_group(G, Config) when G==rdb_fold; G==fold -> + mk_tab(G), + Config; +init_per_group(_, Config) -> Config. end_per_group(_, _Config) -> @@ -60,62 +82,139 @@ init_per_testcase(_, Config) -> end_per_testcase(_, _Config) -> ok. -create_tab(Tab, Grp, Opts) -> - GOpts = case Grp of - _ when Grp==rdb_fwd_fold; Grp==rdb_rev_fold -> - [{user_properties, - [{mrdb_encoding, {raw, raw}}]}]; - _ -> [] - end, - {atomic, ok} = mnesia:create_table(Tab, [{rdb, [node()]} | GOpts ++ Opts]), - ok. +mk_tab(G) -> + T = tab_name(G), + case create_tab(T) of + {atomic, ok} -> fill_tab(T); + {aborted,{already_exists,T}} -> ok + end. + +tab_name(fold ) -> r; +tab_name(select ) -> r; +tab_name(rdb_fold) -> t. + +create_tab(T) -> + Opts = tab_opts(T), + mnesia:create_table(T, [{rdb, [node()]} | Opts]). + +tab_opts(r) -> + [{attributes, [k, v]}, {type, ordered_set}]; +tab_opts(t) -> + [{attributes, [k, v]}, {user_properties, [{mrdb_encoding, {raw, raw}}]}]. no_prefix_fwd_rdb_fold(_Config) -> Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], <<>>, infinity), + Expected = lists:reverse(raw_objs()), + {Res, Res} = {Res, Expected}. + +fwd_fold(_Config) -> + Res = mrdb:fold(r, fun simple_acc/2, []), Expected = lists:reverse(objs()), {Res, Res} = {Res, Expected}. prefixed_fwd_rdb_fold(_Config) -> Pfx = <<"aa">>, Res = mrdb:rdb_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity), - Expected = lists:reverse(prefixed_objs(Pfx)), + Expected = lists:reverse(prefixed_raw_objs(Pfx)), + {Res, Res} = {Res, Expected}. + +filtered_fwd_fold(_Config) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Res = mrdb:fold(r, fun simple_acc/2, [], MS, infinity), + Expected = lists:reverse(filtered_objs(MS)), {Res, Res} = {Res, Expected}. no_prefix_rev_rdb_fold(_Config) -> - Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], <<>>, infinity), + Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], <<>>, infinity), + Expected = raw_objs(), + {Res, Res} = {Res, Expected}. + +rev_fold(_Config) -> + Res = mrdb:fold_reverse(r, fun simple_acc/2, []), Expected = objs(), {Res, Res} = {Res, Expected}. prefixed_rev_rdb_fold(_Config) -> Pfx = <<"aa">>, - Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity), - Expected = prefixed_objs(Pfx), + Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], Pfx, infinity), + Expected = prefixed_raw_objs(Pfx), + {Res, Res} = {Res, Expected}. + +filtered_rev_fold(_Config) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Res = mrdb:fold_reverse(r, fun simple_acc/2, [], MS, infinity), + Expected = filtered_objs(MS), {Res, Res} = {Res, Expected}. prefixed_rev_rdb_fold_max(_Config) -> Pfx = <<255,255>>, - Res = mrdb:rdb_rev_fold(t, fun simple_rdb_acc/3, [], Pfx, infinity), - Expected = prefixed_objs(Pfx), + Res = mrdb:rdb_fold_reverse(t, fun simple_rdb_acc/3, [], Pfx, infinity), + Expected = prefixed_raw_objs(Pfx), {Res, Res} = {Res, Expected}. +fwd_select(_Config) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Res = mrdb:select(r, MS), + Expected = filtered_objs(MS), + {Res, Res} = {Res, Expected}. + +rev_select(_Config) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Res = mrdb:select_reverse(r, MS), + Expected = lists:reverse(filtered_objs(MS)), + {Res, Res} = {Res, Expected}. + +select_cont(_Cont) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Expected = filtered_objs(MS), + select_cont_loop(mrdb:select(r, MS, 1), Expected). + +rev_select_cont(_Cont) -> + MS = [{#r{k = {a,b,'_'}, v = '_'}, [], ['$_']}], + Expected = lists:reverse(filtered_objs(MS)), + select_cont_loop(mrdb:select_reverse(r, MS, 1), Expected). + +select_cont_loop({[Obj], Cont}, [Obj|Rest]) -> + select_cont_loop(mrdb:select(Cont), Rest); +select_cont_loop({[], '$end_of_table'}, []) -> + ok. + +simple_acc(#r{} = Obj, Acc) -> + [Obj | Acc]. + simple_rdb_acc(K, V, Acc) -> [{K,V} | Acc]. -fill_tab(Tab) -> - [mrdb:insert(Tab, {Tab, K, V}) || {K,V} <- objs()], +fill_tab(t = Tab) -> + [mrdb:insert(Tab, {Tab, K, V}) || {K,V} <- raw_objs()], + ok; +fill_tab(r = Tab) -> + [mrdb:insert(Tab, Obj) || Obj <- objs()], ok. -prefixed_objs(Pfx) -> - [Obj || {K,_} = Obj <- objs(), +prefixed_raw_objs(Pfx) -> + [Obj || {K,_} = Obj <- raw_objs(), is_prefix(Pfx, K)]. -objs() -> +raw_objs() -> [ {<<"aa">> , <<"1">>} , {<<"aa1">>, <<"2">>} , {<<"ab">> , <<"3">>} , {<<"ab1">>, <<"4">>} , {<<255,255>>, <<"5">>} ]. +filtered_objs(MS) -> + MSC = ets:match_spec_compile(MS), + ets:match_spec_run(objs(), MSC). + +objs() -> + [ #r{k = {a,a,1}, v = 1} + , #r{k = {a,b,2}, v = 2} + , #r{k = {a,b,3}, v = 3} + , #r{k = {a,c,4}, v = 4} + , #r{k = {b,b,5}, v = 5} + ]. + %% copied from mrdb_select.erl is_prefix(A, B) when is_binary(A), is_binary(B) -> Sa = byte_size(A),