Cloud Firestore

Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform. Like Firebase Realtime Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity.

Add Data to Cloud Firestore

There are several ways to write data to Cloud Firestore:

  • Set the data of a document within a collection, explicitly specifying a document identifier.
  • Add a new document to a collection. In this case, Cloud Firestore automatically generates the document identifier.

This guide explains how to use the set, add, or update individual documents in Cloud Firestore.

Set a Document

To create or overwrite a single document, use the Set method:

BlueprintsC++
#include "Firestore/Firestore.h"
void UMyClass::SetDocument()
{
    // Gets the collection where we want to set a document.
    UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
    
    if (Collection)
    {
        // If the collection is valid, gets the document we want to set.
        UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
        
        if (Document)
        {
            // If the document reference is valid, we set it in Firestore.
            Document->Set
            (
                // The data we want to set.
                {
                    { TEXT("name"),     TEXT("Los Angeles") },
                    { TEXT("state"),    TEXT("CA")          },
                    { TEXT("country"),  TEXT("USA")         }
                },
                
                // An optional callback called when we know the result of the operation.
                FFirestoreCallback::CreateLambda([](const EFirestoreError Error) -> void
                {
                    if (Error == EFirestoreError::Ok)
                    {
                        // Document set.
                    }
                    else
                    {
                        // Document wasn't set, an error occurred.
                    }
                })
            );
        }
        else
        {
            // Failed to get document "LA".
        }
    }
    else
    {
        // Failed to get collection "cities".
    }
}
Set Document Blueprint Example

If the document does not exist, it will be created. If the document does exist, its contents will be overwritten with the newly provided data, unless you specify that the data should be merged into the existing document, as follows:

BlueprintsC++
Document->Set
(
    // The data we want to set.
    {
        { TEXT("name"),     TEXT("Los Angeles") },
        { TEXT("state"),    TEXT("CA")          },
        { TEXT("country"),  TEXT("USA")         }
    },
    
    // The merge option.
    FFirestoreSetOptions::Merge(),
    
    // An optional callback called when we know the result of the operation.
    FFirestoreCallback::CreateLambda([](const EFirestoreError Error) -> void
    {
        if (Error == EFirestoreError::Ok)
        {
            // Document set.
        }
        else
        {
            // Document wasn't set, an error occurred.
        }
    })
);
Blueprint firestore set document example

If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.

Add a Document

When you use Set to create a document, you must specify an ID for the document to create. For example:

BlueprintsC++
Document->Set
(
    // The data we want to set.
    { /* ... */ },
    
    // An optional callback called when we know the result of the operation.
    FFirestoreCallback::CreateLambda(/* ... */)
);
firestore set document blueprint example

But sometimes there isn't a meaningful ID for the document, and it's more convenient to let Cloud Firestore auto-generate an ID for you. You can do this by calling Add:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
if (Collection)
{
    // If the collection is valid, we add the document.
    Collection->Add
    (
        // The data we want to add.
        { /* ... */ },
        
        // An optional callback called when we know the result of the operation.
        FFirestoreCallback::CreateLambda(/* ... */)
    );
}
else
{
    // Failed to get collection, handle error here.
}
firestore add document blueprint example

Unlike "push IDs" in the Firebase Realtime Database, Cloud Firestore auto-generated IDs do not provide any automatic ordering. If you want to be able to order your documents by creation date, you should store a timestamp as a field in the documents.

Update a document

To update some fields of a document without overwriting the entire document, use the update method:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
check(Document);
Document->Update({ TEXT("capital"), true }, FFirestoreCallback::CreateLambda(/* ... */));
firestore update document blueprint code example

Server Timestamp

You can set a field in your document to a server timestamp which tracks when the server receives the update.

BlueprintsC++
TMap<FString, FFirestoreFieldValue> Values = 
{
    { /* ... */ },
    { TEXT("Time"), FFirestoreFieldValue::ServerTimestamp() }
}
firestore blueprint code example server timestamp

Delete Data from Cloud Firestore

The following examples demonstrate how to delete documents, fields, and collections.

Delete documents

To delete a document, use the Delete method:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("DC"));
check(Document);
Document->Delete(FFirestoreCallback::CreateLambda([](const EFirestoreError Error) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        // Document deleted.
    }
    else
    {
        // Failed to delete document.
    }
}));
firestore delete document blueprint code example

Warning: Deleting a document does not delete its subcollections!

When you delete a document, Cloud Firestore does not automatically delete the documents within its subcollections. You can still access the subcollection documents by reference. For example, you can access the document at path /mycoll/mydoc/mysubcoll/mysubdoc even if you delete the ancestor document at /mycoll/mydoc.

Non-existent ancestor documents appear in the console, but they do not appear in query results and snapshots.

Delete fields

To delete specific fields from a document, use the FFirestoreFieldValue::Delete() method when you update a document:

BlueprintsC++
Document->Update({ TEXT("capital"), FFirestoreFieldValue::Delete() });
firestore delete field blueprint code example

Get data with Cloud Firestore

There are two ways to retrieve data stored in Cloud Firestore. Either of these methods can be used with documents, collections of documents, or the results of queries:

  • Call a method to get the data.
  • Set a listener to receive data-change events.

When you set a listener, Cloud Firestore sends your listener an initial snapshot of the data, and then another snapshot each time the document changes.

Get a document

