#1 Re: mORMot 1 » ORM and Dynamic models » 2024-04-01 17:01:52

Vit

do you mean that one in unit mORMotMidasVCL?
What about Mormot2 then?,Thank you

#2 Re: mORMot 1 » ORM and Dynamic models » 2024-04-01 12:46:54

Vit

Hello everyone.
I decided to post my next question here, which is only indirectly related to this discussion.
1) separate column for each field works as expected (made according this topic)
2) in order to somehow show it in theG UI, I decided to transfer the entire returned set (array) to the clientdataset. It give me yje same power as dataset.FieldByName etc... Wouldn't this be a bottleneck?

uses
  SysUtils,
  SynDB,
  SynCommons,
  DB;

procedure VariantArrayToDataSet(const Values: TVariantDynArray; DataSet: TDataSet);
var
  i, j: Integer;
begin
  // Clear the dataset before adding new records
  DataSet.Close;
  DataSet.Fields.Clear;
  for i := Low(Values) to High(Values) do
    DataSet.Fields.Add(TStringField.Create(DataSet)).FieldName := 'Field' + IntToStr(i);

  DataSet.Open;
  for j := 0 to High(Values[0]) do // Assuming all rows have the same length
  begin
    DataSet.Append;
    for i := Low(Values) to High(Values) do
      DataSet.Fields[i].Value := Values[i][j];
    DataSet.Post;
  end;
end;

procedure TestVariantArrayToDataSet;
var
  VariantArray: TVariantDynArray;
  DataSet: TClientDataSet;
begin
  // Sample array of variants
  SetLength(VariantArray, 3);
  VariantArray[0] := VarArrayOf(['Value 1', 'Value 2', 'Value 3']);
  VariantArray[1] := VarArrayOf([123, 456, 789]);
  VariantArray[2] := VarArrayOf([True, False, True]);

  // Create a TClientDataSet
  DataSet := TClientDataSet.Create(nil);
  try
    // Convert array of variants to TDataSet
    VariantArrayToDataSet(VariantArray, DataSet);

    // Output the dataset
    DataSet.First;
    while not DataSet.Eof do
    begin
      Writeln(DataSet.Fields[0].AsString, ', ', DataSet.Fields[1].AsInteger, ', ', DataSet.Fields[2].AsBoolean);
      DataSet.Next;
    end;
  finally
    DataSet.Free;
  end;
end;

begin
  TestVariantArrayToDataSet;
end.

Thanks a lot!

#3 Re: mORMot 1 » ORM and Dynamic models » 2024-03-17 16:56:16

Vit

Hopefully with such a excellent assistent I've finally got it!
Thank you so much.
Only the question, how to work with data?
I have now harcoded dummy string, but do I have to fill this TDocVariant and extract data from there or even it does not needed at all?

procedure TMainForm.ButtonAddClick(Sender: TObject);
var
  Rec: TOrmSample;
  Value: PVariant;

  function MyDummy: Variant;
  var
    doc: TDocVariantData;
  begin
    doc.InitFast(dvObject);
    doc.AddValue('Dummy', 'Here is dummy text');
    Result := Variant(doc);
  end;

begin
  Rec := TOrmSample.Create;
  try
    Rec.Name := StringToUTF8(NameEdit.Text);
    Rec.Question := StringToUTF8(QuestionMemo.Text);
    Rec.DummyData := MyDummy;                               <- this is all-in-one JSON field
    Rec.FMyValues[0] := 'Here is dummy text';               <- my dedicated field

Thank you so much again! thats amazing result for me.

#4 Re: mORMot 1 » ORM and Dynamic models » 2024-03-16 14:56:17

Vit

Thank you so much for help
A bit closer
This code working at least when row with ORM added , but field is empty. For some reason no one method (GetValue, SetValue etc) is fired.

const
  HttpPort = '11111';

