#51 Re: mORMot 1 » Fastest DB 'CAS' in ORM or direct SQL » 2014-06-28 15:20:26

One more question relating:
How do I get an aggregate query, like I would in SQL e.g.:

select YEAR,MONTH,SUM(BYTES),SUM(PACKETS)
FROM TrafficItem
WHERE FMAC='11:22:33:44:55'
GROUP BY YEAR,MONTH

Or must I just query all records and make sum manually in-memory?

Thanks and regards
AntonE

#52 Re: mORMot 1 » Fastest DB 'CAS' in ORM or direct SQL » 2014-06-28 13:22:51

Thanks and WOW.
I just added transaction and kept 1 TSQLRecord instance alive during long loop.
Now it is very very fast compared to my Firebird version. smile
The code is +- 50% less, exe is much smaller, faster...
And this is from simple, readable pure Delphi ORM code!
What can I say? Extremely impressed...

AntonE

#53 mORMot 1 » Fastest DB 'CAS' in ORM or direct SQL » 2014-06-28 08:20:14

AntonE
Replies: 5

Hi all,
still finding my feet in mORMot... smile

I need to do a type of CAS in the DB.
I'm recording traffic that is like this:

type
  TSQLTrafficItem=class(TSQLRecord)
  private
...
  published
    property Router   : Integer    read FRouter  write FRouter;
    property Year     : Word       read FYear    write FYear;
    property Month    : Word       read FMonth   write FMonth;
    property Day      : Word       read FDay     write FDay;
    property Hour     : Word       read FHour    write FHour;
    property Minute   : Word       read FMinute  write FMinute;
    property FIP      : RawUTF8    read FFIP     write FFIP;
    property TIP      : RawUTF8    read FTIP     write FTIP;
    property FMAC     : RawUTF8    read FFMAC    write FFMAC;
    property TMAC     : RawUTF8    read FTMAC    write FTMAC;
    property Bytes    : Int64      read FBytes   write FBytes;
    property Packets  : Int64      read FPackets write FPackets;
    property FUN      : RawUTF8    read FFUN     write FFUN;
    property TUN      : RawUTF8    read FTUN     write FTUN;
  end;
  DB.CreateSQLMultiIndex(TSQLTrafficItem,['Router','Year','Month','Day','Hour','Minute','FIP','TIP'],True);
  DB.CreateSQLIndex     (TSQLTrafficItem,'FMAC',False);
  DB.CreateSQLIndex     (TSQLTrafficItem,'TMAC',False);

Traffic is grouped by variable time increment, e.g. 5 or 15 minutes, so when receiving (logging) new traffic, I have to :
1)Check if such record/row exist (unique key : 'Router','Year','Month','Day','Hour','Minute','FIP','TIP')
2a)If it does, add new traffic to it (Bytes+Packets)
2b)If it does not, create new record.
So for each new item, I have to do at least 2 SQL statements.

ATM I'm doing like this:

   TI:=TSQLTrafficItem.FillPrepare(ServerForm.DB,'ROUTER = ? AND YEAR = ? AND MONTH = ? AND DAY = ? AND HOUR = ? AND MINUTE = ? AND FIP = ? AND TIP = ?',[],[RouterID,Y,M,D,H,N,IPF,IPT]);
   if TI.FillOne
      then begin
            if (TI.FMAC='')and (MACF<>'')
               then TI.FMAC:=MACF;
            if (TI.TMAC='')and (MACT<>'')
               then TI.TMAC:=MACT;
            TI.Bytes  :=TI.Bytes  +IPB;
            TI.Packets:=TI.Packets+IPP;
            ServerForm.DB.Update(TI);
           end
      else begin
            TI.Router   :=RouterID;
            TI.Year     :=Y;
            TI.Month    :=M;
            TI.Day      :=D;
            TI.Hour     :=H;
            TI.Minute   :=N;
            TI.FIP      :=IPF;
            TI.TIP      :=IPT;
            TI.FMAC     :=MACF;
            TI.TMAC     :=MACT;
            TI.Bytes    :=IPB;
            TI.Packets  :=IPP;
            TI.FUN      :=UNF;
            TI.TUN      :=UNT;
            ServerForm.DB.Add(TI,True);
           end;
   TI.Free;

