You are not logged in.
Pages: 1
do you mean that one in unit mORMotMidasVCL?
What about Mormot2 then?,Thank you
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!
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.
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.
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.
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.
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.
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.
Pages: 1