type
  TPropInfo = packed record
      Name: RawUtf8;
      FieldType: TOrmFieldType;
      FieldWidth: Integer;
  end;

  TPropInfoDynArray = array of TPropInfo;

  TPropValueArray = array [0..MAX_SQLFIELDS - 1] of Variant;

  TOrmPropInfoMyField = class(TOrmPropInfoCustom)
  public
    procedure SetValue(Instance: TObject; Value: PUtf8Char; ValueLen: PtrInt; wasString: boolean);
    procedure GetValueVar(Instance: TObject; ToSQL: boolean; var result: RawUTF8; wasSQLString: PBoolean); override;
    procedure SetBinary(Instance: TObject; var Read: TFastReader); override;
    procedure GetBinary(Instance: TObject; W: TFileBufferWriter); override;
    procedure NormalizeValue(var Value: RawUTF8); override;
  end;

  TOrmSample = class(TOrm)
  private
    FName: RawUTF8;
    FQuestion: RawUTF8;
    FTime: TModTime;
    FDummyData: Variant;

    class constructor Create;
    class destructor Destroy;
    constructor Create;
  protected
    class procedure Init;
    class procedure InternalRegisterCustomProperties(Props: TOrmProperties); override;
  published
    property Name: RawUTF8 read FName write FName;
    property Question: RawUTF8 read FQuestion write FQuestion;
    property Time: TModTime read FTime write FTime;
    property DummyData: Variant read FDummyData write FDummyData;
  public
    class var
      FMyProps: TPropInfoDynArray;
    FMyValues: TPropValueArray;
  end;

  function CreateSampleModel: TOrmModel;

implementation

function CreateSampleModel: TOrmModel;
begin
  Result := TOrmModel.Create([TOrmSample]);
end;

{ TOrmSample }

class constructor TOrmSample.Create;
begin
  inherited;

  Init;
end;

constructor TOrmSample.Create;
begin
  inherited Create;
end;

class destructor TOrmSample.Destroy;
begin

  inherited;
end;

class procedure TOrmSample.Init;
begin
  SetLength(self.FMyProps, 1);
  self.FMyProps[0].Name := 'DummyColumn';
  self.FMyProps[0].FieldType := oftUtf8Text;
  self.FMyProps[0].FieldWidth := 255;
end;

class procedure TOrmSample.InternalRegisterCustomProperties(
  Props: TOrmProperties);
var
    i: Integer;
begin
    inherited InternalRegisterCustomProperties(Props);

    for i := 0 to Length(FMyProps) - 1 do
        Props.Fields.Add(
          TOrmPropInfoMyField.Create(
            {aName=}          FMyProps[i].Name,
            {aOrmFieldType=}  FMyProps[i].FieldType,
            {aAttributes=}    [],
            {aFiledWidth=}    FMyProps[i].FieldWidth,
            {aPropIndex=}     i,
            {aProperty=}      @TOrmSample(nil).FMyValues[i],
            {aData2Text=}     nil,
            {aTexttoData=}    nil
          )
        );
end;

{ TOrmPropInfoMyField }

procedure TOrmPropInfoMyField.NormalizeValue(var Value: RawUTF8);
begin // do nothing
end;

procedure TOrmPropInfoMyField.GetBinary(Instance: TObject; W: TFileBufferWriter);
var JSON: RawUTF8;
begin
  //
end;

procedure TOrmPropInfoMyField.SetBinary(Instance: TObject; var Read: TFastReader);
begin
  //
end;

procedure TOrmPropInfoMyField.GetValueVar(Instance: TObject; ToSQL: boolean; var result: RawUTF8; wasSQLString: PBoolean);
var
    wasString: Boolean;
    V: PVariant;
begin
    V:= GetFieldAddr(Instance);
    VariantToUTF8(V^, result, wasString);
    if wasSQLString <> nil then
        wasSQLString^ := not VarIsEmptyOrNull(V^);
end;

procedure TOrmPropInfoMyField.SetValue(Instance: TObject; Value: PUtf8Char; ValueLen: PtrInt; wasString: boolean);
var
    V: PVariant;
    tmp: TSynTempBuffer;
begin
    V := GetFieldAddr(Instance);
    if ValueLen > 0 then
    begin
        tmp.Init(Value, ValueLen);
        try
            GetVariantFromJsonField(tmp.buf, wasString, V^, nil);
        finally
            tmp.Done;
        end;
    end
    else
        VarClear(V^);
end;

end.

Here is "adding row" code:

var
  Rec: TOrmSample;
  Value: PVariant;

  function MyDummy: Variant;
  var
    doc: TDocVariantData;
  begin
    doc.InitFast(dvObject);
    doc.AddValue('Dummy', 'Here is dummy text');
    Result := Variant(doc);
  end;