But I need to get it as fast/efficient as possible. Would it be significantly faster direct to SQL?
I tried to have a look at direct SQL in documentation and other threads, but could not figure out how to get a connection. tongue

Would something like this be possible/optimal where I get 3 statements beforehand?

  SQLF := dbConn.NewStatementPrepared('select * from TrafficItem WHERE ROUTER = ? AND YEAR = ? AND MONTH = ? AND DAY = ? AND HOUR = ? AND MINUTE = ? AND FIP = ? AND TIP = ?',false, true);
  SQLI := dbConn.NewStatementPrepared('insert into TrafficItem (ROUTER,YEAR,MONTH,DAY,HOUR,MINUTE,FIP,TIP,FMAC,TMAC,BYTES,PACKETS,FUN,TUN) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)',false, true);
  SQLU := dbConn.NewStatementPrepared('update TrafficItem (FMAC,TMAC,BYTES,PACKETS,FUN,TUN) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?) WHERE (ROUTER = ? AND YEAR = ? AND MONTH = ? AND DAY = ? AND HOUR = ? AND MINUTE = ? AND FIP = ? AND TIP = ?',false, true);

If so, how do I get/make a direct SQL connection? Can't figure where to get a DB connection from TSQLRestServerDB to call "NewStatementPrepared". smile

These statements run in a loop, sometimes recording >8000 new items at a time, minutes or even seconds apart. Do I start/stop a transaction before/after loop? It's safe from data integrity point of view but will it block DB during that time?
This 'table' (TSQLTrafficItem) is only updated from 1 thread in the server code itself.

Thanks
Regards
AntonE

#54 mORMot 1 » Client-side grid edit demo/sample? » 2014-06-25 12:48:19

AntonE
Replies: 5

Hello,
I've been looking at the samples but do not find a nice demo showing me how to do client side grid that is editable and updated to server.
E.g. like the Fishfacts demo for Delphi, but just in mORMot with Delphi client. Then how to edit a grid and send changed fields to server?

I can do it manually with TClientDataset but though maybe there is some code that can convert TSQLRecord list/array response from server to TClientdataset like in "17 - TClientdataset use" sample's StatementToClientDataSet. My app based on "HTTP client-server" sample.

Thanks
Regards

#55 Re: mORMot 1 » Need imported data reference...from unit SynCommons » 2013-03-05 21:35:01

Thank you i tried to add to project but did not work, so I just put it in SynCommons to get it compiled and it worked.

If this is what you meant:

package MGCustomLiveBindingMethods;

{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES ON}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE DEBUG}
{$ENDIF IMPLICITBUILDING}
{$IMPLICITBUILD ON}
{$DEFINE USEPACKAGES}

requires
  rtl,
  bindengine;

contains
  ISO8601ToStrMethod in 'ISO8601ToStrMethod.pas',
  MethodUtils in 'MethodUtils.pas',
  SynCommons in '..\..\..\..\SynCommons.pas';

#56 Re: mORMot 1 » Need imported data reference...from unit SynCommons » 2013-03-02 22:00:43

I might add that it is done with a simple package that can be found on this aritcle:
http://www.malcolmgroves.com/blog/?p=1226

But here is a direct link:
https://github.com/malcolmgroves/delphi-samples.git/

Here is a simple working folder/project that can just be extracted/compiled to see error:
ftp://ftp.true.co.za/CustomFormats.zip

#57 mORMot 1 » Need imported data reference...from unit SynCommons » 2013-03-02 18:06:31

AntonE
Replies: 3

I'm trying to build a custom format for LiveBindings to display ISO8601 (Don't know if it's needed, but good exercise)

It is basically a package that you need to compile/install into IDE, but I get error in subject when I try to compile.
Some reading suggest a rebuild with {$G+} or {$IMPORTEDDATA ON} but it does not work.

Thanks

unit ISO8601ToStrMethod;

interface

implementation

{$IMPORTEDDATA ON}
uses System.Bindings.Methods, System.SysUtils, MethodUtils,SynCommons,DateUtils;

const
  sIDIso8601ToStr = 'Iso8601ToStr';
  sIDStrToIso8601 = 'StrToIso8601';

procedure RegisterMethods;
begin
  TMethodUtils.RegisterMethod<Iso8601,string>(sIDIso8601ToStr,
    function(AValue: Iso8601): string
    begin
      Result := DateTimeToStr(AValue.ToDateTime);
    end);

  TMethodUtils.RegisterMethod<string,Iso8601>(sIDStrToIso8601,
    function(AValue: string): ISO8601
    var D : TDateTime;
        I : Iso8601;
    begin
     try
      D := StrToDateTime(AValue);
      PIso8601(@I)^.From(D);
      Result:=I;
     finally
      Result.Value:=0;
     end;
    end);
end;

procedure UnregisterMethods;
begin
  TBindingMethodsFactory.UnRegisterMethod(sIDIso8601ToStr);
  TBindingMethodsFactory.UnRegisterMethod(sIDStrToIso8601);
end;

initialization
  RegisterMethods;
finalization
  UnregisterMethods;
end.

#58 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-03-02 00:04:58

I have updated the sample in link above to only include a working LiveBindings in FireMonkey with XE3. (XE2 would not work)
It has simple Grid , TEdit and TMemo to display TSQLSampleRecord with add/edit/save/cancel/delete buttons for a very simple UI.
(All is working, except I do not know how to delete correctly, still learning...)

If someone change/fix that sample extensively, please copy back to the FTP with another filename(no password needed).
Or let me know if/what changes you make (or I must make) and I can update the FTP version as well.


Regards
AntonE

#59 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-03-01 00:26:02

I have somewhat success with XE3, but I thought I'll make a wireframe mORMot + LiveBinding + Firemonkey demo for persons more knowledgeable to perhaps expand so everyone can have a good demo to sample CRUD & queries.
Included is 2 methods:
1) Using TAdapterBinding but I could not find that in XE2... Also do not know how to bi-directionally link to e.g. TEdit1
2) Clone from BOCollections Delphi demos to try and make it work with TSQLSampleRecord...

I made this project as subdir next to other Samples, so it also shares e.g. SampleData, need Sample 04 's server running to work.

You can find first version here, still very basic and broken, still needs lots of input.
ftp://ftp.true.co.za/LiveBindingsFireMonkey.zip

Regards
AntonE

#60 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-28 00:26:42

I downloaded latest source to use new mORMot RetrieveList instead of patch.
My test loop is working now!

I just need to get LiveBindings to work, so it seem the generics issue is solved.
Thank you very much.

AntonE

#61 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-27 20:44:51

Thank you very much for your help so far.
I realise that this is not a mORMot issue so I really appreciate the time.
However, I managed to compile it (interface section) by using TSQLRecordClass as in your example, instead of TSQLRecord :

function TSQLRest.RetrieveGenericList<TSQLRecordClass>(FormatSQLWhere: PUTF8Char; const BoundsSQLWhere: array of const; const aCustomFieldsCSV: RawUTF8=''): TObjectList<TSQLRecord>;

But now I do not have 'Table' variable so do not know how to access it in the function itself...
Hard enough to learn mORMot, but now generics as well. big_smile
E.g.:

...
  if aCustomFieldsCSV<>''
     then T := InternalListJSON(Table,aCustomFieldsCSV,SQL)
     else T := InternalListRecordsJSON(Table,SQL);
...

#62 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-27 14:24:33

Above code:

   ShowMessage('#'+IntToStr(Routers[i].ID)+' '+R.Name+' '+R.IP); (*This always shows '#0' *)

should have been

ShowMessage('#'+IntToStr(Routers[i].ID)+' '+TSQLMikrotik(Routers[i]).Name+' '+TSQLMikrotik(Routers[i]).IP);

roll

But that gives an RT error, so I supposed that invalid typecast. Then I look at declaration:

    Routers : TObjectList<TSQLRecord>;

Maybe objects are returned as TSQLrecord, not as TSQLMikrotik
But if I change it to

    Routers : TObjectList<TSQLMikrotik>;

Then I get 'Invalid type cast' compile error on.

 Routers := Database.RetrieveGenericList(TSQLMikrotik,'',[]);

#63 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-27 14:14:49

I just copy/pasted and reformatted it to suit my indent style(I hope):

procedure TSQLTableToObjectGenericList(aRecordClass: TSQLRecordClass; aSourceTable: TSQLTable; aDestList: TObjectList<TSQLRecord>);
var R     : TSQLRecord;
    V     : ^TSQLRecord;
    Row   : PPUtf8Char;
    i     : integer;
