Search code examples
erlangunique-keymnesia

Erlang Mnesia Unique keys except primariy key


I want to create the following table in mnesia:

-record(cuser, {
id,
login_id,
email,
....}).

where login_id and email are both unique in business logic.

How can I implement it in mnesia?

Thanks in advance.


Solution

  • The simplest way is to performs all the tests in a single mnesia transaction. the following code does it.

    Be aware that this code does not check if the Id already exists, so if you try to add a new user with existing id and new login id and email, it will simply overwrite the existing record (that is the behavior of set).

    %% record declaration
    -record (cuser, {id,login_id,email}),
    %% create a table for test
    mnesia:create_schema([node()]),
    application:start(mnesia),
    mnesia:create_table(cuser,[{attributes,record_info(fields,cuser)},
                               {index,[#cuser.login_id,#cuser.email]},
                               {disc_copies,[node()]},
                               {type,set}]),
    %% write 2 record for test
    Wr = fun(I,L,E) -> mnesia:write(#cuser{id=I,login_id=L,email=E}) end,
    mnesia:transaction(Wr,[u1,l1,m1]),
    mnesia:transaction(Wr,[u2,l2,m2]),
    %% create a function for filtering
    M = fun(Nl,Ne) -> 
            Match = ets:fun2ms(fun(#cuser{id=I,login_id=Li,email=E}) when Li =:= Nl; E =:= Ne -> {I,Li,E} end),
            mnesia:select(cuser,Match)
        end,
    %% create a function to add a new user
    Add = fun(I,Nl,Ne) ->
            {atomic,R} = mnesia:transaction(M,[Nl,Ne]),
            case R of
              [] -> mnesia:transaction(Wr,[I,Nl,Ne]);
              R -> {already_exist,R}
            end
           end,
    %% add new user
    mnesia:transaction(Add,[i3,l3,m1]), %% will return {atomic,{already_exist,[{u1,l1,m1}]}}
    mnesia:transaction(Add,[i3,l3,m3]), %% will return {atomic,{atomic,ok}}
    

    To check if the Id already exists simply add the test in the filter function:

    %% create a function for filtering
    M = fun(Ni,Nl,Ne) -> 
            Match = ets:fun2ms(fun(#cuser{id=I,login_id=Li,email=E}) when I =:= Ni; Li =:= Nl; E =:= Ne -> {I,Li,E} end),
            mnesia:select(cuser,Match)
        end,
    %% create a function to add a new user
    Add = fun(I,Nl,Ne) ->
            {atomic,R} = mnesia:transaction(M,[I,Nl,Ne]),
            case R of
              [] -> mnesia:transaction(Wr,[I,Nl,Ne]);
              R -> {already_exist,R}
            end
           end,