begin
  Rec := TOrmSample.Create;
  try
    Rec.Name := StringToUTF8(NameEdit.Text);
    Rec.Question := StringToUTF8(QuestionMemo.Text);
    Rec.DummyData := MyDummy;

    Rec.FMyValues[0] := '123';

    //Rec.OrmProps.Fields

    if HttpClient.Orm.Add(Rec, True) = 0 then
      ShowMessage('Error adding the data') else begin
      NameEdit.Text := '';
      QuestionMemo.Text := '';
      NameEdit.SetFocus;
    end;
  finally
    Rec.Free;
  end;
end;

But again I see this "abstract error" when try to read already added data:

var
  Rec: TOrmSample;
begin
  Rec := TOrmSample.Create(HttpClient.Orm,'Name=?',[StringToUTF8(NameEdit.Text)]);
  try
    if Rec.ID=0 then
      QuestionMemo.Text := 'Not found' else
      QuestionMemo.Text := UTF8ToString(Rec.Question);
  finally
    Rec.Free;
  end;
end;

Probably this feature (dedicated field) requires more  / deep knowledge.
I know, result is pretty close, but I can not it implement.

Thank you for help so much.

P.S. I have those "guid" example, it works, but can not do something similar.

#5 Re: mORMot 1 » ORM and Dynamic models » 2024-03-15 12:50:50

Vit

Thank you so much for examples.

I see that solution one working aout of the box (no need to extra code), but second one is tricky and still have questions.
Here is my example:

const
  HttpPort = '11111';

type
  TPropInfo = packed record
      Name: RawUtf8;
      FieldType: TOrmFieldType;
      FieldWidth: Integer;
  end;

  TPropInfoDynArray = array of TPropInfo;

  TPropValueArray = array [0..MAX_SQLFIELDS - 1] of Variant;

  TOrmPropInfoMyField = class(TOrmPropInfoCustom)
  public
    //
  end;

  TOrmSample = class(TOrm)
  private
    FName: RawUTF8;
    FQuestion: RawUTF8;
    FTime: TModTime;
    FDummyData: Variant;

    class constructor Create;
    class destructor Destroy;
    constructor Create;
  protected
    class procedure Init;
    class procedure InternalRegisterCustomProperties(Props: TOrmProperties); override;
  published
    property Name: RawUTF8 read FName write FName;
    property Question: RawUTF8 read FQuestion write FQuestion;
    property Time: TModTime read FTime write FTime;
    property DummyData: Variant read FDummyData write FDummyData;
  public
    class var
        FMyProps: TPropInfoDynArray;
    FMyValues: TPropValueArray;
  end;

  function CreateSampleModel: TOrmModel;

implementation

function CreateSampleModel: TOrmModel;
begin
  result := TOrmModel.Create([TOrmSample]);
end;

{ TOrmSample }

class constructor TOrmSample.Create;
begin
  inherited;

  Init;
end;

constructor TOrmSample.Create;
begin
  inherited Create;
end;

class destructor TOrmSample.Destroy;
begin

  inherited;
end;

class procedure TOrmSample.Init;
begin
  SetLength(self.FMyProps, 1);
  self.FMyProps[0].Name := 'DummyColumn';
  self.FMyProps[0].FieldType := oftUtf8Text;
  self.FMyProps[0].FieldWidth := 255;
end;

class procedure TOrmSample.InternalRegisterCustomProperties(
  Props: TOrmProperties);
var
    i: Integer;
begin
    inherited InternalRegisterCustomProperties(Props);

    for i := 0 to Length(FMyProps) - 1 do
        Props.Fields.Add(
          TOrmPropInfoMyField.Create(
            {aName=}          FMyProps[i].Name,
            {aOrmFieldType=}  FMyProps[i].FieldType,
            {aAttributes=}    [],
            {aFiledWidth=}    FMyProps[i].FieldWidth,
            {aPropIndex=}     i,
            {aProperty=}      @TOrmSample(nil).FMyValues[i],
            {aData2Text=}     nil,
            {aTexttoData=}    nil
          )
        );
end;

DummyData as entire JSON working as expected, but DummyColumn - not.

I'm stuck on last step - getting value - GetFieldAddr(Instance);
I Have now this class, I have field description added, but it i snot enough

  TOrmPropInfoMyField = class(TOrmPropInfoCustom)
  public
    //
  end;