begin
 R := aRecordClass.Create;
 try
  R.FillPrepare(aSourceTable);
  aDestList.Count := aSourceTable.RowCount; // faster than manual Add()
  V := @aDestList.ToArray[0];
  Row := @aSourceTable.fResults[aSourceTable.FieldCount]; // R^ points to first row of data
  for i := 1 to aSourceTable.RowCount do
   begin
    V^ := aRecordClass.Create; // TObjectList will free each instance
    R.fFill.Fill(pointer(Row),V^);
    Inc(Row,aSourceTable.FieldCount);
    inc(V);
   end;
 finally
  R.Free;
 end;
end;

function TSQLRest.RetrieveGenericList(Table: TSQLRecordClass; FormatSQLWhere: PUTF8Char; const BoundsSQLWhere: array of const; const aCustomFieldsCSV: RawUTF8=''): TObjectList<TSQLRecord>;
var SQL  : RawUTF8;
    T    : TSQLTable;
begin
  result := nil;
  if (self=nil) or (Table=nil)
     then exit;
  SQL := FormatUTF8(FormatSQLWhere,[],BoundsSQLWhere);
  if aCustomFieldsCSV<>''
     then T := InternalListJSON(Table,aCustomFieldsCSV,SQL)
     else T := InternalListRecordsJSON(Table,SQL);
  if T<>nil
     then try
           result := TObjectList<TSQLRecord>.Create;
           TSQLTableToObjectGenericList(Table,T,result);
          finally
           T.Free;
          end;
end;

#64 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-27 13:40:06

I tried to use this in Delphi XE2 for a Firemonkey app, but it is not working as expected. In my noobness, I wrote the code below to check. First loop work as expected, 2nd loop (generic list) not, but the number of entries/objects/records(!?) are correct, they are just all 'empty'.
AntonE


procedure TForm1.BtnRouters(Sender:TObject);
var R : TSQLMikrotik;
    I : Integer;
begin
 if Assigned(Routers)
    then FreeAndNil(Routers);
 R:=TSQLMikrotik.CreateAndFillPrepare(Database, '',[]);
 try
  while R.FillOne do
   ShowMessage(IntToStr(R.ID)+' '+R.Name+' '+R.IP); (*Displays all records correctly*)
 finally
  R.Free;
 end;

 Routers := Database.RetrieveGenericList(TSQLMikrotik,'',[]);
 for I := 0 to Routers.Count-1 do
  begin
   ShowMessage('#'+IntToStr(Routers[i].ID)+' '+R.Name+' '+R.IP); (*This always shows '#0' *)
  end;

 BindScopeCollection.DataObject := Routers;
 bndlstRoutersToListbox.FillList;
 ListBox1.ItemIndex := 0;
 BindScopeForm.Active := True;
end;

#65 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-22 20:21:29

Thanks!

ab, is this correct/best way (seems very 'elegant') and will you consider adding to official mORMot code?
I do not like to custom-patch libraries.
smile

#66 Re: mORMot 1 » Example to using Livebinding with mORMot » 2013-02-22 13:33:44

Hello, I'm just barely starting to use mORMot now...
Nice howto. Do I also need these changes for XE2 ?
If not, what would this line :

MyPeople := Database.RetrieveGenericList(TSQLSampleRecord,'',[]);

have to look like then?

#67 mORMot 1 » Server threads & Database » 2013-02-15 12:27:11

AntonE
Replies: 1

I have a HTTP server (like Sample 4) but need to have Threads running in background that does various tasks. Some of these tasks involve heavy DB access.
I am concerned that my Threads that run on server, will cause problems.

Should I do something to avoid/detect e.g. simultaneous access by a 'server worker thread' and a Client request to same Object?
What happens if 2 threads calls SQLRestServerDB.Update at the same time...
(Or Should each thread create a client connection to server?)

Regards
AntonE

#68 Re: mORMot 1 » Newbie help needed » 2013-02-15 08:05:52

Thank you for your reply. It makes interesting reading.
I realize my question was to broad.
I will re-post more specifics.

Thank you
AntonE
South Africa

#69 mORMot 1 » Newbie help needed » 2013-02-14 12:06:16

AntonE
Replies: 2

