Skip to content

Instantly share code, notes, and snippets.

@zhongwencool
Created October 13, 2016 08:33
Show Gist options
  • Save zhongwencool/11b511e02ac24c667703db7b112411a4 to your computer and use it in GitHub Desktop.
Save zhongwencool/11b511e02ac24c667703db7b112411a4 to your computer and use it in GitHub Desktop.
[test] mnesia find object without transaction.
-module(mnesia_read_test).
-include_lib("stdlib/include/ms_transform.hrl").
-include_lib("stdlib/include/qlc.hrl").
%% API
-compile(export_all).
-record(person, {name, sex, age, height}).
-define(PERSON, person).
init_table() ->
mnesia:start(),
random:seed(),
mnesia:create_table(?PERSON,
[{attributes, record_info(fields, ?PERSON)},
{ram_copies, [erlang:node()]},
{index, [age]},
{majority, true}]),
create_persons(1000).
%% 1. accord primary key to read one object
find_person_by_name(Name) ->
mnesia:dirty_read(?PERSON, Name).
%% 2. accord index to read objects
find_person_by_age(Age) ->
mnesia:dirty_index_read(?PERSON, Age, #person.age).
%% 3. Match Pattern
%% The function mnesia:match_object/3 automatically uses indexes if these exist. However, no heuristics are performed to select the best index.
find_person_by_age2(Age) ->
mnesia:dirty_match_object(?PERSON, {person, '_', '_', Age, '_'}).
%% 4. assign index's Match Pattern
find_person_by_age3(Age) ->
mnesia:dirty_index_match_object(?PERSON, {person, '_', '_', Age, '_'}, #person.age).
%% 5. select
find_name_by_age_range(MinAge, MaxAge) ->
MatchSpec = ets:fun2ms(fun(#person{age = Age, name = Name}) when Age >= MinAge andalso Age =< MaxAge -> {Age, Name} end),
mnesia:dirty_select(?PERSON, MatchSpec).
%% 6. foldl Iterates over the table
find_name_by_age_range2(MinAge, MaxAge) ->
Fun = fun(#person{age = Age, name = Name}, Acc) when Age >= MinAge andalso Age =< MaxAge -> [{Age, Name}|Acc];
(_, Acc) -> Acc
end,
mnesia:async_dirty(fun() -> mnesia:foldl(Fun, [], ?PERSON) end).
%% 7. first/next Iterates over the table
find_name_by_age_range3(MinAge, MaxAge) ->
case mnesia:dirty_first(?PERSON) of
'$end_of_table' -> [];
First ->
[Person] = mnesia:dirty_read({person, First}),
Persons =
if Person#person.age >= MinAge andalso Person#person.age =< MaxAge ->
[{Person#person.age, Person#person.name}];
true -> []
end,
read_next(First, MinAge, MaxAge, Persons)
end.
%% 8. qlc
find_name_by_age_range4(MinAge, MaxAge) ->
Fun = fun() ->
qlc:e(qlc:q([{X#person.age, X#person.name} || X <- mnesia:table(?PERSON),
X#person.age >= MinAge,
X#person.age =< MaxAge]))
end,
mnesia:async_dirty(Fun).
test() ->
init_table(),
Res0 = [P0|_] = find_person_by_name("zhongwencool100"),
io:format("dirty_read find_person_by_name Number:~p~nSample:~p~n", [length(Res0), P0]),
Res1 = [P1|_] = find_person_by_age(21),
io:format("dirty_index_read find_person_by_age Number:~p~nSample:~p~n", [length(Res1), P1]),
Res2 = [P2|_] = find_person_by_age2(21),
io:format("dirty_match_object find_person_by_age2 Number:~p~nSample:~p~n", [length(Res2), P2]),
Res3 = [P3|_] = find_person_by_age3(21),
io:format("dirty_index_match_object find_person_by_age3 Number:~p~nSample:~p~n", [length(Res3), P3]),
Res4 = [P4|_] = find_name_by_age_range(10, 20),
io:format("dirty_select find_name_by_age_range Number:~p~nSample:~p~n", [length(Res4), P4]),
Res5 = [P5|_] = find_name_by_age_range2(10, 20),
io:format("foldl find_name_by_age_range2 Number:~p~nSample:~p~n", [length(Res5), P5]),
Res6 = [P6|_] = find_name_by_age_range3(10, 20),
io:format("first/next find_name_by_age_range3 Number:~p~nSample:~p~n", [length(Res6), P6]),
Res7 = [P7|_] = find_name_by_age_range4(10, 20),
io:format("qlc find_name_by_age_range4 Number:~p~nSample:~p~n", [length(Res7), P7]),
io:format("verify result :~p~n", [{Res1 == Res2, Res2 == Res3, Res4 == Res5, Res5 == Res6, lists:sort(Res6) == lists:sort(Res7)}]),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Local Function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
create_person(Person)when is_record(Person, person) ->
{atomic, ok} = mnesia:transaction(fun() -> mnesia:write(Person) end).
create_persons(Num)when is_integer(Num) ->
Fun =
fun() ->
[begin
Person = #person{name = "zhongwencool" ++ integer_to_list(Index),
sex = random_sex(),
age = random_age(),
height = Index + 120},
mnesia:write(Person)
end|| Index <-lists:seq(1, Num)],
ok
end,
{atomic, ok} = mnesia:transaction(Fun).
read_next(Current, MinAge, MaxAge, Acc) ->
case mnesia:dirty_next(person, Current) of
'$end_of_table' -> Acc;
Next ->
[Person] = mnesia:dirty_read({person, Next}),
Persons =
if Person#person.age >= MinAge andalso Person#person.age =< MaxAge ->
[{Person#person.age, Person#person.name}|Acc];
true -> Acc
end,
read_next(Next, MinAge, MaxAge, Persons)
end.
random_age() ->
Age = lists:nth(random:uniform(9), [10, 20, 30, 40, 50, 60, 70, 80, 90]),
Age + random:uniform(10) - 1.
random_sex() ->
case random:uniform(2) of
1 -> male;
2 -> female
end.
@zhongwencool
Copy link
Author

Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V7.3  (abort with ^G)
1> mnesia_read_test:test().
dirty_read               find_person_by_name     Number:1
Sample:{person,"zhongwencool100",female,63,220}
dirty_index_read         find_person_by_age      Number:11
Sample:{person,"zhongwencool203",female,21,323}
dirty_match_object       find_person_by_age2     Number:11
Sample:{person,"zhongwencool203",female,21,323}
dirty_index_match_object find_person_by_age3     Number:11
Sample:{person,"zhongwencool203",female,21,323}
dirty_select             find_name_by_age_range  Number:137
Sample:{12,"zhongwencool670"}
foldl                    find_name_by_age_range2 Number:137
Sample:{12,"zhongwencool670"}
first/next               find_name_by_age_range3 Number:137
Sample:{12,"zhongwencool670"}
qlc                      find_name_by_age_range4 Number:137
Sample:{11,"zhongwencool109"}
verify result :{true,true,true,true,true}
ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment