Skip to content

Firestore

Kyle Szklenski edited this page Jun 27, 2024 · 28 revisions

Contents on this page:


Class Table

Class Description
Firestore Firestore module, lets user connect to collections, issue queries and request a list of documents/collections at a specified path.
FirestoreCollection A node reference to a collection in Firestore, used to call functions to manage documents or nested collections.
FirestoreDocument A node representing a fetched, updated or deleted document in Firestore.
FirestoreQuery An object representing a query to Firestore, used as a parameter for the query() function.
FirestoreListener A node created in order to listen to changes on a given document.
FirestoreListenerConnection A reference to the listener so that when you are done listening to changes on a document, you can stop making requests; note that if you can stop a listener and be happy about it, you should do so, as even at the current minimum polling time - 2 minutes per request - it can be expensive over time. In the future, when Godot supports HTTP2+ requests, this will be refactored completely to use proper requests to the Listen endpoint instead of polling.
FirestoreTransform Base class to support future work with transforms.
FieldTransform Base class for all FieldTransforms.
DecrementTransform FieldTransform designed to reduce the value of a field by a given amount; note this happens atomically, so no need to attempt to start a transaction, as commit already does transactions; finally, also note that DecrementTransform does not exist in the real Firebase Client SDKs. The reason it exists here is simply for utility, as I don't believe calling something an IncrementTransform should be allowed to reduce the value of a field conceptually.
IncrementTransform FieldTransform designed to increase the value of a field by a given amount; note this happens atomically, so no need to attempt to start a transaction, as commit already does transactions; also note that this is functions slightly differently from the standard Firebase Client SDKs since those allow reduction of the value.
ServerTimestampTransform FieldTransform designed to set the value of the given field to a timestamp generated in UTC by the server.
MaxTransform FieldTransform designed to set the value of the given field the max of the field's current value and the value passed in.
MinTransform FieldTransform designed to set the value of the given field the min of the field's current value and the value passed in.


Firestore

Firebase.Firestore

Functions Description
collection(collection_path: String) -> FirestoreCollection Create a node reference to a collection inside Firestore. Returns the associated node.
list(collection_path: String) -> Array List all contents of a collection in Firestore.
Returns an Array with the list of available collections.
query(firestore_query: FirestoreQuery) -> Array Issue a query on Firestore.
Returns an Array with all document instances meeting the query criteria.

Properties Description
collections: Dictionary A Dictionary containing all referenced collections.

Signals Description
error(code, status, message) Emitted when a request is not processed successfully.

***

FirestoreCollection

Firebase.Firestore.collection()

Functions                                          Description
add(document_id : String, data : Dictionary) -> FirestoreDocument Adds a FirestoreDocument to a collection.
If document_id is an empty string, the id will be randomly generated by Firebase. Must be awaited/yielded to get the resulting document.
update(document : FirestoreDocument) -> FirestoreDocument Update a specified document in a collection. Must be awaited/yielded to get the resulting document.
get_doc(document_id : String, from_cache : bool = false, is_listener : bool = false) -> FirestoreDocument Get a specific document; from_cache will return a cached version of the document automatically, while is_listener is an internal-only boolean but necessary as a parameter here. Must be awaited/yielded to get the resulting document.
delete(document : FirestoreDocument) -> bool Delete a specific document. Must be awaited/yielded to get the resulting boolean value.
commit(document : FirestoreDocument) -> Dictionary Commits the current list of document field transforms which can then apply server-side data to each field within your document; transforms are cleared out automatically after commit is successful, so that they aren't applied again later by accident. This returns all changes which were made to the document in a Dictionary. See: https://github.com/GodotNuts/GodotFirebase/blob/4.x/addons/godot-firebase/firestore/firestore_document.gd#L35 for the format of said Dictionary. May be updated in the future to return a specific type. This is only defined for the 4.x branch, and must be awaited in order to get the Dictionary of changes back.

Properties Description
collection_name: String The referenced collection's name

Signals Description
error(error_result) Emitted when a request has not processed correctly.


FirestoreDocument

