Skip to content

FB4D Reference IFirestoreDatabase

Christoph Schneider edited this page Oct 15, 2021 · 42 revisions

Interface IFirestoreDatabase

This interface provides all functions for accessing the Firestore Database.

Create an instance for the interface IFirestoreDatabase

The interface will be created by the constructor of the class TFirestoreDatabase in the unit FB4D.Firestore. The constructor expects as parameters the Project ID of the Firebase Project and the instance of the interface IFirebaseAuthentication that is in the state signed-in. If you use more than one database in the same project, you must also pass the DatabaseID.

var
  Database: IFirestoreDatabase;
begin
  Database := TFirestoreDatabase.Create(const ProjectID: string; Auth: IFirebaseAuthentication; const DatabaseID: string = DefaultDatabaseID);

Alternatively, you can use the class factory TFirebaseConfiguration.Database to get an instance of IFirestoreDatabase.

Read one Document of a Collection by their Document Id

The method Get retrieves one distinct document from a collection when the collection and the document ID are passed in the params array.

  function GetSynchronous(Params: TRequestResourceParam; 
    QueryParams: TDictionary<string, string> = nil): IFirestoreDocuments;

The following examples show, how a document with a given document ID can be retrieved by using the asynchronous Get method:

Database.Get(['MyCollection', DocumentID: string], nil, OnDocuments, OnError); 

Read all Documents of a Collection

The Get method can also be used to retrieve all documents from a collection. For this purpose, only the collection name shall be passed within the params array. For collections with more than 20 documents (default page size), the Get method shall be called several times until IFirestoreDocuments.MorePagesToLoad is no longer true. Thereby, in the 2nd parameter TQueryParams the last PageToken must be passed.

var 
  DocsFromLastGet: IFirestoreDocuments; 
begin
  Query := TQueryParams.CreateQueryParams.AddPageSize(10);
  if DocsFromLastGet.MorePagesToLoad then
    Query.AddPageToken(DocsFromLastGet.PageToken);
  Database.Get(['MyCollection'], Query, OnDocuments, OnError); 
end;

Create a new Document

The method CreateDocument allows to create of an empty new document for a given document path.

function CreateDocumentSynchronous(DocumentPath: TRequestResourceParam;
  QueryParams: TQueryParams = nil): IFirestoreDocument

Insert or Update all Fields of a Document

The method InsertOrUpdateDocument allows storing a complete document with a set of fields. In case the document for the given DocumentPath exists already this method overwrites the entire document.

function InsertOrUpdateDocumentSynchronous(
  DocumentPath: TRequestResourceParam; Document: IFirestoreDocument;
  QueryParams: TQueryParams = nil): IFirestoreDocument;

Update parts of a Document

The method PatchDocument allows updating only some fields of a document. For this purpose, the parameter UpdateMask contains the list of fields which shall be overwritten from the given DocumentPart. With the empty Mask parameter, the method returns the entire updated document. If a list of fields is defined in the parameter Mask just these fields will be packed into the document that will be returned.

function PatchDocumentSynchronous(DocumentPath: TRequestResourceParam;
  DocumentPart: IFirestoreDocument; UpdateMask: TStringDynArray;
  Mask: TStringDynArray = []): IFirestoreDocument;

Delete a Document in a Collection

The method Delete allows deleting a distinct document in a collection.

function DeleteSynchronous(Params: TRequestResourceParam): IFirebaseResponse;

The status in the response informs if the deletion was successfully done.

Hint: The deprecated version of this method has an additional optional parameter QuerParams which had no functional purpose. The previous method is marked as deprecated and will be removed with version 1.3.

Search for Documents in a Collection by using a Filter Criteria for Fields

The method IFirestoreDatabase.RunQuery allows retrieving data from the Firestore by using a structured query that is comparable to an SQL query statement. As a parameter, an instance to a structured query is expected. It returns by IFirestoreDocuments a list of documents that contains usually just one document.

function RunQuerySynchronous(StructuredQuery: IStructuredQuery): 
  IFirestoreDocuments;

For queries on child collections there is an additional overloaded method available that takes a document path:

function TFirestoreDatabase.RunQuerySynchronous(
  DocumentPath: TRequestResourceParam;
  StructuredQuery: IStructuredQuery): IFirestoreDocuments; overload;

