# Cloud Firestore Cloud Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. While the Cloud Firestore interface has many of the same features as traditional databases, as a NoSQL database it differs from them in the way it describes relationships between data objects. For more information about Cloud Firestore, read the [Cloud Firestore Documentation](https://cloud.google.com/firestore/docs/). The goal of google-cloud is to provide an API that is comfortable to Rubyists. Authentication is handled by {Google::Cloud::Firestore.new Firestore.new}. You can provide the project and credential information to connect to the Cloud Firestore service, or if you are running on Google Cloud Platform (GCP), including Google Compute Engine (GCE), Google Kubernetes Engine (GKE), Google App Engine (GAE), Google Cloud Functions (GCF) and Cloud Run this configuration is taken care of for you. You can read more about the options for connecting in the {file:AUTHENTICATION.md Authentication Guide}. ## Adding data Cloud Firestore stores data in Documents, which are stored in Collections. Cloud Firestore creates collections and documents implicitly the first time you add data to the document. (For more information, see [Adding Data to Cloud Firestore](https://cloud.google.com/firestore/docs/manage-data/add-data). To create or overwrite a single document, use {Google::Cloud::Firestore::Client#doc Client#doc} to obtain a document reference. (This does not create a document in Cloud Firestore.) Then, call {Google::Cloud::Firestore::DocumentReference#set DocumentReference#set} to create the document or overwrite an existing document: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.set({ name: "New York City" }) # Document created ``` When you use this combination of `doc` and `set` to create a new document, you must specify an ID for the document. (In the example above, the ID is "NYC".) However, if you do not have a meaningful ID for the document, you may omit the ID from a call to {Google::Cloud::Firestore::CollectionReference#doc CollectionReference#doc}, and Cloud Firestore will auto-generate an ID for you. ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Get a document reference with data random_ref = cities_col.doc random_ref.set({ name: "New York City" }) # The document ID is randomly generated random_ref.document_id #=> "RANDOMID123XYZ" ``` You can perform both of the operations shown above, auto-generating an ID and creating the document, in a single call to {Google::Cloud::Firestore::CollectionReference#add CollectionReference#add}. ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Get a document reference with data random_ref = cities_col.add({ name: "New York City" }) # The document ID is randomly generated random_ref.document_id #=> "RANDOMID123XYZ" ``` You can also use `add` to create an empty document: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Create a document without data random_ref = cities_col.add # The document ID is randomly generated random_ref.document_id #=> "RANDOMID123XYZ" ``` ## Retrieving collection references Collections are simply named containers for documents. A collection contains documents and nothing else. It can't directly contain raw fields with values, and it can't contain other collections. You do not need to "create" or "delete" collections. After you create the first document in a collection, the collection exists. If you delete all of the documents in a collection, it no longer exists. (For more information, see [Cloud Firestore Data Model](https://cloud.google.com/firestore/docs/data-model). Use {Google::Cloud::Firestore::Client#cols Client#cols} to list the root-level collections: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get the root collections firestore.cols.each do |col| puts col.collection_id end ``` Retrieving a reference to a single root-level collection is similar: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get the cities collection cities_col = firestore.col "cities" ``` To list the collections in a document, first get the document reference, then use {Google::Cloud::Firestore::DocumentReference#cols DocumentReference#cols}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.cols.each do |col| puts col.collection_id end ``` Again, retrieving a reference to a single collection is similar:: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" # Get precincts sub-collection precincts_col = nyc_ref.col "precincts" ``` ## Reading data You can retrieve a snapshot of the data in a single document with {Google::Cloud::Firestore::DocumentReference#get DocumentReference#get}, which returns an instance of {Google::Cloud::Firestore::DocumentSnapshot DocumentSnapshot}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_snap = nyc_ref.get nyc_snap[:population] #=> 1000000 ``` In the example above, {Google::Cloud::Firestore::DocumentSnapshot#[] DocumentSnapshot#[]} is used to access a top-level field. To access nested fields, use {Google::Cloud::Firestore::FieldPath FieldPath}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new user_snap = firestore.doc("users/frank").get nested_field_path = firestore.field_path :favorites, :food user_snap.get(nested_field_path) #=> "Pizza" ``` Or, use {Google::Cloud::Firestore::Client#get_all Client#get_all} to retrieve a list of document snapshots (data): ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get and print city documents cities = ["cities/NYC", "cities/SF", "cities/LA"] firestore.get_all(cities).each do |city| puts "#{city.document_id} has #{city[:population]} residents." end ``` To retrieve all of the document snapshots in a collection, use {Google::Cloud::Firestore::CollectionReference#get CollectionReference#get}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Get and print all city documents cities_col.get do |city| puts "#{city.document_id} has #{city[:population]} residents." end ``` The example above is actually a simple query without filters. Let's look at some other queries for Cloud Firestore. ## Querying data Use {Google::Cloud::Firestore::Query#where Query#where} to filter queries on a field: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Create a query query = cities_col.where(:population, :>=, 1000000) query.get do |city| puts "#{city.document_id} has #{city[:population]} residents." end ``` You can order the query results with {Google::Cloud::Firestore::Query#order Query#order}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Create a query query = cities_col.order(:name, :desc) query.get do |city| puts "#{city.document_id} has #{city[:population]} residents." end ``` Query methods may be chained, as in this example using {Google::Cloud::Firestore::Query#limit Query#limit} and {Google::Cloud::Firestore::Query#offset Query#offset} to perform pagination: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a collection reference cities_col = firestore.col "cities" # Create a query query = cities_col.limit(5).offset(10) query.get do |city| puts "#{city.document_id} has #{city[:population]} residents." end ``` See [Managing Indexes in Cloud Firestore](https://cloud.google.com/firestore/docs/query-data/indexing) to ensure the best performance for your queries. ## Updating data You can use {Google::Cloud::Firestore::DocumentReference#set DocumentReference#set} to completely overwrite an existing document: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.set({ name: "New York City" }) ``` Or, to selectively update only the fields appearing in your `data` argument, set the `merge` option to `true`: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.set({ name: "New York City" }, merge: true) ``` Use {Google::Cloud::Firestore::DocumentReference#update DocumentReference#update} to directly update a deeply-nested field with a {Google::Cloud::Firestore::FieldPath}: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new user_ref = firestore.doc "users/frank" nested_field_path = firestore.field_path :favorites, :food user_ref.update({ nested_field_path => "Pasta" }) ``` ### Listening for changes You can listen to a document reference or a collection reference/query for changes. The current document snapshot or query results snapshot will be yielded first, and each time the contents change. You can use {Google::Cloud::Firestore::DocumentReference#listen DocumentReference#listen} to be notified of changes to a single document: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" listener = nyc_ref.listen do |snapshot| puts "The population of #{snapshot[:name]} " puts "is #{snapshot[:population]}." end # When ready, stop the listen operation and close the stream. listener.stop ``` You can use {Google::Cloud::Firestore::Query#listen Query#listen} to be notified of changes to any document contained in the query: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Create a query query = firestore.col(:cities).order(:population, :desc) listener = query.listen do |snapshot| puts "The query snapshot has #{snapshot.docs.count} documents " puts "and has #{snapshot.changes.count} changes." end # When ready, stop the listen operation and close the stream. listener.stop ``` ## Using transactions and batched writes Cloud Firestore supports atomic operations for reading and writing data. In a set of atomic operations, either all of the operations succeed, or none of them are applied. There are two types of atomic operations in Cloud Firestore: A transaction is a set of read and write operations on one or more documents, while a batched write is a set of only write operations on one or more documents. (For more information, see [Transactions and Batched Writes](https://cloud.google.com/firestore/docs/manage-data/transactions). ### Transactions A transaction consists of any number of read operations followed by any number of write operations. (Read operations must always come before write operations.) In the case of a concurrent update by another client, Cloud Firestore runs the entire transaction again. Therefore, transaction blocks should be idempotent and should not not directly modify application state. ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new city = firestore.col("cities").doc("SF") city.set({ name: "San Francisco", state: "CA", country: "USA", capital: false, population: 860000 }) firestore.transaction do |tx| new_population = tx.get(city).data[:population] + 1 tx.update(city, { population: new_population }) end ``` ### Batched writes If you do not need to read any documents in your operation set, you can execute multiple write operations as a single batch. A batch of writes completes atomically and can write to multiple documents. Batched writes are also useful for migrating large data sets to Cloud Firestore. ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new firestore.batch do |b| # Set the data for NYC b.set("cities/NYC", { name: "New York City" }) # Update the population for SF b.update("cities/SF", { population: 1000000 }) # Delete LA b.delete("cities/LA") end ``` ## Deleting data Use {Google::Cloud::Firestore::DocumentReference#delete DocumentReference#delete} to delete a document from Cloud Firestore: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.delete ``` To delete specific fields from a document, use the {Google::Cloud::Firestore::Client.field_delete Client.field_delete} method when you update a document: ```ruby require "google/cloud/firestore" firestore = Google::Cloud::Firestore.new # Get a document reference nyc_ref = firestore.doc "cities/NYC" nyc_ref.update({ name: "New York City", trash: firestore.field_delete }) ``` To delete an entire collection or sub-collection in Cloud Firestore, retrieve all the documents within the collection or sub-collection and delete them. If you have larger collections, you may want to delete the documents in smaller batches to avoid out-of-memory errors. Repeat the process until you've deleted the entire collection or sub-collection. ## Additional information Google Firestore can be configured to use gRPC's logging. To learn more, see the {file:LOGGING.md Logging guide}.