Returned from several functions within the FirestoreCollection class

Functions Description
is_null_value(key : String) -> bool Returns true if the field represented by key is represented by a null value. Note you cannot just check null within the document Dictionary anymore to get a valid value!
add_field_transform(transform : FieldTransform) -> void Adds a field transform to the current document for committing later. Available only in 4.x.
remove_field_transform(transform : FieldTransform) -> void Removes a field transform to the current document if added by accident, or no longer desired. Available only in 4.x.
clear_field_transforms() -> void Clears all field transforms from the document; typically used internally, but can be used if you've added many and don't want any of them anymore. Available only in 4.x.
remove_field(field_path : String) -> void Removes a property from your document; you must use this instead of document.document.erase(key), as the latter will not properly update in Firestore and the value will be returned as normal to you again when you update said document. This is required to remove a property from your document.
add_or_update_field(field_path : String, value : Variant) -> void Adds or updates a field within the document; note that if you intend to add fields programmatically to your document, you must do so this way, or when calling to firestore_collection.add the first time; if you attempt to call document.document[field_path] = value manually, you will get an error when attempting to update your document. This may be updated in the future, but for now, this is required if you want to add a new field or change the value of a field at runtime in your document.
on_snapshot(when_called : Callable, poll_time : float) -> FirestoreListener.FirestoreListenerConnection Makes it so that the changed signal connects when_called, and updates with a frequency of once at least every 2 minutes (you can put higher, but not lower). Available only in 4.x. This is not intended for realtime updates, but it will give you updates consistently. Ensure that you call stop() on the returned value when you are done listening to changes on the document: it will automatically call if your user closes your game, but it's better to not use it as much as possible, in order to avoid incurring expenses. Note that if this is for server use, I would not recommend calling this function at all, unless you put a massive amount of time between requests, like a full 24 hours. For standard usage, at 2 minutes, the cost is not likely to be that high. You can calculate the cost with the following two equations:

Snapshot polling calculations

count = calls_per hour * expected_user_hours_played * expected_user_count cost = (50,000 - count) * current_get_document_cost_from_firebase (current_prices)|

If the cost value is negative, that's how much you will owe to Google (or if you're on the free tier only, it'll severely restrict your future calls to it). Note this only applies to the default database; if you create a Firestore database with a non-default name, remove the 50K and just use -count * current_cost. 50K represents the free tier that Firebase provides, which only applies to the default database.

|get_value(property: StringName) -> Variant|Gets the value associated with property from the document and returns it. Note: it is best to use this instead of attempting to get the value from document.document, as document.document now contains internal-only housekeeping information that you will return and possibly corrupt your document with if you change it somehow.| |keys() -> Array|Returns all fields associated with this document.| |_to_string() -> String|Automatically called on print(document) to print a formatted document.|

Properties Description
doc_name: String The document name.
create_at: String Timestamp of creation date.
collection_name: String The collection to which this document belongs.

Signals Description
changed(changes : Dictionary) Whenever the document is changed, this fires. Note that changes is of this form. Note also that the only time is_listener should be true is when you've gotten an automatic update from the on_snapshot method; at all other times, is_listener should be false, so if you only want to listen to changes when something has happened on the server, look at the is_listener value; if you want all changes, including local changes you make on the fly, you do not need to check is_listener.

***

FirestoreQuery

FirestoreQuery.new()

Properties Description