Be aware that the last (lowest) level of the document path must not be written in the DocumentPath because this document name shall be passed in IStructuredQuery.Collection.

Firestore listener

In order to be able to react to changes in the Firestore, a listener has been introduced for the following monitoring functions:

  • Monitoring of a single document
  • Monitoring of a whole collection:
    • A new document has been added
    • A document has been changed
    • A document has been deleted
  • Monitoring a collection with a filter criterion

The listener can monitor several documents and collections at the same time. To do this, the monitoring must first be registered with Subscribe before the listener can be started.

The following function monitors a single document. In case of changes in the document, the OnChanchedDoc call back method will be called and returns the entire document. In case the observed document was deleted, the OnDeletedDoc call back method will be started. You can subscribe to more than one document by a repeated call of SubscribeDocument. The function returns the target ID for this subscription.

function SubscribeDocument(DocumentPath: TRequestResourceParam;
  OnChangedDoc: TOnChangedDocument;
  OnDeletedDoc: TOnDeletedDocument): cardinal;

type
  TOnChangedDocument = procedure(ChangedDocument: IFirestoreDocument) of object;
  TOnDeletedDocument = procedure(const DeleteDocumentPath: string;
    TimeStamp: TDateTime) of object;

To be able to monitor all documents in a collection, use SubscribeQuery. Thereby you can use the filter options to restrict the resulting set of documents by using a query. The sort ordering of the resulting document set is also possible. For each changed or newly created document you get a call of the call back method OnChangedDoc. If a document from a subcollection shall be monitored, the subcollection can be specified with the optional parameter DocumentenPath.

function SubscribeQuery(Query: IStructuredQuery;
  OnChangedDoc: TOnChangedDocument; OnDeletedDoc: TOnDeletedDocument;
  DocumentPath: TRequestResourceParam = []): cardinal;

After stopping the listener, you can change your subscription by removing previous targets with Unscubscribe and adding new ones.

procedure Unsubscribe(TargetID: cardinal);

Finally, you can start the Firestore listener. Thereby giving a method that will be called when the listener stops again. Additionally, you register an event handler for the error case. Optionally, you can be informed when the authorization tokens need to be renewed. If the connection to the Firestore Server will be interrupted, the listener will automatically re-establish the connection as soon as the server is available again. To let the application know if the Firestore Server Connection is active, a callback method for OnConnectionStateChange can be registered. In case the optional flag DoNotSynchronizeEvents is set, the listener calls all callback methods including OnChangedDoc and _OnDeletedDoc from the background thread. This can be useful within a service application or if no GUI update shall be done within the callback methods.

procedure StartListener(OnStopListening: TOnStopListenEvent;
  OnError: TOnRequestError; OnAuthRevoked: TOnAuthRevokedEvent = nil;
  OnConnectionStateChange: TOnConnectionStateChange = nil;
  DoNotSynchronizeEvents: boolean = false);

type
  TOnStopListenEvent = procedure(Sender: TObject) of object;
  TOnRequestError = procedure(const RequestID, ErrMsg: string) of object;
  TOnAuthRevokedEvent = procedure(TokenRenewPassed: boolean) of object;
  TOnConnectionStateChange = procedure(ListenerConnected: boolean) of object;

If you are no longer interested in change notification for the subscriptions or the monitoring targets need to be changed, call StopListener.

procedure StopListener(RemoveAllSubscription: boolean = true);

In case of a connection interruption to the Firestore Server, you would like to know when the last message from the server arrived? The following function returns this local PC time. The last message can be from the listener or from the last Firestore request.

function GetTimeStampOfLastAccess: TDateTime;

Transactions

To read documents from different collections from the same timestamp, you can use a Read-Transaction. For this purpose use the following methods:

TFirestoreDatabase.BeginReadOnlyTransaction -> Transaction: TTransaction;

Query := TQueryParams.CreateQueryParams.AddTransaction(Transaction);
fDatabase.Get(['MyCollection', 'MyDocId'], Query, OnFirestoreGet, OnFirestoreError)

Unfortunately, starting a write transaction in the Firestore is only possible via a cloud function.

Clone this wiki locally