Introduction
Firestore is a noSQL cloud database that helps users to upload, sync and retrieve their data in real-time. It is highly scalable and supports offline mode. Firebase is a hot topic in interviews nowadays.
In this article, we will learn everything about Firebase from scratch.
Key Features of Firestore.
- Scalable: it is highly scalable, hence we can use it for large project also
- Sync: provides real-time sync for our app.
- Queries: provides a large range of advanced queries such as filtering, ordering, and multiple conditions.
- It is a noSQL cloud database, so we don't need to learn SQL for it.
- It provides offline support to mobile.
Setting-up Firestore in Your App
1. Add Firebase to your Android app.
In this section we will see, how we can implement the Firestore in our app.
- Open Firebase console.
- Create a project.
- Add android app by providing the package name same as your build.gradle.
- Install
google-service.json
file . - Open Android Studio and place the json file in
app/
directory. - Add the Firebase SDK dependencies in build.gradle (Project level):
dependencies {
classpath 'com.google.gms:google-services:4.3.10'
}
7 Add Firebase dependencies in build.gradle (Module level):
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.7.0')
implementation 'com.google.firebase:firebase-firestore-ktx'
}
8 Sync your project.
2. Enable Firestore in Firebase Console.
- Open your Firebase project in the console.
- Click on the
Firestore database
-> Create Database. - Click start in test mode.
- Enable.
3. Initialize Firestore in your app.
val db =FirebaseFirestore.getInstance()
This will return you a Firebase instance and this will be your database.
Firestore CRUD Operations.
In this section we will learn CRUD (Create, Read, Update, Delete) operations provided by the Firestore but first, we should see how Firestore stores our data.
How Firestore Stores The Data.
Firebase is document-based, meaning it stores the data in the form of collection (folder) and inside them document (users/ sub data).
- Collection: It consists of different collections of data/documents, you can think of it as table in RDBMS. Example: Users, Students, Courses.
-
Document: Inside the collections, it stores different data of collection type such as different users, students, and courses. This data is called
document
. A document is the data of the user. - Fields: Key value pair inside the document, it represents the data of the user (eg. "name":"Harsh", "roll":12).
Example of Firestore Data Structure.
users (Collection)
├── user123 (Document)
│ ├── name: "Harsh"
│ ├── age: 18
│ ├── email: "harsh@example.com"
├── user456 (Document)
│ ├── name: "John"
│ ├── age: 21
1. Adding Data to Firestore.
There are two ways to add data to the Firestore: 1. Autogenerated document id, 2. Custom document id.
1. Autogenerated document id
Firestore will generate a unique ID for the document.
val db = FirebaseFirestore.getInstance()
val user = hashMapOf(
"name" to "Harsh",
"age" to 18,
"email" to "harsh@example.com"
)
// Add data to the "users" collection with an auto-generated ID
db.collection("users")
.add(user)
.addOnSuccessListener { documentReference ->
Log.d("Firestore", "Document added with ID: ${documentReference.id}")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error adding document", e)
}
Output:
Document added with ID: xetiRfzp5LSAckrDwEre
Obviously, you will get a different ID.
2. Custom document ID.
In this case, we will give a custom document id for our document to the Firestore.
val db = FirebaseFirestore.getInstance()
val user = hashMapOf(
"name" to "Harsh",
"age" to 18,
"email" to "harsh@example.com"
)
// Set a custom document ID (e.g., email)
db.collection("users")
.document("harsh@example.com") // Custom ID
.set(user)
.addOnSuccessListener {
Log.d("Firestore", "Document successfully written!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error writing document", e)
}
-
.set()
will override the user if it already exists.
2. Retrieving The Data.
There are three ways to read the data from Firestore.
- 1. Get a Single Document (Fetch one user's details).
- 2. Get All Documents in a Collection (Fetch all users).
- 3. Query Specific Documents (Filter results, like finding users above age 18).
1. Getting a Single Document.
If we know the document ID, we can retrieve it using .document().get()
.
val db = FirebaseFirestore.getInstance()
db.collection("users")
.document("harsh@example.com") // Use custom document ID
.get()
.addOnSuccessListener { document ->
if (document.exists()) {
val name = document.getString("name")
val age = document.getLong("age")
val email = document.getString("email")
Log.d("Firestore", "User Data: Name=$name, Age=$age, Email=$email")
} else {
Log.d("Firestore", "No such document!")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching document", e)
}
Explanation:
.document("harsh@example.com")
→ Fetches the document with this specific ID.
.get()
→ Reads data from Firestore.
.exists()
→ Checks if the document exists.
.getString("name")
& .getLong("age") → Extract field values.
####2. Fetch All Documents from a Collection
To get all users from the "users" collection:
db.collection("users")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
val name = document.getString("name")
val age = document.getLong("age")
val email = document.getString("email")
Log.d("Firestore", "User: Name=$name, Age=$age, Email=$email")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching users", e)
}
Explanation:
.collection("users").get()
→ Fetches all documents in the "users" collection.
for (document in documents)
→ Loops through each document.
.getString("name"), .getLong("age")
→ Reads field values.
3. Query Specific Documents (Filtering Data)
What if we want only users above age 18? We use queries!
db.collection("users")
.whereGreaterThan("age", 18) // Only users older than 18
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
val name = document.getString("name")
val age = document.getLong("age")
Log.d("Firestore", "User: Name=$name, Age=$age")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching filtered users", e)
}
Explanation:
.whereGreaterThan("age", 18)
→ Fetches users where age > 18.
Firestore supports queries like:
whereEqualTo("name", "Harsh")
→ Find users with a specific name.
whereLessThan("age", 30)
→ Users younger than 30.
orderBy("age", Query.Direction.ASCENDING)
→ Sort by age.
3. Updating Data.
Firestore allows two types of updates:
- 1. Overwrite Entire Document (Replace all fields)
- 2. Update Specific Fields (Modify only selected fields)
1. Overwrite an Entire Document
Using .set()
, we can replace an entire document.
val db = FirebaseFirestore.getInstance()
val updatedUser = hashMapOf(
"name" to "Harsh Lade",
"age" to 19,
"email" to "harsh@example.com"
)
// Overwrite the existing document
db.collection("users")
.document("harsh@example.com") // Custom document ID
.set(updatedUser)
.addOnSuccessListener {
Log.d("Firestore", "Document successfully updated!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error updating document", e)
}
Warning:
.set(updatedUser)
replaces the entire document.
If the document has extra fields (e.g., "phone": "12345"), they will be removed unless included in updatedUser.
2. Update Specific Fields
If we want to update only one or two fields, we use .update().
db.collection("users")
.document("harsh@example.com")
.update("age", 20) // Update only the "age" field
.addOnSuccessListener {
Log.d("Firestore", "Field successfully updated!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error updating field", e)
}
Explanation:
.update("age", 20)
→ Updates only the "age" field to 20.
Other fields (name, email) remain unchanged.
3. Update Multiple Fields at Once
We can update multiple fields together:
db.collection("users")
.document("harsh@example.com")
.update(
"name", "Harsh L.",
"age", 21
)
.addOnSuccessListener {
Log.d("Firestore", "Multiple fields updated!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error updating fields", e)
}
Explanation:
Updates "name" to "Harsh L." and "age" to 21.
Other fields remain untouched.
4. Add a New Field to an Existing Document.
If we want to add a new field (without replacing old ones), we use .update()
.
db.collection("users")
.document("harsh@example.com")
.update("phone", "9876543210") // New field "phone" added
.addOnSuccessListener {
Log.d("Firestore", "New field added!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error adding field", e)
}
Explanation:
If "phone" does not exist, it gets added.
If "phone" already exists, its value is updated.
4. Deleting Data in Firestore (Delete Operation)
Now that we know how to update data, let's learn how to delete Firestore documents and fields.
Ways to Delete Data in Firestore
- Firestore allows three types of deletions:
- Delete an Entire Document (Remove a user completely)
- Delete a Specific Field (Remove only one field, keeping others) 3 Delete an Entire Collection (Delete all users – not directly supported)
1. Delete an Entire Document
To remove a document completely, use .delete().
db.collection("users")
.document("harsh@example.com") // Delete this document
.delete()
.addOnSuccessListener {
Log.d("Firestore", "Document successfully deleted!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error deleting document", e)
}
Explanation:
.delete()
removes the entire document from Firestore.
This cannot be undone, so be careful!
2. Delete a Specific Field in a Document
Instead of deleting the whole document, we can remove only one field using FieldValue.delete().
import com.google.firebase.firestore.FieldValue
db.collection("users")
.document("harsh@example.com")
.update("phone", FieldValue.delete()) // Remove only the "phone" field
.addOnSuccessListener {
Log.d("Firestore", "Field successfully deleted!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error deleting field", e)
}
Explanation:
.update("phone", FieldValue.delete())
→ Removes only the "phone" field.
Other fields (name, age, email) remain untouched.
3. Delete an Entire Collection
Firestore does not allow deleting a collection directly, so we must delete each document manually.
db.collection("users")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
db.collection("users").document(document.id).delete()
}
Log.d("Firestore", "All documents in 'users' deleted!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error deleting collection", e)
}
Explanation:
.get()
→ Fetches all documents in the "users" collection.
for (document in documents) → Loops through and deletes each document.
This is not atomic, meaning if the process fails midway, some documents may remain.
Querying Data in Firestore
Now that we know how to create, read, update, and delete data, let's learn how to query Firestore efficiently.
Why Are Queries Important?
In real-world apps, we often need to:
- Find specific users (e.g., users older than 18).
- Sort data (e.g., show the latest products first).
- Limit results (e.g., show only the top 10 users).
- Apply multiple filters (e.g., users in India AND above 18).
Firestore provides powerful querying features, but it has some limitations we must understand.
1. Basic Query (Filter Data)
We use .whereEqualTo()
, .whereGreaterThan()
, etc., to filter results.
Example: Get all users where "age" >= 18
db.collection("users")
.whereGreaterThanOrEqualTo("age", 18)
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
Log.d("Firestore", "${document.id} => ${document.data}")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching documents", e)
}
Explanation:
.whereGreaterThanOrEqualTo("age", 18)
→ Gets users aged 18 or older.
.get()
→ Fetches matching documents.
Loop through documents and print results.
📌 Other Query Filters
Query Method Example Meaning
.whereEqualTo() "city", "Mumbai" Users in Mumbai
.whereNotEqualTo() "city", "Delhi" Users NOT in Delhi
.whereGreaterThan() "age", 25 Users older than 25
.whereLessThan() "age", 30 Users younger than 30
.whereArrayContains() "hobbies", "chess" Users who like chess
2. Sorting Data
We use .orderBy() to sort results in ascending or descending order.
Example: Get all users, sorted by "age" (youngest first)
db.collection("users")
.orderBy("age", Query.Direction.ASCENDING) // Sort by age (ascending)
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
Log.d("Firestore", "${document.id} => ${document.data}")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching documents", e)
}
Sorting Options:
Query.Direction.ASCENDING
→ Smallest first (youngest users).
Query.Direction.DESCENDING
→ Largest first (oldest users).
3. Limiting Data (Pagination)
We use .limit(n) to fetch only the first n results.
Example: Get the top 5 youngest users
db.collection("users")
.orderBy("age", Query.Direction.ASCENDING)
.limit(5)
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
Log.d("Firestore", "${document.id} => ${document.data}")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching documents", e)
}
Why Use .limit(n)?
- Reduces database load.
- Faster performance.
- Useful for pagination (loading more results later).
4. Combining Filters & Sorting
Firestore allows only one .orderBy()
per query, and all filters must be indexed.
Example: Get all users aged 18+, sorted by "age"
db.collection("users")
.whereGreaterThanOrEqualTo("age", 18)
.orderBy("age", Query.Direction.ASCENDING) // Required for sorting
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
Log.d("Firestore", "${document.id} => ${document.data}")
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error fetching documents", e)
}
Firestore Limitation:
You must create an index when using .where()
with .orderBy().
Firestore will show an error and provide a link to create the index.
Top comments (0)