Functions Description
select(fields: Variant) A SELECT query in a collection; fields can be an Array/PoolStringArray/PackedStringArray of fields, or just a String for a single field.
from(collection_id: String, all_descendants: bool = true) A FROM query in a single collection
from_many(collections: Array) A FROM query across multiple collections. collections must be an Array of Arrays of type [collection_id:String, all_descendants:bool].
where(field: String, operator: FirestoreQuery.OPERATOR, value: Variant, chain: int = -1) A WHERE query to match fields' values with Operators.
operator must be a FirestoreQuery.Operator element.
chain can be OPERATOR.AND or OPERATOR.OR to chain multiple where() in sequence. The last one in a chain (or if a single one) must have chain = -1 (automatically injected).
order_by(field: String, direction: int = DIRECTION.ASCENDING) An ORDER BY query in a collection. Direction must be of type FirestoreQuery.DIRECTION. You must have an index defined in the Firebase console in order to use this query type.
order_by_fields(order_field_list : Array) An ORDER BY query in a collection based on multiple fields. order_field_list must be an Array of Arrays with structure [field: String, order: DIRECTION]. You must have an index defined in the Firebase console in order to use this query type.
start_at(value: Variant, before: bool) A START AT query in a collection. value can be of any type. If true, before lists values before the specified one; if false, then after.
end_at(value: Variant, before: bool) An END AT query in a collection. value can be of any type. If true, before lists values before the specified one; if false, then after.
offset(offset: int) An OFFSET query in a collection. Defines the amount of results to be skipped in a query.
limit(limit: int) A LIMIT query in a collection. Defines the max number of results to be returned.

Back


Connect to a Collection

Note you need to be authenticated for this to work

var collection = Firebase.Firestore.collection('COLLECTION_NAME')

The following will return a collection from Firestore called firestore_collection. This collection can be called on later when adding or getting documents from it.

var firestore_collection : FirestoreCollection = Firebase.Firestore.collection('COLLECTION_NAME')

Back


firestoretransform

Base class for transforms which is mostly for future development. Available only in 4.x.

fieldtransform

Base class for transforms that act over fields in a document. Available only in 4.x.

DecrementTransform

Class that allows you to decrement a field value atomically, rather than having to change the field value and update it yourself, which can be problematic for ordered operations. Available only in 4.x.

IncrementTransform

Class that allows you to increment a field value atomically, rather than having to change the field value and update it yourself, which can be problematic for ordered operations. Available only in 4.x.

ServerTimestampTransform

Class that allows you to set the value of a field to the timestamp on the server at the time of setting it; this is useful if you want to have a standardized time frame against which to compare your documents, since the created_at time in the FirestoreDocument class is equal to the current user's time, rather than something standard. Available only in 4.x.

MaxTransform

Class that sets the value of a field to the maximum of the current value of that field and the value passed in. Available only in 4.x.

MinTransform

Class that sets the value of a field to the minimum of the current value of that field and the value passed in. Available only in 4.x.

firestorelistenerconnection