Hello,
I have looked at mORMot a while ago and have now committed to start using it for new projects.
We start with a small program to test and get familiar with mORMot framework.
I have read SAD document twice and I get a general feel, but it still hasn't 'clicked' properly yet as I start to use it now, so I need some advice please:


I'll try to re-write a VCL/Firebird Internet traffic recording program...
I has a background thread that get IP-pairs (TSQLTraffic) with bytes traffic every minute or 2 from a router.
All traffic is recorded and software detects if new PC is on network (if new IP in 'trusted' IP range appears as FromIP or ToIP. It then creates a TSQLComputer and user can enter HostName/DeviceName if it cannot be detected. Anyways, that's not the problem.

  TSQLComputer    = class(TSQLRecord)
                     private
                      fHostName       : RawUTF8;
                      fLastLanIP  : RawUTF8;
                     published
                      property HostName   : RawUTF8  read fHostName   write fHostName;
                      property LastLANIP  : RawUTF8  read fLastLANIP  write fLastLANIP;
                    end;
  TSQLTraffic     = class(TSQLRecord)
                     private
                      fTime   : TTimeLog;
                      fFromIP : Integer;
                      fToIP   : Integer;
                      fBytes  : Integer;
                      fPackets: Integer;
                     published
                      property Time       : TTimeLog read fTime       write fTime;
                      property FromIP     : Integer  read fFromIP     write fFromIP;
                      property ToIP       : Integer  read fToIP       write fToIP;
                      property Bytes      : Integer  read fBytes      write fBytes;
                      property Packets    : Integer  read fPackets    write fPackets;
                    end;

Eventually there will be millions of TSQLTraffic records and typically < 100 TSQLComputers.

I need to be able to do fast queries on e.g. :
- Total upload/download traffic for IP between 2 dates...
- Total traffic for IP range(s)  between 2 dates...
- Total traffic per hour between 2 dates...
- Total daily traffic...
- Graphs over time for IP range or specific IPs, etc.
- Top 5 Source IPs in last hour/day/month
- Top 5 Destination IPs in last hour/day/month
etc.

I do not know how to do the master/detail relationship. Is both TSQLRecord or TSQLRecordMany ?
Help please, I'm still a bit confused.  :~|

Problems:
1)link 1 TSQLTraffic record to 'possibly' 0, 1 or 2 TSQLComputer records
2)Make fast queries and stats based on FromIP and/or ToIP fields
3)'Aggregate' record subtotals into e.g. daily, monthly, like using SQL, sum(xx) and GROUP BY.

In old program I had (below), so it was easy to do, eg:

select "YEAR","MONTH","DAY","HOUR",SUM(UL+DL) FROM TRAFFIC GROUP BY "YEAR","MONTH","DAY","HOUR";

But then graphs only show in 'hour' chucks. I hope mORMot can help me make the database more fine-grained that Hour 'chunks'

CREATE TABLE "TRAFFIC"
(
  "IP"     INTEGER NOT NULL,
  "YEAR"     INTEGER NOT NULL,
  "MONTH"     INTEGER NOT NULL,
  "DAY"     INTEGER NOT NULL,
  "HOUR"     INTEGER NOT NULL,
  "DL"     DOUBLE PRECISION,
  "UL"     DOUBLE PRECISION,
  "DATETIME"     TIMESTAMP,
CONSTRAINT "PKTRAFFIC" PRIMARY KEY ("IP", "YEAR", "MONTH", "DAY", "HOUR")
);


Later I might need to add LanIP and WiFi-IP for laptops, so a computer is a sum of the traffic for 2 IPs...

#70 Re: mORMot 1 » Demo code? » 2012-02-03 14:53:54

lol!
Feeling nice and stupid now.
Thanks.

#71 mORMot 1 » Demo code? » 2012-02-03 13:47:54

AntonE
Replies: 2

Reading the documentation, this seems 90% similar to a library I wrote in BorlandPascal (DOS) with turbo-vision UI in the 80's.
I always thought we should get out of dark ages with 'flat' databases.
Although I understand most of the concepts, it is a slow process to get up to speed.
Is there a complete (client/server)demo(s) available so I can get some sample code? I saw mention of demos, but could not find a download link.

Thanks
AntonE

Board footer

Powered by FluxBB