Let me just get this out there. Firestore is amazing. I only found out about it recently and it is new (it’s still in beta as of now) but its documented structure is ingenious! Now I see it as an almost definite replacement to the Realtime Database.
By the way, out of topic as it may be, I can’t help but to recommend this book, The Definitive Guide to Firebase by Laurence Moroney. He’s an online acquaintance of mine who’s very accomodating and extremely knowledgable about Firebase. Even for the more Firebase-specialised minds, you’ll learn a lot from this book. You’ll gain a deeper understanding of the different tools and services of Firebase (many of which you’re still probably unaware of).
So what is Firestore?
What makes Firestore unique is the way it stores data. Data is stored in documents which are stored in collections. A document can store multiple instances of data. A collection can store multiple documents, and a collection can also store multiple subcollections.
Step 0: Connect Firebase to your App
As with all of my other Firebase tutorials, this is the main prerequisite. You can find my tutorial on that here where I show you how to add Firebase to your app in 2 minutes if you’re reading through, and probably not even a minute once you know how to do it.
Step 1: Setup Firestore in the Console
Cloud Firestore requires a little console setup. Open your project in the Firebase Console, select Database in the left side menu then click Try Firestore Beta. The console will then prompt you to start in locked or test mode. If your app is already released and being used by the public, choose Locked Mode for security. Otherwise, Test Mode will be easier to use.
Step 2: Add the Gradle Dependency to your App
compile 'com.google.firebase:firebase-firestore:11.8.0'
Add this to your app build.gradle file. Simple and easy.
Step 3: Initialise and Read/Write Data
Initialise by getting an instance to the database.
FirebaseFirestore db = FirebaseFirestore.getInstance();
Writing Data
To write data, you have to wrap your head around the documents structure of Firestore. Data is stored in Documents which are stored in Collections.
Prepare documents by creating a Map<String, Object> (for each document). Put your data in that map with appropriate keys as you’d visualise them as JSON. Once all is put, just call add on your collection and add success and failure listeners if you need them. Just like child in the Realtime Database, stating a collection that doesn’t exist will automatically create it.
Map<String, Object> user = new HashMap<>(); user.put("name", "Montgomery"); // Add a new document with a generated ID db.collection("users") .add(user) .addOnSuccessListener(documentReference -> textView.setText("DocumentSnapshot added with ID: " + documentReference.getId())) .addOnFailureListener(e -> Log.w(TAG, "Error adding document", e));
You can view if you’ve added the data properly in the data viewer of the Firebase Console. Note that documents under the same collection don’t necessarily have to contain the same sets of data.
If you started your Firestore database in locked mode, chances are doing this caused a permission denied error and writing to the database was unsuccessful. Take a look below where I explain security rules to learn your way around this.
Reading Data
Use the get method on your collection to retrieve the entire collection. In the OnCompleteListener, check if the task is successful, query through the documents, then get your data from your document like its a bundle.
db.collection("users") .get() .addOnCompleteListener(task -> { if (task.isSuccessful()) { for (DocumentSnapshot document : task.getResult()) { String name = document.getString("name"); } } else { Log.w(TAG, "Error getting documents.", task.getException()); } });
In this example, I got the user’s first name by calling getString and passing in the key “first”. Note that there isn’t a getInt method. According to Abe Haskins, this is because Firestore doesn’t ever store data as integers. If you really need it, your best bet is to call getLong.toInt.
Security Rules
To access the Firestore configuration rules, navigate to Rules on the top bar of your Firestore database. You can match one or more paths (documents or whole collections) with the following code
service cloud.firestore { match /databases/{database}/documents { // Rules match specific paths, matching a particular document within a collection match /myCollection/myDocument { allow read, write: if <condition>; } // Rules can also specify a wildcard, matching any document within a collection match /myCollection/{anyDocument} { allow write: if <other_condition>; } } }
You may have noticed conditions are applicable as well. These include the likes of request.auth != null or request.resource.data.age > 17. Another thing is you can compare incoming data to existing data in the database with the get or exists functions.
allow write: if get(/databases/$(database)/documents/games/$(game)).data.referee == request.auth.uid;
Note that get and exist functions don’t work on collections. This is a very in-a-nutshell overview of the Firestore security rules. If you need to find out more, check out my post on it or the official docs.
Get the Example App Source Code
As with my other tutorials, I provided an example app that implements a very basic use of reading and writing data in the Cloud Firestore Database for you here.