The following example shows how to retrieve the contents of a single document using Get:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
check(Document);
Document->Get(FDocumentSnapshotCallback::CreateLambda([](const EFirestoreError Error, const FFirestoreDocumentSnapshot& Snapshot) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        // Access the document snapshot here.
        if (Snapshot.Exists())
        {
            // Document found.
        }
        else
        {
            // Document not found.
        }
    }
    else
    {
        // Handle error here.
    }
}));
firestore get document blueprint code example

Note: If there is no document at the location referenced by docRef, the resulting document will be empty and calling exists on it will return false.

Source Options

For platforms with offline support, you can set the source option to control how a get call uses the offline cache.

By default, a get call will attempt to fetch the latest document snapshot from your database. On platforms with offline support, the client library will use the offline cache if the network is unavailable or if the request times out.

You can specify the source option in a Get call to change the default behavior. You can fetch from only the database and ignore the offline cache, or you can fetch from only the offline cache. For example:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
check(Document);
Document->Get
(
    /* We explicitly ask for data in the cache. */
    EFirestoreSource::Cache,
    
    /* Our callback as before. */
    FDocumentSnapshotCallback::CreateLambda([](const EFirestoreError Error, const FFirestoreDocumentSnapshot& Snapshot) -> void
    {
        if (Error == EFirestoreError::Ok)
        {
            // Access the document snapshot here.
            if (Snapshot.Exists())
            {
                // Document found.
            }
            else
            {
                // Document not found.
            }
        }
        else
        {
            // Handle error here.
        }
    })
);
firestore get option blueprint code example

Get Multiple Documents from a Collection

You can also retrieve multiple documents with one request by querying documents in a collection. For example, you can use Where to query for all of the documents that meet a certain condition, then use Get to retrieve the results:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreQuery* const Query = Collection->WhereEqualTo(TEXT("capital"), true);
check(Query);
Query->Get(FFirestoreQueryCallback::CreateLambda([](EFirestoreError Error, TArray<FFirestoreDocumentSnapshot> Snapshots, TArray<UFirestoreDocumentChange*> Changes) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        // Access the documents here.
    }
    else
    {
        // Failed to get documents.
    }
}));
firestore get query blueprint code example

By default, Cloud Firestore retrieves all documents that satisfy the query in ascending order by document ID, but you can order and limit the data returned.

Get All Documents in a Collection

In addition, you can retrieve all documents in a collection by omitting the Where filter entirely:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
Collection->Get(FFirestoreQueryCallback::CreateLambda([](EFirestoreError Error, TArray<FFirestoreDocumentSnapshot> Snapshots, TArray<UFirestoreDocumentChange*> Changes) -> void
{
    /* ... */
});
firestore get all documents blueprint code example

Get Realtime Updates with Cloud Firestore

You can listen to a document with the help of listeners. An initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document. Then, each time the contents change, another call updates the document snapshot.

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
check(Document);
Document->AddSnapshotListener(FDocumentSnapshotListenerCallback::CreateLambda([](const EFirestoreError Error, const FFirestoreDocumentSnapshot& Snapshot, const TArray<class UFirestoreDocumentChange*>& Changes) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        // The document changed. We can access it with the snapshot.
    }
    else
    {
        // An error occurred.
    }
}));
firestore listen events blueprint code example

Events for local changes

Local writes in your app will invoke snapshot listeners immediately. This is because of an important feature called "latency compensation." When you perform a write, your listeners will be notified with the new data before the data is sent to the backend.

Retrieved documents have a Metadata.HasPendingWrites property that indicates whether the document has local changes that haven't been written to the backend yet. You can use this property to determine the source of events received by your snapshot listener:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreDocumentReference* const Document = Collection->GetDocumentFromPath(TEXT("LA"));
check(Document);
Document->AddSnapshotListener(FDocumentSnapshotListenerCallback::CreateLambda([](const EFirestoreError Error, const FFirestoreDocumentSnapshot& Snapshot, const TArray<class UFirestoreDocumentChange*>& Changes) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        if (Snapshot.GetMetadata().bHasPendingChanges)
        {
            // Local change.
        }
        else
        {
            // Server change.
        }
    }
    else
    {
        // An error occurred.
    }
}));
firestore listeners server blueprint code example

Listen to Multiple Documents in a Collection

As with documents, you can use AddSnapshotListener() instead of Get() to listen to the results of a query. This creates a query snapshot. For example, to listen to the documents with state CA:

BlueprintsC++
UFirestoreCollectionReference* const Collection = UFirestore::GetCollection(TEXT("cities"));
check(Collection);
UFirestoreQuery* const Query = Collection->WhereEqualTo(TEXT("state"), TEXT("CA"));
check(Query);
Query->AddSnapshotListener(FQuerySnapshotListenerCallback::CreateLambda([](const EFirestoreError Error, const TArray<FFirestoreDocumentSnapshot>& Snapshots, const TArray<class UFirestoreDocumentChange*>& Changes) -> void
{
    if (Error == EFirestoreError::Ok)
    {
        // The documents changed. We can access them with the snapshots.
    }
    else
    {
        // An error occurred.
    }
}));
firestore setup listeners blueprint code example

The snapshot handler will receive a new query snapshot every time the query results change (that is, when a document is added, removed, or modified).

Important: You will receive events immediately for your local writes. Your listener can use the Metadata.bHasPendingWrites field on each document to determine whether the document has local changes that have not yet been written to the backend.