-
-
Notifications
You must be signed in to change notification settings - Fork 54
FB4D Reference IFirestoreDocument
The Firestore database stores and retrieves documents with structured data. Documents can contain either a simple list of fields as well as complex nested objects organized in arrays and records (maps). Documents are organized in collections. In most cases, a collection contains multiple identical structured documents comparable to records in a table of a relational database. However, a collection is more flexible and may contain differently structured documents.
In FB4D, the interface IFirestoreDocument enables read access to documents retrieved from the Firestore and write access to documents that will be written to the Firestore database.
The constructor TFirestoreDocument.Create
in the unit FB4D.Document
allows to create an empty document that can be written later into the database by using the method IFirestoreDatabase.InsertOrUpdateDocument
or IFirestoreDatabase.PatchDocument
.
To add content to the document, the AddOrUpdateField
method allows you to insert a new field or, if the field already exists, to overwrite it with new content. As field value a JSON value is expected. Because the Firestore expects field data with the field name, the declared data type and the value in a Firestore specific JSON format.
For this purpose the unit FB4D.Helpers
contains a class helper for TJSONObject
that offers several class functions to build all types of Firestore fields. The following example shows how easy it is to generate a string value that is added to the field named strField
:
Doc.AddOrUpdateField(TJSONObject.SetString('strField', 'This is the string value'));
The following field types are supported in Firestore and FB4D:
TFirestoreFieldType = (fftNull, fftBoolean, fftInteger, fftDouble,
fftTimeStamp, fftString, fftBytes, fftReference, fftGeoPoint, fftArray,
fftMap);
For this purpose the TJSONHelper
class helper offers these functions that returns a TJSONPair
for the field name and the field body.
class function SetString(const VarName, Val: string): TJSONPair;
class function SetInteger(const VarName: string; Val: integer): TJSONPair;
class function SetBoolean(const VarName: string; Val: boolean): TJSONPair;
class function SetDouble(const VarName: string; Val: double): TJSONPair;
class function SetTimeStamp(const VarName: string; Val: TDateTime): TJSONPair;
class function SetNull(const VarName: string): TJSONPair;
class function SetReference(const Name, ProjectID, Ref: string): TJSONPair;
class function SetGeoPoint(const VarName: string; Val: TLocationCoord2D): TJSONPair;
class function SetBytes(const VarName: string; Val: TBytes): TJSONPair;
class function SetMap(const VarName: string; MapVars: array of TJSONPair): TJSONPair;
class function SetArray(const VarName: string; ArrayVars: array of TJSONValue): TJSONPair
The type array and map are nested data types that are built by combinations of other simple data types.
The map type is comparable to a record in Pascal. The following example creates a map that contains two subfields: The string field MapStr
and the integer field MapInt
:
IFirestoreDocument.AddOrUpdateField(TJSONObject.SetMap('MyMap', [
TJSONObject.SetString('MapStr', 'Map would be called in Delphi "record"'),
TJSONObject.SetInteger('MapInt', 324)]);
The array type is more flexible than its pendant in Pascal. The array type is more flexible than an array in Pascal because each element can store a different type. Unlike the type map, the array does not have a field name per element.
In order to build nested data structures with arrays, the TJSONHelper
class helper offers for each field type a second class functions to build just the field body without a field name. This second function is named Set<Type>Value
and requires only the field value as a parameter:
class function SetString(const VarName, Val: string): TJSONPair;
class function SetStringValue(const Val: string): TJSONObject;
The following examples creates an array that contains three elements: Firstly a string, secondly an integer and thirdly a boolean field:
TJSONObject.SetArray('MyArr',
[TJSONObject.SetStringValue('Element0'),
TJSONObject.SetIntegerValue(1),
TJSONObject.SetBooleanValue(true)]);
In arrays, each element often contains an identical map as following example shows:
TJSONObject.SetArray('MyCompoundArr',
[TJSONObject.SetMapValue(
[TJSONObject.SetString('FieldA', 'Delphi rocks with Firebase 👨'),
TJSONObject.SetInteger('FieldB', 4711)],
[TJSONObject.SetMapValue(
[TJSONObject.SetString('FieldA', 'A second map element'),
TJSONObject.SetInteger('FieldB', 4712)]]);
For the sake of completeness, it should also be mentioned that the function AddOrUpdateField
offers an overloaded variant with field name and field-body as a parameter:
IFirestoreDocument = interface(IInterface)
function AddOrUpdateField(Field: TJSONPair): IFirestoreDocument; overload;
function AddOrUpdateField(const FieldName: string; Val: TJSONValue): IFirestoreDocument; overload;
Hint: The AddOrUpdateField
method supports optionally the use of fluent interface design os you can easily add multiple fields in one step without the need of store the IFirestoreDocument
in a local variable because you can pass it directly to the consuming method e.g. CreateDocument
.
When you read a document from the database, the document object is usually created by IFirestoreDatabase. Therefore, the document does not have to be created in the application code.
Following read functions offers to read the header information and the structure of a document:
FirestoreDocument = interface(IInterface)
function DocumentName(FullPath: boolean): string;
function DocumentFullPath: TRequestResourceParam;
function DocumentPathWithinDatabase: TRequestResourceParam;
function CreateTime: TDateTime;
function UpdateTime: TDatetime;
function CountFields: integer;
function Fields(Ind: integer): TJSONObject;
function FieldName(Ind: integer): string;
function FieldByName(const FieldName: string): TJSONObject;
function FieldValue(Ind: integer): TJSONObject;
function FieldType(Ind: integer): TFirestoreFieldType;
function FieldTypeByName(const FieldName: string): TFirestoreFieldType;
The function DocumentName
returns the entire path of the document in the Firestore if the boolean parameter is true. If false, the function returns only the document ID in the surrounding collection.
The function DocumentFullPath
returns the entire path of the document including the project and database identifier split into an array of strings.
The function DocumentPathWithinDatabase
returns do the same but without the project and database identifier.
The function CreateTime
returns the time when a document was first written into the database. As long the document is still under construction in FB4D there is no creation time available.
The function UpdateTime
returns the timestamp of the last write access for this document.
With the function CountFields
and the function Fields(Ind)
you can read out the root fields of the document. The function FieldType(Ind)
informs about the field type (fftNull..fftMap)
of the field.
Most of the time you have an expectation regarding the field structure of the read document, so you can use the function FieldByName
which returns a TJSONObject
. In this way, you can quickly access their field contents via the name and the known field type. With the help of the class helper for TJSONObject
in the unit FB4D.Helpers
you can easily read a value of the known type.
For root fields, the interface IFirestoreDocument provides an even faster way:
function IFirestoreDocument.GetStringValue(StringFieldName: string): string;
function IFirestoreDocument.GetIntegerValue(IntegerFieldName: string): integer;
function IFirestoreDocument.GetDoubleValue(DoubleFieldName: string): double;
function IFirestoreDocument.GetTimeStampValue(TimeStampFieldName: string): TDateTime;
function IFirestoreDocument.GetBoolValue(BooleanFieldName: string): boolean;
function IFirestoreDocument.GetGeoPoint(GeoFieldName: string): TLocationCoord2DValue;
function IFirestoreDocument.GetReference(ReferenceFieldName: string): string;
function IFirestoreDocument.GetBytes(ByteFieldName: string): TBytes;
If the field in the document is optional, there are additional methods for each field type that require a default value. While GetStringValue
throws an exception if the field is not present in the document, the function GetStringValueDef(const FieldName, Default: string): string
returns the default value in the same situation.
For read arrays, the IFirestoreDocument interface offers the following functions:
function GetArraySize(const FieldName: string): integer;
function GetArrayType(const FieldName: string; Index: integer): TFirestoreFieldType;
function GetArrayItem(const FieldName: string; Index: integer): TJSONPair;
function GetArrayValue(const FieldName: string; Index: integer): TJSONValue;
function GetArrayValues(const FieldName: string): TJSONObjects;
While GetArraySize
returns the number of element, the function GetArrayType(Index)
informs about the element type which can be different for each element! Note that in this point an array of Firebase differs significantly from an array in Delphi.
In order to get the element of an array, you can use the method GetArrayValue(Index)
that returns a TJSONValue. To access the value of such array element, use the same class helper functions as GetStringValue
and GetIntegerValue
without passing in a field name because all the elements in an array are nameless.
For sub-arrays, the TJSONObject
class helper function GetArraySize
returns the number of elements in an array. To access an element of the array, you can use the following class helper function:
function GetArrayItem(Ind: integer): TJSONObject;
Often in Firebase documents, data are structured within an array of maps. To access directly the elements of the maps the following method can be used:
function GetArrayMapValues(const FieldName: string): TJSONObjects;
For read maps (similar to records in Delphi), the IFirestoreDocument interface offers the following functions:
function GetMapSize(const FieldName: string): integer;
function GetMapType(const FieldName: string; Index: integer): TFirestoreFieldType;
function GetMapValue(const FieldName: string; Index: integer): TJSONValue; overload;
function GetMapValue(const FieldName, SubFieldName: string): TJSONObject; overload;
function GetMapValues(const FieldName: string): TJSONObjects;
For fast access of a variable in a map use the function GetMapValue(const FieldName, SubFieldName: string).
E.g. after creating a complex document in the DemoFB4D application, you can address the MapStr
in MyMap
with this new function as follows:
Doc.GetMapValue('MyMap', 'MapStr').GetStringValue
Alternatively, for sub-maps, you get the number of elements by the TJSONObject
class helper method GetMapSize
. To access an element of the map, you can use the following class helper function:
function GetMapItem(Ind: integer): TJSONPair;
Here you get a TJSONPair
that contains the name of the element and the value as TJSONObject
. Note that elements in maps are not nameless like elements of arrays.
Have you discovered an error? Or is something unclear? Please let us know in the discussion forum.
Schneider Infosystems Ltd. CH-6340 Baar, Switzerland, www.schneider-infosys.ch
Introduction into FB4D
Getting Started
Fundamental Principles
Project Settings
GUI Pattern Self-Registration
RT-DB Sample Applications
Firestore Chat Sample Applications
PhotoBox demonstrates Firestore, Storage, VisionML
Interface Reference
Configuration and Class Factory
Helper Classes
Authentication