Skip to content

Firestore

Kyle Szklenski edited this page Jun 4, 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.
auth: Dictionary A Dictionary containing all auth info for the current authenticated user.

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

***

FirestoreCollection

Firebase.Firestore.collection()

Functions                                          Description
add(document_id: String, document: Dictionary) -> FirestoreDocument Add a dDocument to a collection.
If document_id is an empty string, the id will be randomly generated by Firebase.
update(document_id: String, fields: Dictionary) -> FirestoreTask Update a specified document in a collection. If document_id is an empty string, the id will be randomly generated by Firebase, creating a new document.
Similarly, if the document with the given document_id does not exist, it will be created.
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.
delete(document : FirestoreDocument) -> bool Delete a specific document.
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.

Properties Description
config: Dictionary Configuration settings for this plugin.
auth: Dictionary Authentication dictionary to issue requests as a logged user.
collection_name: String The referenced collection's name

Signals Description
error(code, status, message) Emitted when a request has not processed correctly by a FirestoreTask.


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.
remove_field_transform(transform : FieldTransform) -> void Removes a field transform to the current document if added by accident, or no longer desired.
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.
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(doc_id, document) 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 to 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). 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 call if you shut down the 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 first; 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
doc_name: String The document name
doc_fields: Dictionary The document fields, as a GDScript Dictionary
create_at: String Timestamp of creation date

Functions Description
select(fields: Variant) A SELECT query in a collection; fields can be an Array/PoolStringArray 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.

# 3.x
var firestore_collection : FirestoreCollection = Firebase.Firestore.collection('COLLECTION_NAME')
# 4.x
var firestore_collection : FirestoreCollection = Firebase.Firestore.collection('COLLECTION_NAME')

Back


firestoretransform

Base class for transforms which is mostly for future development.

fieldtransform

Base class for transforms that act over fields in a document

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

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

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.

MaxTransform

Class that sets the value of a field to the maximum of the current value of that field and the value passed in

MinTransform

Class that sets the value of a field to the minimum of the current value of that field and the value passed in

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

# 3.x
firestore_collection.get(documentId: String) -> FirestoreTask
# 4.x
var document = await 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:

  1. Call the #4.x .get_doc() or #3.x .get() function and then yield for the collection to return it with a signal
# 3.x
var collection: FirestoreCollection = Firebase.Firestore.collection(COLLECTION_ID)
collection.get(DOCUMENT_ID)
var document: FirestoreDocument = yield(collection, "get_document")

- or -

var collection: FirestoreCollection = Firebase.Firestore.collection(COLLECTION_ID)
var document_task: FirestoreTask = collection.get(DOCUMENT_ID)
var document: FirestoreDocument = yield(document_task, "get_document")
  1. Connect a signal to the collection and get the document separately
# 3.x
var collection: FirestoreCollection = Firebase.Firestore.collection(COLLECTION_ID)
collection.connect("get_document",self,"_on_get_document")
collection.get(DOCUMENT_ID)

[...]

func _on_get_document(document: FirestoreDocument) -> void:
    pass

# 4.x
var collection: FirestoreCollection = Firebase.Firestore.collection(COLLECTION_ID)
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 add_task: FirestoreTask = firestore_collection.add("DOCUMENT_ID", {'name': 'Document Name', 'active': 'true'})
var document: FirestoreTask = yield(add_task, "task_finished")

- or -

var document = yield(add_task, "add_document")

- or -

var document = yield(Firebase.Firestore, "add_document")

# 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


Update a Document

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

3.x:
collection.update(documentId: String, fields: Dictionary = {}) -> FirestoreTask

4.x:
await collection.update(document : FirestoreDocument) -> FirestoreDocument

The following will update document in Firestore.

# 3.x
var up_task: FirestoreTask = firestore_collection.update("DOCUMENT_ID", {'name': 'Document Name', 'active': true})
var document: FirestoreDocument = yield(up_task, "task_finished")

- or -

var document: FirestoreDocument = yield(up_task, "update_document")

- or -

var document: FirestoreDocument = yield(Firebase.Firestore, "update_document")

# 4.x
var document = await firestore_collection.update(other_document) # Other document can be document itself, or something else; the returned value will be correct either way.

NOTE! this function will automatically update only those fields specified in the requests. 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" : "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

3.x:
collection.delete(documentId: String) -> FirestoreTask

4.x:
await collection.delete(document) -> bool # Returns true if deleted successfully

The following will delete a new document in Firestore.

# 3.x
var del_task: FirestoreTask = firestore_collection.delete("DOCUMENT_ID")
var document: FirestoreDocument = yield(del_task, "task_finished")

# 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:
Firebase.Firestore.query(FirestoreQuery.new()) -> FirestoreTask

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 query_task: FirestoreTask = Firebase.Firestore.query(query)
# Yield on the request to get a result
var result: Array = yield(query_task, "task_finished")

# 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: Array = yield(Firebase.Firestore.query(query), "result_query")

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_code, error_status, error_message)

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

Clone this wiki locally