[erlang-questions] Mnesia wildcard query

99 views
Skip to first unread message

Janos Hary

unread,
May 14, 2013, 4:44:34 AM5/14/13
to erlang-q...@erlang.org

Hi,

 

Is there a way to specify a ‘begins with’ style query in Mnesia? My keys are strings, and I’d like to get all of them sharing a given prefix.

 

Thanks,

Janos

karol skocik

unread,
May 14, 2013, 6:32:09 AM5/14/13
to Janos Hary, Erlang Questions
You can use ets:fun2ms/1 to construct example match spec, which can be also used for select over mnesia table:

3> Tab = ets:new(tab, []).
20496
4> ets:insert(Tab, [{"abcdef", 1}, {"abxxxxx", 2}, {"bbbbbb", 3}]).
true
5> MatchSpec = ets:fun2ms(fun ({[$a, $b | _], V}) -> V end).
[{{[97,98|'_'],'$1'},[],['$1']}]
6> ets:select(Tab, MatchSpec).
[1,2]

ets:fun2ms/1 does not recognize literal funs like: fun ({"prefix" ++ _, V) -> V end, (++ is the problem)
so it's more convenient to write a simple wrapper function returning the MatchSpec, given the prefix.
 
Cheers, 
  Karol


_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions


Janos Hary

unread,
May 14, 2013, 12:01:23 PM5/14/13
to karol skocik, Erlang Questions

Thanks for the answer. As far as I understand it works great if the prefix is known at compile time. I wasn’t clear on this, but in my case the prefix is given in runtime.

 

So far I come up with this solution. It understands a ‘*’ character at a pattern’s end. I’d be happy to get suggestions to improve it.

 

%% finds '*' in pattern and return a minimum and maximum pattern

%% e.g. <<"hello*">> -> {<<"hello">>, <<"hellp">>}

%% if binary string X starts with 'hello' then <<"hello">> <= X < <<"hellp">>

wildcard(Pattern) when is_binary(Pattern) ->

       case binary:match(Pattern, <<"*">>) of

              nomatch ->

                     {Pattern, Pattern};

              {0, _->

                     {error, wrong_pattern};

              {S, _->

                     PatPref = binary:part(Pattern, 0, S-1),

                     io:format("~s", [PatPref]),

                     <<PatLast:8>> = binary:part(Pattern, S-1, 1),

                     {<<PatPref/binary, PatLast:8>>, <<PatPref/binary, (PatLast+1):8>>}

       end.

 

find(Pat) ->

       {PatMin, PatMax} = wildcard(Pat),

       F = fun() ->

                     qlc:eval(qlc:q(

                           [Patient || #patient{search_name = N}=Patient <- mnesia:table(pat),

                                         N >= PatMin, N < PatMax]

                     ))

              end,

       mnesia:activity(transaction, F, [], mnesia_frag).

 

 

Regards,

Janos

karol skocik

unread,
May 14, 2013, 2:11:22 PM5/14/13
to Janos Hary, Erlang Questions
On Tue, May 14, 2013 at 6:01 PM, Janos Hary <janos....@gmail.com> wrote:

Thanks for the answer. As far as I understand it works great if the prefix is known at compile time. I wasn’t clear on this, but in my case the prefix is given in runtime.


Yes, literal fun as arg to ets:fun2ms is a compile time entity. It's good for getting the first sketch of match spec in shell. 
This is a full scan of the table, and since match spec's first arg can't be a binary with prefix only the full scan can't be avoided.
Function you have is pretty much the only way to do what you want.

Cheers
 

Ulf Wiger

unread,
May 17, 2013, 2:05:28 PM5/17/13
to Janos Hary, erlang-q...@erlang.org

Many years ago I wrote rdbms_ms.erl,


which takes a regexp.erl-style expression and converts it to a match spec.

It has its limitations, but works pretty well for simple expressions.

BR,
Ulf

_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions

Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.



Reply all
Reply to author
Forward
0 new messages