| Author |
Message |
|
| seancharles |
Posted: Mon Nov 26, 2007 11:23 am |
|
|
|
User
Joined: 18 Jul 2007
Posts: 57
|
Hi everybody,
Sorry for the long post but it's a long journey too. But an enjoyable one, and I'm sure there are others who would benefit from a Guru level answer to bread-and-butter stuff like this.
I have been working my way through Chapter 17 of *the book* and apart from wondering how I managed without it I am having some 'issues' with Mnesia, mostly because up until now (apart from some Hibernate and Symfony exposure) the majority of my DBMS work has been with PostgreSQL, MySQL or SQLServer.
I decided to implement a simple tag cloud server in Mnesia instead of SQL. My SQL implementation basically consisted of support code that wrapped around a table:
Code:
id
cloud_id -> FK into a Tag-Cloud Name table.
frequency -> Number of hits for this tag term.
modified_at -> date of last modification.
value -> The actual tag value, a string.
This table lets me ask fairly simple things such as:
- Give me the top(N) popular terms and their frequencies.
- Give me the newest term(s)
- Give me the oldest term(s)
- Which term can I delete to enter a *new* term (N-rollover policy in effect to minimise space run-away)
I wanted to implement the same thing in Mnesia so I created this record:
Code:
-record(
tagcloud, {
name, % Tag cloud name eg search, user etc
frequency = 1, % Frequency count
modified = now(), % Date of last modification
value % The tag term,
}).
My first mistake was ordered_set: as soon as I added a different term it mashed the first one out of existence; some headscratching and re-reading led me to realise that the 'name' was being used as the key and because I added the new search term as 'search' it was just overwriting the old one.
Then I re-thought the problem: the key. My SQL solution allows multiple tag clouds to co-exist in the same table, differentiated by the 'cloud_id' column. My other thought was to use a 'bag' and then have the key as the cloud name and the data value as the actual collection of records but being obsessed with 'speed and efficiency' I began to wonder just what would an experienced Mnesia user actually do, as bags need to be checked for duplicate keys and sorted on each insertion. I want to keep them sorted by 'frequency' and maintain an index on the 'modified' value as well.
So, my real question then is, what is the 'best' (define best!) way to implement the Mnesia equivalent of:
id
cloud_id -> FK into a Tag-Cloud Name table.
frequency -> Number of hits for this tag term.
modified_at -> date of last modification.
value -> The actual tag value, a string.
My 'current' problem is that I decided to create *two* mnesia clouds instead so that the cloud name is implied in the table, thus removing the problem, or so I thought but then I learned another lesson the abrupt way: the mnesia:write( R ) function infers the table name from the first key in the record (can this be altered during the table create?) so when I try to insert into one table or the other, the table 'tagcloud' doesn't exist anymore; it's now 'search_tags' and 'user_tag', which kind of implies I need two record definitions that are identical apart from the first part. Could I have used a macro to model the common part here ?
Ideally I would like (and am in active pursuit) of a solution that used a single Mnesia table called 'tagcloud' and that I could just add to it and retrieve from it using my already waiting API:
Code:
% add a word to a tag cloud
add( CloudName, Value )
% get N most popular tag terms
popular( CloudName, N )
% get N most recent tag terms
recent( CloudName, N )
Thanks
Sean Charles. |
|
|
| Back to top |
|
| brett hallett |
Posted: Fri Nov 30, 2007 4:58 am |
|
|
|
User
Joined: 21 Aug 2007
Posts: 21
|
I'm not sure if the following is what you require, but I think your trying to create a 'transction' type logging
of records, the following was what I did to create unique records for transactions so maybe of use to you ( I hope)
==============
While playing around with that 'Bank' example from Joes tutorial, I decide to try and expand it so that system
kept a complete transaction log of all tranactions ( deposits & withdrawals). The original program only keeps a running total for each account holder.
So I added the following record structures :
%% ==================================
%% records for CreditUnion Banking example
%% a development based upon Joe Armstrongs 'bank' example
%% using his simple client/server code as the base.
-created_by('Brett Hallett : dragoncity@impulse.net.au').
-created('Date: 29 Sept 07 15:43').
-record(account, {accNum, balance}).
-record(acctrans, { transtime, seq, accNo , amount}).
%% =================================
the record, acctrans, is of interest is
-record(acctrans, { transtime, seq, accNo , amount}).
I'm keeping a transaction datetime , a unique seq number, the account no (name), and the tranaction value.
so keeping the records in order was not really necesary anyway. I do the query then sort the result. Seems to work ok.
The sequence number is obtained from a little function I copied from this site called "sequence", which returns the next number for a user defined key, so any records you add to your table are uniquely identifiable.
23> ets:tab2list(acctrans).
[{acctrans,{{2007,11,23},{17,4,46}},10,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,28,50}},13,"max",-121.180},
{acctrans,{{2007,11,23},{17,4,42}},8,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,45}},34,"max",123.760},
{acctrans,{{2007,11,23},{16,34,23}},2,"beatie",109},
{acctrans,{{2007,11,23},{17,4,53}},12,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,38}},7,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,33,25}},26,"max",10.9900},
{acctrans,{{2007,11,23},{16,34,33}},4,"beatie",10.9900},
{acctrans,{{2007,11,23},{16,28,34}},12,"max",100},
{acctrans,{{2007,11,23},{17,4,34}},31,"max",123.760},
{acctrans,{{2007,11,23},{17,5,3}},35,"max",-123.760},
{acctrans,{{2007,11,23},{16,33,16}},14,"max",10.9900},
{acctrans,{{2007,11,23},{16,33,24}},24,"max",10.9900},
{acctrans,{{2007,11,23},{17,5,52}},13,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,34,18}},1,"beatie",10.9900},
{acctrans,{{2007,11,23},{17,4,18}},6,"beatie",11.8100},
...etc...
Note acctrans records are 'jumbled', but each account ( 'max') has a unique sequence number as well as a datetime stamp -teh seqno ensures that I dont get a transaction clash if a new transaction arrives at the same time.
%%%==============================================
%%% extract all transactions code
%%% ==========================================
%% accTrans : select out ALL transactions for a given AccNo eg: "beatie"
%% -record(acctrans, { transtime, seq, accNo , amount}).
accTrans(AccNo) ->
F = fun() ->
Q = qlc:q([E || E <- mnesia:table(acctrans),
E#acctrans.accNo == AccNo ]),
%% query : get all records off acctrans where field accNo equals "AccNo" value in list "E"
qlc:e(Q) %% execute the query
end,
S = mnesia:transaction(F),
io:fwrite("Trans list: ~s \n ~p \n", [ AccNo, S]), %% print the returned records ( returned order)
{_,S1} = S,
SL = lists:sort(S1), %% sort into transaction order
io:fwrite("Trans sort: ~s \n ~p \n", [ AccNo, SL]).
%%% =============
%% ====================================
%%% log a transaction - code
%% ====================================
log_trans(AccNo, X) ->
SeqNo = sequence:sequence(AccNo),
%% returns next sequence number for key : AccNo
%% a record in sequence table will be created if required
TransTime = calendar:local_time(),
%% get current date & time
mnesia:write(#acctrans { transtime = TransTime , seq = SeqNo,
accNo = AccNo, amount =X }).
%% write a transaction
======================
execute log
======================
22> cu_client:accTrans("beatie").
Trans list: beatie
{atomic,[{acctrans,{{2007,11,23},{17,4,46}},10,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,42}},8,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,34,23}},2,"beatie",109},
{acctrans,{{2007,11,23},{17,4,53}},12,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,38}},7,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,34,33}},4,"beatie",10.9900},
{acctrans,{{2007,11,23},{17,5,52}},13,"beatie",11.8100},
{acctrans,{{2007,11,23},{16,34,18}},1,"beatie",10.9900},
{acctrans,{{2007,11,23},{17,4,18}},6,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,51}},11,"beatie",-11.8100},
{acctrans,{{2007,11,23},{16,34,29}},3,"beatie",109.980},
{acctrans,{{2007,11,23},{16,46,0}},5,"beatie",1019},
{acctrans,{{2007,11,23},{17,4,44}},9,"beatie",11.8100}]}
Trans sort: beatie
[{acctrans,{{2007,11,23},{16,34,18}},1,"beatie",10.9900},
{acctrans,{{2007,11,23},{16,34,23}},2,"beatie",109},
{acctrans,{{2007,11,23},{16,34,29}},3,"beatie",109.980},
{acctrans,{{2007,11,23},{16,34,33}},4,"beatie",10.9900},
{acctrans,{{2007,11,23},{16,46,0}},5,"beatie",1019},
{acctrans,{{2007,11,23},{17,4,18}},6,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,38}},7,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,42}},8,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,44}},9,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,46}},10,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,4,51}},11,"beatie",-11.8100},
{acctrans,{{2007,11,23},{17,4,53}},12,"beatie",11.8100},
{acctrans,{{2007,11,23},{17,5,52}},13,"beatie",11.8100}]
--
Brett S Hallett
dragoncity@impulse.net.au |
|
|
| Back to top |
|
|
|
All times are GMT
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|
|