Class which allows the user to turn off the listening by calling the stop() method. Note: users will want to call this as soon as they reasonably can, as even at a minimum of 2 minutes per request, if the game is running for a long period of time, this can become expensive. See [Snapshot polling calculations] (https://github.com/GodotNuts/GodotFirebase/wiki/Firestore#Snapshot-polling-calculations) for more on this.

firestorelistener

Node that enables listening to changes on documents. Note: this uses polling for the time being, but when and if Godot ever supports HTTP2+ requests, it will be updated to use a single request and connect to the Listener endpoint, which is much, much more efficient.

Back


Get a Document

Note you need to be authenticated and connected to a collection for this to work

var document = await/yield() firestore_collection.get_doc(documentId: String, from_cache : bool, is_listener : bool) -> FirestoreDocument

The following methods will let you return a document from Firestore with the DOCUMENT_ID that is specified to an object called firestore_document of type FirestoreDocument:

var collection: FirestoreCollection = Firebase.Firestore.collection(COLLECTION_ID)
# 3.x
var document = yield(collection.get_doc(DOCUMENT_ID), "completed")
# 4.x
var document = await collection.get_doc(DOCUMENT_ID)

The following will parse the data into a human readable format with automatic parsing, from Firebase format to Dictionary.

print(firestore_document)

Back


Add a Document

Note you need to be authenticated and connected to a collection for this to work

collection.add(documentId : String, document : Dictionary = {}) -> FirestoreDocument

The following will add a new document into Firestore by first creating the object, and then adding it.

# 3.x
var new_document = yield(firestore_collection.add("DOCUMENT_ID", {'name': 'Document Name', 'active': 'true'}), "completed")

# 4.x
var document = await firestore_collection.add("DOCUMENT_ID", {'name': 'Document Name', 'active': 'true'})

Note: if "DOCUMENT_ID" is left as an empty String, Firebase will assign a random name to this document. This name will exist in the returned FirestoreDocument as doc_name.

Back


Commit a Document

Note you need to be authenticated and connected to a collection for this to work

4.x:
await collection.commit(document : FirestoreDocument) -> Dictionary # This does not exist in 3.x yet, nor is likely ever to

Back


Update a Document

Note you need to be authenticated and connected to a collection for this to work

3.x:
yield(collection.update(document : FirestoreDocument), "completed") -> FirestoreDocument # Updated document is returned

4.x:
await collection.update(document : FirestoreDocument) -> FirestoreDocument # Updated document is returned

The following will update document in Firestore.

# 3.x
...
document.add_or_update_field("name", "New Document Name")
document.add_or_update_field("active", true)
var updated_document = yield(collection.update(document), "completed")

# 4.x
var new_document = await firestore_collection.update(document) # This is why I mainly maintain 4.x now

NOTE! this function will automatically update only those fields specified in the request. This will help you update your document without overwriting the fields you don't specify in the request and don't want to touch.
For instance, if the document in this example was like this (before the update):

{
"name" : "A Name",
"active" : false,
"points" : 25,
"directory": { "path" : "a path" }
}

after the update it will look like this:

{
"name" : "New Document Name",
"active" : true,
"points" : 25,
"directory": { "path" : "a path" }
}

Back


Delete a Document

Note you need to be authenticated and connected to a collection for this to work

collection.delete(document: FirestoreDocument) -> bool # Returns true if deleted successfully, must be awaited

The following will delete a document in Firestore.

# 3.x
var was_deleted = yield(firestore_collection.delete(document), "completed")

# 4.x
var was_deleted = await firestore_collection.delete("DOCUMENT_ID")

Back


Issue a Query

Note you need to be authenticated for this to work

3.x:
var result = yield(Firebase.Firestore.query(FirestoreQuery.new()), "completed") -> Array

4.x:
var results = await Firebase.Firestore.query(FirestoreQuery.new()) -> Array

The following query retrieves some documents from a collection based on the value of a field.

# create a query
var query: FirestoreQuery = FirestoreQuery.new()

# FROM a collection
query.from("user_list")	

# WHERE points > 20
query.where("points", FirestoreQuery.OPERATOR.GREATER_THAN, 20)

# ORDER BY points, from the user with the best score to the latest
query.order_by("points", FirestoreQuery.DIRECTION.DESCENDING)

# LIMIT to the first 10 users
query.limit(10)

# Issue the query
3.x:
var result = yield(Firebase.Firestore.query(query), "completed")

# 4.x
var results = await Firebase.Firestore.query(query)

Note: each FirestoreQuery function will always return itself, so the query component can be concatenated to create multi-line queries

# Do all the previous, but in one line
var query : FirestoreQuery = FirestoreQuery.new().from("user_list").where("points", FirestoreQuery.OPERATOR.GREATER_THAN, 20).order_by("points", FirestoreQuery.DIRECTION.DESCENDING).limit(10)

# 3.x
var result = yield(Firebase.Firestore.query(query), "completed")

Note: to order_by multiple fields, you must use order_by_fields

query.order_by_fields([
    ["points", FirestoreQuery.DIRECTION.DESCENDING],
    ["name", FirestoreQuery.DIRECTION.DESCENDING]
])

Back


Debugging

Note you need to be authenticated and connected to a collection for this to work

  • signal error(error_result)

You can connect to the error signal via the collection object, and it'll fire whenever there's an error with one of the associated calls that you make to said collection.

Back


Common Errors

Unauthorized

This occurs when you are trying to do something with a collection but have not logged in.

Need index

This occurs when you attempt to query a set of fields but no index exists for them in Firestore; make sure you follow the link they provide in the output if you are getting errors, as it will take you to a page that will be able to create that index for you automatically, which is nice.

Back