I created a table using
mafiapp_friends = [aaa, expertise, xxx, yyy]
Note expertise is at position 2 in record
mnesia:create_table(mafiapp_friends,
[{attributes, record_info(fields, mafiapp_friends)},
{disc_copies, Nodes}]).
I forgot to add an index statement in it
{index, [#mafiapp_friends.expertise]},
Now I need to create this index. However I do not want to delete the table and re-create the table as I have data in it.
I executed following statement:
mnesia:add_table_index(mafiapp_friends, expertise)
And then I did
mnesia:schema(mafiapp_friends).
In the output I see,
index -> [3]
{index,3} -> {mafiapp_friends,index,3}
Can you tell me what [3] means here?
So, 3 is position of indexed field in the table, i.e.
#mafiapp_friends.expertise
See an example:
2> rd(mafiapp_friends, {aaa, expertise, xxx, yyy}).
mafiapp_friends
3> #mafiapp_friends.expertise.
3
4> record_info(fields, mafiapp_friends).
[aaa,expertise,xxx,yyy]
5>
Note that records in erlang are just tuples with the first element being record name, that's why indexing looks a bit strange on the first sight
5> X = #mafiapp_friends{expertise = hello}.
#mafiapp_friends{aaa = undefined,expertise = hello,
xxx = undefined,yyy = undefined}
6> element(3, X).
hello
7>
Related
Suppose I have an ets table like:
I = ets:new(mytable, [named_table, set]).
ets:insert(I, {10,{10, 4 ,"description"}).
I would like to update the element 4 using the ets:update_counter.
I tried in different way, but can't find the solution, for example:
ets:update_counter(I, 10 , {3,1}).
** exception error: bad argument
in function ets:update_counter/3
called as ets:update_counter(mytable,10,{3,1})
I'd like to have the result as:
{10,{10, 5 ,"description"}
I recommend to use just one tuple for key and values, instead of using a tuple for values in another tuple:
1> I = ets:new(mytable, [named_table, set]).
mytable
2> ets:insert(I, {10, 10, 4 ,"description"}).
true
3> ets:update_counter(I, 10 , {3,1}).
5
4> ets:lookup(I, 10).
[{10,10,5,"description"}]
I want to know how can I update multiple records of table
for example I have a table named : transaction
I want to modifiy the id of transaction
I try without success with
testupdate()->
Key =20,
Update=#transaction{id=Key} ,
Fun = fun() ->
List = mnesia:match_object(Update),
lists:foreach(fun(X) ->
mnesia:write_object(X)
end, List)
end,
mnesia:transaction(Fun).
when I test I didn't find an error
1> model:testupdate().
{atomic,ok}
but the id of transaction are not changed
It depends a lot on the Type of Table, and what field you are updating in the records. Unfortunately, you have not told us some details about your table. Lets say, you are updating multiple records basing on a given field.
NOTE that i do not recommend the use of the word transaction as a table name. But for purposes of learning, lets continue. Pseudo Code:
Get all records whose: Obj#transaction.field == Key
Then, Foreach, set: Obj#transaction.field == Key2
Then consider this example basing on Query List Comprehension
-include_lib("stdlib/include/qlc.hrl").
select(Q)->
case mnesia:is_transaction() of
false ->
F = fun(QH)-> qlc:e(QH) end,
mnesia:activity(transaction,F,[Q],mnesia_frag);
true -> qlc:e(Q)
end.
gen_update(FilterFun,UpdateFun)->
A = select(qlc:q([R || R <- mnesia:table(transaction),FilterFun(R) == true])),
[UpdateFun(X) || X <- A],
ok.
update_by_key(OldKey,NewKey)->
FilterFun = fun(#transaction{key = OldKey}) -> true;
(_) -> false
end,
UpdateFun = fun(T) ->
NewT = T#transaction{key = NewKey},
mnesia:write(NewT),
ok
end,
gen_update(FilterFun,UpdateFun),
ok.
That should do it. Look at the function: gen_update. I have used funs to create generic Objects which will filter according to any desired form, and another which will do the update. Now, you can construct any fun of your choice as long as it takes in a record as an argument. Note that this method may be applicable to tables of type set, depending on what you are doing. If you are updating by primary key, then, you need to make some new changes.
I have a table person with this record
-record(person, {id, firstname, lastname, phone}).
I want to update the phone of all records of this table
Itry with
test()->
Newphone ="216",
Update=#person{phone=Newphone} ,
Fun = fun() ->
List = mnesia:match_object(Update),
lists:foreach(fun(X) ->
mnesia:write_object(X)
end, List)
end,
mnesia:transaction(Fun).
The table person contains
12 alen dumas 97888888
15 franco mocci 55522225
13 ali othmani 44444449
I want that this table became like this :
12 alen dumas 216
15 franco mocci 216
13 ali othmani 216
I try with :
test()->
Newphone ="216",
Update=X#person{phone=Newphone, _ = '_'}
Fun = fun() ->
List = mnesia:match_object(Update),
lists:foreach(fun(X) ->
mnesia:write(X)
end, List)
end,
mnesia:transaction(Fun).
but with this code I have this error :
Variable X is unbound
this is related to this line :
Update=X#person{phone=Newphone, _ = '_'}
to resolve this probleme I do :
test()->
Newphone ="216",
Update=#person{phone=Newphone, _ = '_'}
Fun = fun() ->
List = mnesia:match_object(Update),
lists:foreach(fun(X) ->
mnesia:write(X)
end, List)
end,
mnesia:transaction(Fun).
when I test I have this message :
{atomic,ok}
but when I consult the database I find that the records are not changed
the difficulty in my code is to change all records of the table person
so change 97888888 and 55522225 and 44444449
this values should became 216
Continuing from what #legoscia has started. There are a couple of problems left with your code:
In the mnesia:match_object/1 call Update is being used as a pattern so when you set the phone field phone=NewPhone in Update you are actually saying to match_object give me all the records which have a phone of value "216". Which is not what you want.
You are writing back exactly the same record as you matched. You are not changing the record before writing it back.
A solution could be (untested):
test()->
Newphone ="216",
Match=#person{_ = '_'}, %Will match all records
Fun = fun() ->
List = mnesia:match_object(Match),
lists:foreach(fun(X) ->
%% Create new record with phone=NewPhone and write it back
Update = X#person{phone=NewPhone},
mnesia:write(Update)
end, List)
end,
mnesia:transaction(Fun).
Any fields you set in Match will limit which records you will match in match_object. For example Match = #person{phone="123",_='_'} will match all records which have phone "123".
A few things need changing here:
If you are going to use a record as a template for mnesia:match_object, you should fill in the record fields that you don't care about with the atom '_'. There is a special syntax to do that:
Update=#person{phone=Newphone, _ = '_'}
You probably don't want Newphone in there—the record that you pass to match_object should match the objects that are already in the table but should be changed, not what you want the objects to be changed to.
Which records are you trying to change? That will determine what you should pass to match_object.
The only thing you do in the transaction is reading records and writing them back unchanged. Have a look at the Records chapter of the Erlang reference manual; you probably want something like X#person{phone = Newphone} to change the phone field in X.
The function mnesia:write_object doesn't exist; you probably meant mnesia:write.
I have two records:
-record(foo, {timestamp, name}).
-record(bar, {timestamp, name}).
And I would like to execute a Mnesia query that mimics the following SQL query
SELECT f.* FROM foo f WHERE f.timestamp NOT IN ( SELECT b.timestamp FROM boo b)
What would be an efficient Mnesia equivalent?
Good Question ! Now, i have thought of two ways. One where we use qlc and another where we use mnesia's own table iteration methods with accumulators. Here is the first option:
%% Here i use 'qlc', with a guard
%% which ensures that the timestamp
%% in the given 'foo record'
%% does NOT exist in table 'bar'
query()->
Is_not_in_bar = fun(Stamp)->
case mnesia:dirty_read({bar,Stamp}) of
[] -> true;
_ -> false
end
end,
Q = qlc:q([R || R <- mnesia:table(foo), Is_not_in_bar(R#foo.timestamp) == true])),
Fun = fun(QH)-> qlc:e(QH) end,
mnesia:activity(transaction,Fun,[Q],mnesia_frag).
Another option would be to iterate table foo while cross referencing each timestamp in the table bar. If its not found in bar then its added to the accumulated amount. look at this below
%% Here i iterate table 'foo'
%% for each record i find,
%% i try to cross reference
%% its timestamp in table 'bar'
%% If its not found, then i add that
%% 'foo record' into the Buffer
%% I accumulate this Buffer until
%% the entire table 'foo' has been
%% traversed
query_vsn2()->
Iterator = fun(#foo{timestamp = T} = Rec,Buffer)->
NewBuffer = case mnesia:dirty_read({bar,T}) of
[] -> [Rec|Buffer];
_ -> Buffer
end,
NewBuffer
end,
Find = fun(Loop)-> mnesia:foldl(Loop,[],foo) end,
mnesia:activity(transaction,Find,[Iterator],mnesia_frag).
I guess depending on the table size, the application and the user preference, each of these functions will have consequences. However, try both of them and see which one blends well into your app. The good thing is that this is entirely a read job, no writes, so i expect to be efficient enough. Success !
I tried this code snippet:
print_next(Current) ->
case mnesia:dirty_next(muppet, Current) of
'$end_of_table' ->
io:format("~n", []),
ok;
Next ->
[Muppet] = mnesia:dirty_read({muppet, Next}),
io:format("~p~n", [Muppet]),
print_next(Next),
ok
end.
print() ->
case mnesia:dirty_first(muppet) of
'$end_of_table' ->
ok;
First ->
[Muppet] = mnesia:dirty_read({muppet, First}),
io:format("~p~n", [Muppet]),
print_next(First),
ok
end.
But it is so long. Also I can use dirty_all_keys and then iterate through key list, but I want to know if there is a better way to print out Mnesia table contents.
If you just want a quick and dirty way to print the contents of a Mnesia table in the shell, and if your table is not of type disc_only_copies, then you can take advantage of the fact that Mnesia stores its data in ETS tables and run:
ets:tab2list(my_table).
or, if you think the shell truncates the data too much:
rp(ets:tab2list(my_table)).
Not recommended for "real" code, of course.
For a simple and quick look at your table contents you can use select function of mnesia with catch-all Match Specification as follows:
CatchAll = [{'_',[],['$_']}].
mnesia:dirty_select(TableName, CatchAll).
and also you can run it inside a transaction context:
CatchAll = [{'_',[],['$_']}].
SelectFun = fun() -> mnesia:select(TableName, CatchAll) end.
mnesia:transaction(SelectFun).
however be careful if you are in a production environment with a big data.
Well, if the intent is to see the contents of your table, there is the application called tv, which can view both ETS and mnesia tables.
If you wish to see all the table contents on your terminal, then try something like this:
traverse_table_and_show(Table_name)->
Iterator = fun(Rec,_)->
io:format("~p~n",[Rec]),
[]
end,
case mnesia:is_transaction() of
true -> mnesia:foldl(Iterator,[],Table_name);
false ->
Exec = fun({Fun,Tab}) -> mnesia:foldl(Fun, [],Tab) end,
mnesia:activity(transaction,Exec,[{Iterator,Table_name}],mnesia_frag)
end.
Then if your table is called muppet, you use the function as follows:
traverse_table_and_show(muppet).
Advantages of this:
If its executed within a transaction , it will have no problems of nested transactions. It is less work because its done within one mnesia transaction through mnesia iterator functionality as compared to your implementation of get_next_key -> do_read_with_key -> then read the record (these are many operations). With this, mnesia will automatically tell that it has covered all the records in your entire table. Also, if the table is fragmented, your functionality will only display records in the first fragment. This will iterate through all the fragments the belong to that table.
In this iteration mnesia method, i do nothing with the Accumulator variable which should go along with the Iterator fun and thats why you see the underscore for the second variable.
Details of this iteration can be found here: http://www.erlang.org/doc/man/mnesia.html#foldl-3
As Muzaaya told, you can you use tv (table visualizer tool) to view both mnesia and ets tables.
Alternatively, you can use the following code to get mnesia table data - Print on terminal or in case you want to store the result in a file :
select_all() ->
mnesia:transaction(
fun() ->
P=qlc:e(qlc:q([E || E <- mnesia:table(tableName)])), %query to select all data from table named 'tableName'
io:format(" ~p ~n ", [P]), % Prints table data on terminal
to_file("fileName.txt",P) % to_file method writes the data to file
end ).
to_file(File, L) ->
mnesia:transaction(
fun() ->
{ok, S} = file:open(File, write),
lists:foreach(fun(X) -> io:format(S, "~p.~n" ,[X]) end, L),
file:close(S)
end).