In client as long as I call Add, there is error "abstract error"

procedure TMainForm.ButtonAddClick(Sender: TObject);
var
  Rec: TOrmSample;
  Value: PVariant;

  function MyDummy: Variant;
  var
    doc: TDocVariantData;
  begin
    doc.InitFast(dvObject);
    doc.AddValue('Dummy', 'Here is dummy text');
    Result := Variant(doc);
  end;

begin
  Rec := TOrmSample.Create;
  try
    Rec.Name := StringToUTF8(NameEdit.Text);
    Rec.Question := StringToUTF8(QuestionMemo.Text);
    Rec.DummyData := MyDummy;

    Rec.FMyValues[0] := '123';

    //Rec.OrmProps.Fields

    if HttpClient.Orm.Add(Rec, True) = 0 then
      ShowMessage('Error adding the data') else begin
      NameEdit.Text := '';
      QuestionMemo.Text := '';
      NameEdit.SetFocus;
    end;
  finally
    Rec.Free;
  end;
end;

Have no idea wath is wrong. But (let say if second version is working), can be initial field (DummyData) be omitted entirelly?

THank you so much. Almost there, but can't fix it itself.

#6 Re: mORMot 1 » ORM and Dynamic models » 2024-03-14 16:45:07

Vit

Probably its quite a simple for you, but I did not get it working.
I have created decsendent this way:

type
  TDummyPropInfo = class(TOrmPropInfoCustom)
    //
  end;
  TOrmFile = class(TOrm)
  private
    FMyProps: TDummyPropInfo;
    FTitle: RawUtf8;
    FComment: RawUtf8;

But can't implement RegisterCustomProperties  - error "Instanse member FMyProps inaccessible here":

class procedure TOrmFile.InternalRegisterCustomProperties(Props: TOrmProperties);
begin
    inherited InternalRegisterCustomProperties(Props);
    for i := 0 to Length(FMyProps) - 1 do

Anyway I did other test (copy / paste), with way working (I see new dedicated field):

 
 Props.RegisterCustomPropertyFromTypeName(
    self,
    'TGUID',
    'GUID',
    @TOrmFile(nil).fGUID,
    [aIsUnique],
    38
  );

How I can see initialize empty TDocvariant (and query the structure)? The only way I now so far is:

var
  doc: TDocVariantData;
begin
  doc.InitFast(dvObject);
  doc.AddValue('Creator', pmcCreator);
  doc.AddValue('Location', pmcLocation);
  doc.AddValue('Latitude', pmLatitude);
  doc.AddValue('Longitude', pmLongitude);
  doc.AddValue('Date', DateToIso8601(pmDate, True));
  doc.AddValue('Time', TimeToIso8601(pmTime, True));
  Result := Variant(doc);

But I want somwthing like:

 
  TOrmFile = class(TOrm)
  private
    FMyDummy: TDocVariantData; <- and some existing structure already here

You can see, I'm pretty beginner, but it is quite interesting framework in general and this topic as well.

Thank you in advance.

#7 Re: mORMot 1 » ORM and Dynamic models » 2024-03-12 17:07:55

Vit

Thank you.
I did it already :-)

And still have those quastions.

But suppose I have this shortened class / table - for example any register, like customers:
ID integer
Name varchar
This example (from link above) can be easily implemented for that case.

But what if client1 (let assume this possibility already exists) has add field:
X integer
But client2 has added another / different Y boolen field.

I want to have "core" / pure structure and plus all what client added.

As I realized, 1) ORM can not be omitted entirely?
2) Not going so deep, but seems like all TDocvariant (entire JSON / object) - image with all properties in example mentioned - will be put in ONE separate field. Am I right?

Thanks a lot.

#8 Re: mORMot 1 » ORM and Dynamic models » 2024-03-12 10:48:04

Vit

Hello there
This topic very interesting for me, as looking for any ORM with possibility to add fields on the fly (for example customized by user, but not available in "base" class).
What I understood I have to:
1) use not relational model, but aggregate model instead
2) use TDocVariant 

This is not clear for me, but will my possible solution "map" on DB field to one vaiants property?

Do Mormot fw have such a example? ORM implementation widely found in examples, but this - not.

Thank in advance.

Board footer

Powered by FluxBB