Prev Next

Database / Google Firestore Interview questions

Last updated

1. What is Google Firestore? 2. What are the key features of Firestore? 3. What is a document in Firestore? 4. What is a collection in Firestore? 5. What is a subcollection in Firestore? 6. What data types does Firestore support? 7. What is a document reference in Firestore? 8. How do you add data to Firestore? 9. How do you read a document in Firestore? 10. How do you update specific fields of a document? 11. How do you delete a document or a field in Firestore? 12. What are Firestore security rules? 13. What are the two Firestore database modes? 14. What is a real-time listener in Firestore? 15. What is offline persistence in Firestore? 16. What are the key limits of a Firestore document? 17. How do you query documents with where clauses? 18. What are Firestore indexes? 19. What is the difference between Firestore and Realtime Database? 20. What is the difference between set, add, and update in Firestore? 21. How do Firestore transactions work? 22. What is a batched write in Firestore? 23. What is the difference between transactions and batched writes? 24. How does Firestore pricing work? 25. How do you paginate query results with cursors? 26. Why does Firestore require composite indexes? 27. How does Firestore handle OR queries? 28. What is the difference between single-field and composite indexes? 29. How do you use array-contains and array-contains-any? 30. How do you atomically increment a counter in Firestore? 31. How do security rules use request.auth? 32. When should you use subcollections instead of nested maps? 33. Why is denormalization common in Firestore? 34. How do Cloud Functions Firestore triggers work? 35. How does the Firestore emulator help with testing? 36. How can you reduce Firestore read costs? 37. What is a TTL policy in Firestore? 38. How do you back up and restore Firestore? 39. Why should you avoid sequential document IDs in Firestore? 40. How do you build a scalable distributed counter in Firestore? 41. What happens when a Firestore transaction conflicts? 42. How does Firestore achieve strong consistency? 43. Explain the lifecycle of a Firestore real-time listener? 44. How does Firestore offline sync handle write conflicts? 45. How would you model a chat app in Firestore? 46. How do you implement full-text search with Firestore? 47. What is vector search in Firestore? 48. What are aggregation queries in Firestore? 49. How do you enforce data validation and roles in security rules? 50. How do you migrate from Realtime Database to Firestore?

1. What is Google Firestore?

Google Firestore is a fully managed, serverless NoSQL document database offered through Firebase and Google Cloud. You store data as documents grouped into collections, and each document holds fields instead of fixed table columns.

It scales automatically, so there are no servers or shards to size. Clients on web, Android, iOS, and backend languages talk to it through SDKs.

Beyond plain storage, Firestore gives you indexed queries, real-time listeners, offline caching on client SDKs, and ACID transactions. That mix is why it is popular for mobile apps and event-driven backends.

Take quiz
How does Firestore organize data?
Rows inside fixed-schema tables
One big JSON tree under a single root
Flat key-value pairs with no nesting
Documents grouped into collections
Who handles capacity planning and scaling for Firestore?
You, by adding shards manually
Google, because the service is serverless and scales automatically
Your application, through a connection pool
You, by picking an instance size up front

2. What are the key features of Firestore?

Firestore combines a flexible document model with query power and live data sync. The features interviewers usually expect:

  • Document model with nested maps and arrays, no fixed schema
  • Indexed queries where speed depends on result size, not collection size
  • Real-time listeners that push changes to clients
  • Offline support through a local cache on mobile and web SDKs
  • ACID transactions and batched writes
  • Strong consistency and automatic multi-zone or multi-region replication
  • Security rules plus Firebase Authentication for client access control

It also plugs into Cloud Functions, so a document write can trigger backend code.

Take quiz
Which feature pushes data changes to clients without polling?
Real-time listeners using onSnapshot
Composite indexes
Scheduled exports to Cloud Storage
TTL policies
Which guarantee do Firestore transactions provide?
Eventual consistency only
Isolation only within one collection
ACID properties
At-most-once delivery

3. What is a document in Firestore?

A document is the basic unit of storage in Firestore. It is a set of key-value fields, identified by a unique ID inside its collection, and it can be at most 1 MiB in size.

Field values can be strings, numbers, booleans, timestamps, arrays, or nested maps. A document cannot contain another document as a field, but it can have subcollections hanging off it.

users/alice
{
  "name": "Alice",
  "age": 29,
  "address": { "city": "Pune", "zip": "411001" },
  "tags": ["admin", "beta"]
}

Take quiz
What is the maximum size of a single Firestore document?
There is no size limit
1 MiB
10 MiB
16 KB
Can a document hold another document directly as a field?
Only up to three levels deep
Only in Datastore mode
Yes, documents nest inside documents as fields
No, it can hold maps, and it can have subcollections

4. What is a collection in Firestore?

A collection is a container for documents. It has no schema, so two documents in the same collection can have completely different fields.

You never create a collection explicitly. It appears the moment you write the first document into it and disappears when the last document is deleted.

Collections can only contain documents, never other collections or raw values. Paths therefore alternate: collection/document/collection/document.

Because collections are schema-less, checks on field names and types live in your app code or in security rules, not in the database. A document ID only has to be unique within its own collection, so the same ID can exist in two different collections.

Take quiz
How is a Firestore collection created?
With a CREATE COLLECTION statement
By enabling it in the security rules
Implicitly, by writing the first document into it
By defining its schema in the console first
What happens to a collection when its last document is deleted?
The collection no longer exists
It turns into a subcollection of the root
It is archived for later restore
It stays behind as an empty billable container

5. What is a subcollection in Firestore?

A subcollection is a collection attached to a specific document, for example users/alice/orders/o1. Nesting can go up to 100 levels deep.

It suits one-to-many data that grows without a clear limit, such as orders, messages, or comments, because it avoids the 1 MiB document cap.

Two behaviours catch people out. Fetching a parent document does not return its subcollections, and deleting a parent document does not delete them either. They have to be removed separately.

A good rule of thumb: choose a subcollection when you need to query the children on their own, and a nested map when you only ever read them together with the parent.

Take quiz
You delete users/alice. What happens to users/alice/orders?
They move to a root orders collection
The delete is rejected while they exist
They are deleted automatically by cascade
Its documents remain, orphaned, until deleted separately
When you read the users/alice document, is its subcollection data included?
Only if you pass includeSubcollections
No, reads are shallow
Yes, up to three levels
Yes, but only the first 20 documents

6. What data types does Firestore support?

Firestore supports a compact set of field types:

Type Notes
String UTF-8 text
Number 64-bit integer or double
Boolean true / false
Map Nested object with its own fields
Array Ordered list, mixed types allowed
Null Explicit null value
Timestamp Microsecond precision
GeoPoint Latitude and longitude pair
Bytes Binary data
Reference Pointer to another document
Vector Embedding used for nearest-neighbour search

One restriction to remember: an array cannot directly contain another array. Wrap the inner array in a map if you need that shape.

Take quiz
Which field type stores a pointer to another document?
Reference
Map
GeoPoint
Bytes
What is true about arrays in Firestore?
They are capped at 100 elements
Every element must share one type
They cannot directly contain other arrays
They can nest arrays up to five levels

7. What is a document reference in Firestore?

A DocumentReference is a lightweight object that points to a document location by its path. It is just an address, so the document may or may not exist yet.

You use it as the handle for getDoc, setDoc, updateDoc, deleteDoc, and onSnapshot. It can also be stored inside another document as a reference field.

import { doc, getDoc } from "firebase/firestore";

const ref = doc(db, "users", "alice");   // no network call yet
const snap = await getDoc(ref);          // billed read happens here

Take quiz
What happens when you create a document reference with doc()?
An empty document is created
Nothing is read, it only points to a path
A listener starts on the collection
A billed read of the document runs
A document reference can point to:
Only documents in the root collection
Only collections
Only documents that already exist
A path whether or not a document exists there

8. How do you add data to Firestore?

There are two main calls. setDoc writes to a document ID you choose, while addDoc creates a new document with an auto-generated ID.

import { doc, setDoc, collection, addDoc } from "firebase/firestore";

// You pick the ID
await setDoc(doc(db, "users", "alice"), { name: "Alice", age: 29 });

// Firestore picks the ID
const ref = await addDoc(collection(db, "users"), { name: "Bob" });
console.log(ref.id);

Auto IDs are 20 random characters, which spreads writes evenly across the database. Multi-document inserts belong in a batched write or transaction.

Take quiz
Which call creates a document with an auto-generated ID?
deleteDoc
getDoc
addDoc
updateDoc
By default, what does setDoc do to an existing document?
Overwrites it completely
Merges the new fields in
Stores a second version beside it
Throws an already-exists error

9. How do you read a document in Firestore?

Call getDoc with a document reference. It returns a DocumentSnapshot; check exists() and then use data().

const snap = await getDoc(doc(db, "users", "alice"));
if (snap.exists()) {
  console.log(snap.data().name);
} else {
  console.log("No such document");
}

A missing document is not an error. You get a snapshot with exists() false, and it still counts as one billed read. If you need the data to keep updating, use onSnapshot instead of getDoc.

By default the SDK asks the server and falls back to the cache when offline. Pass the cache source if you deliberately want the local copy, for example on a screen that can tolerate slightly old data.

Take quiz
What does getDoc return for a document that does not exist?
An empty map with exists() true
A thrown 404 exception
A null pointer
A snapshot where exists() is false
Which call keeps delivering updates after the first read?
getDoc
onSnapshot
getCountFromServer
getDocs

10. How do you update specific fields of a document?

Use updateDoc with only the fields you want to change. Other fields stay untouched. For nested maps, use dot notation so you do not replace the whole map.

await updateDoc(doc(db, "users", "alice"), {
  age: 30,
  "address.city": "Mumbai",          // only city changes
  loginCount: increment(1),
  tags: arrayUnion("vip"),
  lastSeen: serverTimestamp()
});

updateDoc fails with not-found if the document is missing. When you want create-or-update behaviour, use setDoc with { merge: true }.

Several fields in one call are applied atomically, so readers never see half of the change. Transforms such as increment and arrayUnion are computed on the server, which avoids lost updates when many clients write at once.

Take quiz
How do you change only address.city inside a nested map?
Use dot notation: 'address.city'
Call setDoc with that one field and no options
Pass {address: {city: 'Pune'}}, which merges into the map
Use slash notation: 'address/city'
What does updateDoc do when the target document does not exist?
Succeeds and writes nothing
Creates it silently
Fails with a not-found error
Creates it only inside a transaction

11. How do you delete a document or a field in Firestore?

deleteDoc removes a document. To remove just one field, call updateDoc with deleteField().

await deleteDoc(doc(db, "users", "alice"));

await updateDoc(doc(db, "users", "bob"), {
  nickname: deleteField()
});

Deleting a document does not delete its subcollections. To clear a whole collection tree, use the Admin SDK recursiveDelete or firebase firestore:delete --recursive. Looping deleteDoc from a client is slow and fragile.

Billing note: every document removed counts as one billed delete, so wiping a large collection costs one delete per document plus the reads needed to find them. For data that expires on a schedule, a TTL policy is usually cheaper to operate than a cleanup script.

Take quiz
Which approach removes just one field from a document?
arrayRemove on that field
updateDoc with deleteField()
deleteDoc with a field path
Setting the field to null, which erases it
Best way to delete a large collection with subcollections from a server?
A browser loop of deleteDoc calls
Dropping it through the security rules
deleteDoc on the collection path
Admin SDK recursiveDelete or the CLI --recursive flag

12. What are Firestore security rules?

Security rules are declarative rules, evaluated on Google's servers, that decide whether a request from the web, Android, or iOS SDK is allowed. Without matching rules, access is denied.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }
  }
}

Rules can inspect request.auth, resource.data (stored data), and request.resource.data (incoming data). Server libraries such as the Admin SDK use IAM and bypass these rules.

Take quiz
Do security rules apply to Admin SDK calls?
Only for write operations
Only in Datastore mode
No, server libraries use IAM and bypass rules
Yes, exactly like client calls
Which variable holds the data a client is trying to write?
request.resource.data
resource.data
request.auth.token
database.document

13. What are the two Firestore database modes?

A Firestore database runs in either Native mode or Datastore mode. You choose when creating it, and it cannot be switched afterwards.

Aspect Native mode Datastore mode
Target use Mobile, web, and server apps Server workloads moving from Cloud Datastore
Client SDKs Yes (web, Android, iOS) No, server libraries only
Real-time listeners Supported Not supported
Offline cache Supported Not available
API Firestore API Datastore API

Pick Native for almost any new project, unless you are migrating an existing Datastore application.

Take quiz
Which mode supports real-time listeners and offline sync for client SDKs?
Neither, you need Realtime Database
Both equally
Datastore mode
Native mode
Once a database exists, its mode:
Changes based on the SDK you use
Cannot be switched later
Can be toggled in settings anytime
Can be changed once a year

14. What is a real-time listener in Firestore?

A real-time listener is created with onSnapshot. It delivers the current result first and then pushes a new snapshot every time the watched document or query result changes.

const unsubscribe = onSnapshot(
  query(collection(db, "orders"), where("status", "==", "open")),
  (snap) => {
    snap.docChanges().forEach((c) => console.log(c.type, c.doc.id));
  }
);
// later
unsubscribe();

Each change is added, modified, or removed. You are billed one read per document in the initial result and one per later change, so detach listeners when a screen closes.

Take quiz
How do you stop a Firestore listener?
Call the unsubscribe function returned by onSnapshot
Do nothing, listeners expire after a minute
Delete the query path
Call closeConnection() on db
After the initial snapshot, what does a listener bill?
One read for each full result refresh
One read per minute connected
One read per document that is added or changes in the result
Nothing, updates are free

15. What is offline persistence in Firestore?

Offline persistence keeps a local cache of documents your app has read or listened to. While offline, reads are served from that cache and writes are queued and sent when the connection returns.

It is on by default for Android and iOS. On the web it must be enabled, and recent SDKs do that through the cache settings:

import { initializeFirestore, persistentLocalCache,
         persistentMultipleTabManager } from "firebase/firestore";

const db = initializeFirestore(app, {
  localCache: persistentLocalCache({
    tabManager: persistentMultipleTabManager()
  })
});

Listeners fire immediately for local writes, which is called latency compensation. The write promise only resolves once the server acknowledges it.

Take quiz
While offline, what happens to a setDoc call?
It is discarded
It is queued locally and synced on reconnect
It blocks the UI thread until online
It throws a network error immediately
Which platforms enable offline persistence by default?
None, it is always opt-in
Web browsers
Only the Node.js server SDK
Android and iOS

16. What are the key limits of a Firestore document?

Document design is shaped by a handful of hard and soft limits:

Limit Value
Document size 1 MiB
Single field value 1 MiB minus 89 bytes
Subcollection depth 100 levels
Map nesting depth 20 levels
Index entries per document 40,000
Sustained writes to one document About 1 per second (soft)

The write guideline matters for hot items such as counters or shared status documents. Past roughly one write per second to the same document you will see contention, so spread load with sharding. Always confirm numbers on the current quotas page since Google adjusts them.

Take quiz
Roughly how many sustained writes per second should a single document receive?
About 10,000
There is no guidance
About one
About 500
What is the maximum depth of nested subcollections?
100 levels
3 levels
20 levels
1,000 levels

17. How do you query documents with where clauses?

Build a query with query() and one or more where() filters, then run it with getDocs. Multiple filters combine with AND.

const q = query(
  collection(db, "products"),
  where("category", "==", "books"),
  where(&quot;price&quot;, &quot;<&quot;, 500),
  orderBy(&quot;price&quot;),
  limit(20)
);
const snap = await getDocs(q);

Supported operators include ==, !=, <, <=, >, >=, in, not-in, array-contains, and array-contains-any. Mixing equality and range filters on different fields usually needs a composite index.

Take quiz
Which operator checks whether an array field contains one given value?
array-any
in
== (matches the whole array only)
array-contains
A query with status == 'active' and age > 30 typically needs:
A security-rule update
A composite index
No index of any kind
A Cloud Function

18. What are Firestore indexes?

Every Firestore query is answered from an index, a sorted list of field values pointing at documents. Because of that, query time grows with the size of the result set, not with the size of the collection.

Firestore builds single-field indexes automatically for every field (ascending, descending, and array-contains). Queries that combine fields in ways those cannot serve need a manually defined composite index.

Indexes are not free. Each one adds storage cost and a little write latency, so you can add index exemptions for fields you never query, such as large text blobs.

You can check which indexes exist, and whether a composite one is still building, in the Firebase console under Firestore, then Indexes. A new composite index on a large collection can take a while to finish building.

Take quiz
Firestore query performance depends mainly on:
The size of the result set, not the whole collection
The total number of documents scanned
The number of collections in the database
The length of the document ID
Single-field indexes are:
Created manually after the first query fails
Available only for string fields
Created automatically for each field
Created only when you request them

19. What is the difference between Firestore and Realtime Database?

Both are Firebase NoSQL databases with live sync, but they model data differently. Realtime Database is one large JSON tree. Firestore uses documents and collections with richer queries.

Aspect Cloud Firestore Realtime Database
Data model Documents in collections Single JSON tree
Queries Compound filters, sorting, indexed One sort or filter per query, deep reads
Scaling Automatic, multi-region option Scale by sharding databases manually
Billing Per read, write, delete, plus storage Mostly bandwidth and storage
Latency Low Very low, good for presence
Offline Web, Android, iOS Android and iOS (web limited)

Many apps use Firestore as the main store and keep Realtime Database for presence or typing indicators.

Take quiz
Which database offers compound filters and sorting in one query?
Both are identical here
Cloud Firestore
Realtime Database
Neither one
Realtime Database billing is mostly based on:
Per index entry
Per listener minute
Per document operation
Bandwidth and storage

20. What is the difference between set, add, and update in Firestore?

All three write data, but they differ in how the document ID is chosen and what happens to existing data.

Call Document ID Existing document
setDoc You supply it Overwritten, unless merge: true
addDoc Auto-generated Always creates a new document
updateDoc You supply it Only listed fields change; fails if missing

A common upsert pattern is setDoc(ref, data, { merge: true }). It creates the document if needed and leaves fields not in the payload alone.

Choose by intent. Use addDoc for append-only data such as log entries, setDoc when the ID carries meaning (a user uid or an order number), and updateDoc when a missing document should be treated as a bug rather than silently created.

Take quiz
Which call upserts users/{uid} without wiping other fields?
setDoc without options
addDoc
setDoc with { merge: true }
updateDoc, which creates it if missing
What does setDoc with merge: true do to fields not in the payload?
Leaves them untouched
Sets them to null
Rejects the write
Deletes them

21. How do Firestore transactions work?

A transaction is a function that reads documents, then writes, and commits everything atomically. If another client changes a document you read before you commit, Firestore re-runs your function.

await runTransaction(db, async (tx) => {
  const ref = doc(db, &quot;stock&quot;, &quot;sku42&quot;);
  const snap = await tx.get(ref);          // reads first
  const qty = snap.data().qty;
  if (qty < 1) throw new Error(&quot;Out of stock&quot;);
  tx.update(ref, { qty: qty - 1 });        // writes last
});

flowchart TD
A["Start transaction"] --> B["Run function: reads then writes"]
B --> C{Read documents changed by someone else?}
C -- No --> D["Commit all writes atomically"]
C -- Yes --> E{Retries left?}
E -- Yes --> B
E -- No --> F["Fail with error"]

Reads must come before writes. Transactions do not work offline.

Take quiz
Inside a transaction, reads must happen:
Only on the server SDK
In a separate batch
After all writes
Before any writes
Why should a transaction function avoid side effects like sending email?
Each email would be billed as a write
It may run more than once when contention occurs
Transactions block network calls
Writes would be committed twice

22. What is a batched write in Firestore?

A batched write groups several set, update, and delete operations into one atomic commit. Either all succeed or none are applied. Unlike a transaction, a batch has no reads.

const batch = writeBatch(db);
batch.set(doc(db, &quot;orders&quot;, &quot;o1&quot;), { total: 40 });
batch.update(doc(db, &quot;users&quot;, &quot;alice&quot;), { orders: increment(1) });
batch.delete(doc(db, &quot;carts&quot;, &quot;alice&quot;));
await batch.commit();

Batches also work offline and are queued like single writes. Older docs capped a batch at 500 operations; check the current quota page, as request size is now the main constraint.

Batches fit fan-out updates, such as writing a post and incrementing the author's post count together. With no read step, a batch never retries on contention, which makes it cheaper than a transaction under heavy load.

Take quiz
In a batch of three writes, the second fails. What is applied?
None of the writes
The first and third
Only the first
All three, and the error is logged
Can you read documents in the middle of a batched write?
Yes, using batch.read()
Only in Native mode
No, batches are write-only
Yes, with getDoc inside the batch

23. What is the difference between transactions and batched writes?

Both commit multiple writes atomically. The deciding factor is whether the writes depend on a value you must read first.

Aspect Transaction Batched write
Reads allowed Yes, before writes No
Atomic Yes Yes
Contention Retries the function No retries needed
Offline Fails Queued and synced
Typical use Check stock then decrement Update several docs together

If you only need to push known changes to several documents, use a batch. If the new value depends on the current one, use a transaction or a field transform like increment.

Under heavy contention a transaction keeps retrying, while a batch just commits. If a code review shows a transaction wrapped around writes that never depend on a read, a batch would be simpler and faster.

Take quiz
Decrement stock only if the current stock is above zero. Which fits?
A plain updateDoc
A transaction
A batched write
A collection group query
Which one keeps working while the device is offline?
Neither
Transactions
Only server SDK transactions
Batched writes, which queue locally

24. How does Firestore pricing work?

Firestore bills per operation rather than per hour of capacity. You pay for document reads, writes, and deletes, plus storage and network egress.

A daily free quota covers 50,000 reads, 20,000 writes, 20,000 deletes, and 1 GiB of storage. Rates vary by location, so check the pricing page.

  • A query returning no results still costs one read
  • Listeners cost one read per initial document, then one per change
  • Storage includes index entries, not just document bodies
  • Aggregation queries bill by index entries scanned

Estimate cost by counting documents touched, not API calls. A screen that lists 50 items and opens a listener costs 50 reads on load, plus one read per later change. Multiply by daily active users to forecast the bill, and set budget alerts in Google Cloud Billing to catch surprises early.

Take quiz
A query returns zero documents. How many reads are billed?
As many as documents scanned
Zero
One
One per index entry
What is billed in addition to the documents themselves?
Index entry storage
The number of subcollections
Collection names
The size of security rules

25. How do you paginate query results with cursors?

Order the query, set a limit, and pass the last document of the previous page to startAfter.

const first = query(collection(db, &quot;posts&quot;), orderBy(&quot;createdAt&quot;, &quot;desc&quot;), limit(20));
const page1 = await getDocs(first);
const last = page1.docs[page1.docs.length - 1];

const next = query(collection(db, &quot;posts&quot;), orderBy(&quot;createdAt&quot;, &quot;desc&quot;),
                   startAfter(last), limit(20));

Avoid offset: skipped documents are still billed as reads. If many documents share the same sort value, add the document ID as a final orderBy so page boundaries stay stable.

For live pagination, attach a listener to each page query. For paging backwards, combine limitToLast with endBefore. Keep the last snapshot object rather than just a field value, because the snapshot carries every ordering value the cursor needs.

Take quiz
Why is cursor pagination preferred over offset?
Cursors are free of charge
Offset only sorts descending
Offset breaks transactions
Skipped documents under offset are still billed as reads
To keep page boundaries stable when many docs share one sort value, add:
A TTL policy
The document ID as a last sort field
A second limit()
A where clause on the collection name

26. Why does Firestore require composite indexes?

Firestore can only answer a query by reading one contiguous range of an index. A single-field index cannot give you results filtered on one field and sorted by another, so a composite index over both fields is needed.

For example, where('city','==','Pune').orderBy('rating','desc') needs an index on (city, rating). Equality-only filters across fields are different, because Firestore can merge single-field indexes for those.

When a query lacks its index, the SDK error includes a link that creates it. For teams, define indexes in firestore.indexes.json and deploy them with the Firebase CLI.

Index merging has limits. It works for equality filters, but a range filter or an orderBy on a different field breaks the merge. Composite indexes also cost storage and write time, so create only the ones your real queries use.

Take quiz
Which query needs a composite index?
where('city','==','Pune') plus orderBy('rating','desc')
where('city','==','Pune') and where('type','==','cafe')
orderBy('rating') alone
where('city','==','Pune') alone
How should a team share composite index definitions?
Pass them in Admin SDK init options
Embed them in security rules
Keep them in firestore.indexes.json and deploy with the Firebase CLI
Email console screenshots

27. How does Firestore handle OR queries?

Firestore supports OR natively through or() (or Filter.or in server SDKs). Internally it splits the query into disjunctions, runs each against an index, and merges the results.

const q = query(collection(db, &quot;tickets&quot;),
  or(where(&quot;priority&quot;, &quot;==&quot;, &quot;high&quot;),
     where(&quot;assignee&quot;, &quot;==&quot;, uid)));

A query can have up to 30 disjunctions. Operators such as in and array-contains-any are really shorthand for OR. Before native support, the workaround was running separate queries and merging results in the client.

A document matching several branches still appears once in the result. Also note that adding orderBy on a field drops documents that lack that field, which can make an OR query look like it is missing rows.

Take quiz
What is the maximum number of disjunctions in one query?
10
30
3
100
Before native OR support, a common workaround was:
Use SQL UNION in the console
Mirror data into Realtime Database
Attach a TTL to each branch
Run separate queries and merge results in the client

28. What is the difference between single-field and composite indexes?

Aspect Single-field index Composite index
Fields covered One field Two or more fields in a set order
Creation Automatic Manual (console, CLI, or JSON file)
Serves Simple filters, one sort Combined filters and sorts
Control Can be exempted per field Defined per query shape

Order inside a composite index matters. (city, rating) serves equality on city then sort by rating, but not the reverse.

Disable a single-field index when you never query that field. It cuts storage and write cost, and also avoids hotspots on fields such as sequential timestamps.

A quick check: if a query uses only equality filters, merged single-field indexes can usually serve it. The moment you add a sort or range on another field, expect the error message to ask for a composite index.

Take quiz
Which index type does Firestore manage automatically?
Composite indexes
Both need manual creation
Single-field indexes
Neither
You might exempt a single-field index when:
You never query that field and want lower write and storage cost
Security rules demand it
You use transactions
The field is a map key

29. How do you use array-contains and array-contains-any?

array-contains matches documents whose array field includes one value. array-contains-any matches if the array includes any of up to 30 values.

where(&quot;tags&quot;, &quot;array-contains&quot;, &quot;java&quot;)
where(&quot;tags&quot;, &quot;array-contains-any&quot;, [&quot;java&quot;, &quot;kotlin&quot;])

There are limits. You can use at most one array-contains per disjunction, and you cannot combine it with array-contains-any in the same disjunction. Equality on the whole array (==) is a different, exact-match filter.

Arrays suit tags and roles, because each element gets its own index entry. Keep them small: huge arrays generate many index entries and push a document toward the 40,000-entry limit. For many-to-many relations that keep growing, use a separate document per relation instead.

Take quiz
Which clause matches documents whose tags array has 'java' or 'kotlin'?
== ['java','kotlin']
in on the tags field
array-contains with two values
array-contains-any
Can array-contains and array-contains-any share one disjunction?
Yes, but only with orderBy
No, Firestore rejects that combination
Yes, but only inside transactions
Yes, in any amount

30. How do you atomically increment a counter in Firestore?

Use the increment() field transform. The server applies the change itself, so concurrent updates from many clients do not overwrite each other and you skip the read.

await updateDoc(doc(db, &quot;posts&quot;, &quot;p1&quot;), {
  likes: increment(1)
});

If the field is missing or not numeric, it is set to the increment value. Transforms also work offline and replay on reconnect.

The limit is still about one write per second to one document. For busier counters, use shards.

Typical uses are like counts, view counters, and stock adjustments that need no condition. When the update depends on a condition, such as only decrement while above zero, increment alone cannot express it, so switch to a transaction. A decrement is simply increment(-1).

Take quiz
Why is increment(1) safer than read-then-write?
The server applies it atomically, so concurrent updates are not lost
It costs no writes
It bypasses security rules
It caches the value on the client
increment() applied to a missing field results in:
A string field
A null field
The field starting from the increment value
An error

31. How do security rules use request.auth?

request.auth describes the signed-in Firebase user. It is null for unauthenticated requests, and otherwise exposes uid and token (claims such as email_verified or custom roles).

match /notes/{noteId} {
  allow read: if request.auth != null;
  allow write: if request.auth.uid == resource.data.ownerId;
}

For role checks you can use custom claims on the token, or look up a role document with get(). Each get() or exists() in a rule counts as a billed document read.

Rules are not filters. A query must itself include a condition, such as where('ownerId','==',uid), that guarantees every possible result passes the rule. Otherwise the whole query is rejected, even if the matching documents would have been allowed.

Take quiz
What is request.auth for an unauthenticated request?
An object with uid 'guest'
null
false
An empty map with uid ''
Reading a role document with get() inside a rule has what cost impact?
It counts only in the emulator
It is free because rules run server-side
It is billed as a write
It counts as a billed document read

32. When should you use subcollections instead of nested maps?

Use a subcollection when the data can grow without bound or needs its own queries. Use a nested map when it is small, bounded, and always read together with the parent.

Need Subcollection Nested map
Unbounded growth Yes No, 1 MiB cap
Query or paginate on its own Yes No
Independent listener or rules Yes No
Fetched with parent in one read No Yes

Every write to a map entry also rewrites the whole parent document, which concentrates writes on one hot item. A chat room that stores its messages in a map would hit the size and write limits quickly.

Take quiz
Chat messages per room grow without limit. Best model?
A map keyed by message ID in the room document
An array of messages in the room document
A messages subcollection under each room
One document per user holding all messages
Which data suits a nested map best?
A small, fixed address block always read with the profile
Line items across all customers
Comments on a viral post
Audit log entries

33. Why is denormalization common in Firestore?

Firestore has no joins, and a query only returns documents from one collection. To show a feed in a single read, apps copy the fields they need, such as the author's name, into each post.

The price is update fan-out. When the author renames themselves, every copy must change. Teams handle this with a Cloud Function trigger or a batched write that updates the copies.

The rule of thumb is to denormalize data that is read often and changes rarely, and keep fast-changing data in one place.

Typical copies are the author name on posts, the product title on order lines, and the category name on products. Treat a copy as a snapshot. For an order line, keeping the price at purchase time is actually the correct behaviour.

Take quiz
Why duplicate the author's name into every post document?
It reduces document size
Security rules cannot read other documents
Firestore requires it for indexing
To render a feed in one query without joins
What is the main cost of denormalizing?
Transactions stop working
Changes must be propagated to every copy
You lose automatic indexes
Queries get slower with more fields

34. How do Cloud Functions Firestore triggers work?

A Firestore trigger runs a Cloud Function when a document is created, updated, deleted, or written at a matching path. The path can contain wildcards, such as orders/{orderId}.

const { onDocumentCreated } = require(&quot;firebase-functions/v2/firestore&quot;);

exports.notifyOrder = onDocumentCreated(&quot;orders/{orderId}&quot;, (event) => {
  const order = event.data.data();
  console.log(&quot;New order&quot;, event.params.orderId, order.total);
});

Update and write triggers expose before and after snapshots. Delivery is at-least-once, so handlers must be idempotent. A trigger that writes back to its own document can start an infinite loop, so guard with a condition.

Use event.data.before and event.data.after in update triggers to react only when one specific field changed, which also prevents the loop above. Heavy work should be queued as a task so the function returns before its timeout.

Take quiz
What is the delivery guarantee of Firestore triggers?
At-least-once, so handlers must be idempotent
Exactly-once always
At-most-once
Best-effort with no retries ever
A trigger on users/{id} updates the same users document. What is the risk?
Security rules reject it
Storage is billed twice
An infinite trigger loop
The document gets locked

35. How does the Firestore emulator help with testing?

The Firestore emulator runs a local Firestore instance through the Firebase CLI. Tests read and write against it with no production data and no billing.

firebase emulators:start --only firestore

// in app or test setup
import { connectFirestoreEmulator } from &quot;firebase/firestore&quot;;
connectFirestoreEmulator(db, &quot;localhost&quot;, 8080);

It also evaluates your security rules, which you can test with @firebase/rules-unit-testing by acting as different users. The Emulator UI lets you inspect data, and you can export and import seed data between runs.

A typical CI job runs firebase emulators:exec, which starts the emulator, runs the test command, and shuts down. Seed data loads with --import and saves with --export-on-exit, so every developer begins from the same state. Point only test builds at the emulator.

Take quiz
Which call points a web SDK at the local emulator?
enableNetwork(db)
connectFirestoreEmulator
useLocalCache(db)
initializeApp with debug: true
What does testing against the emulator avoid?
Needing the Firebase CLI
Having to write security rules
Needing indexes
Touching production data and incurring billing

36. How can you reduce Firestore read costs?

Reads are the usual bill driver, so the goal is to read fewer documents, not just faster ones.

  1. Use count(), sum(), or average() aggregation queries instead of downloading documents to count them.
  2. Add limit() and cursor pagination so screens load a page at a time.
  3. Detach listeners when a screen closes and avoid listening to whole large collections.
  4. Enable offline persistence and use cached reads where fresh data is not required.
  5. Denormalize hot fields so a screen needs one query instead of many.
  6. For read-heavy public data, serve data bundles from a CDN instead of querying per user.

Check the usage dashboard first. A single forgotten listener often explains a surprise bill.

Another cheap win is replacing a polling screen with one listener on a narrow query, since it bills only for changes. For public data read by many users, such as a product catalog, one CDN-cached bundle can replace thousands of identical query reads.

Take quiz
What is the cheapest way to count documents matching a query?
getDocs and then .length
A collection group scan
A count() aggregation query
onSnapshot and counting changes
A common cause of unexpectedly high read counts is:
Listeners left attached after the user leaves the screen
Defining security rules
Creating composite indexes
Using document references

37. What is a TTL policy in Firestore?

A TTL (time-to-live) policy tells Firestore to automatically delete documents once a timestamp field in them has passed. You define it per collection group and field.

gcloud firestore fields ttls update expireAt \
  --collection-group=sessions --enable-ttl

Deletion is not instant. It usually happens within about a day after expiry, so keep checking the timestamp in queries if stale documents must never show. TTL deletes are billed as normal deletes, and subcollections are not removed with the parent.

It suits sessions, temporary tokens, and short-lived logs.

Two practical notes. The expiry can differ per document, so one session may expire in an hour and another in a month. And if the field is missing or is not a timestamp, that document never expires.

Take quiz
When does TTL actually delete an expired document?
After a fixed 30 days
Exactly at the timestamp second
Only when someone queries it
Not instantly, typically within about a day of expiry
Which field type must a TTL policy use?
Number of epoch seconds
Timestamp
ISO date string
Map with seconds and nanos

38. How do you back up and restore Firestore?

Firestore offers three routes, and the right one depends on how fast and how granular recovery must be.

Method What it does Restores into
Managed export gcloud firestore export gs://bucket writes data to Cloud Storage Import into a database
Scheduled backups Daily or weekly snapshots with retention up to 14 weeks A new database
Point-in-time recovery Keeps versions for 7 days, one-minute granularity Read, or export then restore

Exports bill reads, and backups bill storage. Test a restore before you need one, because restoring scheduled backups creates a new database rather than overwriting the live one.

Match the method to the recovery goal. PITR suits an accidental delete noticed within days, scheduled backups suit disaster recovery and retention rules, and exports suit moving data between projects or into BigQuery. Enable PITR and a schedule before launch, since neither can recover data from before it was turned on.

Take quiz
Scheduled backups are restored into:
A new database
A CSV file in Cloud Storage
The same database, overwriting it in place
Realtime Database
What is the maximum point-in-time recovery window?
24 hours
30 days
7 days
365 days

39. Why should you avoid sequential document IDs in Firestore?

Firestore splits data by key range. If IDs always increase, such as order-000123, every new write lands in the same range, creating a hotspot with higher latency and contention.

Auto-generated IDs are random, so writes spread across many ranges. The same problem hits an indexed timestamp or counter field that only grows, which is why such fields sometimes get an index exemption.

For a brand-new collection under heavy load, follow the 500/50/5 rule: start at 500 operations per second and raise it by 50% every 5 minutes.

If you need ordering, keep a createdAt field and query on it, instead of encoding order in the ID.

Watch for the symptom too: rising write latency and errors concentrated on a narrow key range usually mean a hotspot. Fix it with random IDs, or prefix keys with a hash or shard value so writes spread out.

Take quiz
Why do auto-generated IDs scale better?
They skip security-rule checks
They are random, so writes spread across key ranges
They are shorter, so indexes shrink
They are cached on the client
What is the traffic ramp-up guidance for a new collection?
Start at full load at once
Start at 10 ops/sec and double hourly
Start at 5,000 ops/sec and hold
Start at 500 ops/sec and raise 50% every 5 minutes

40. How do you build a scalable distributed counter in Firestore?

A single document handles roughly one sustained write per second, so a hot counter is split into shards. Each increment goes to a random shard, and the total is the sum of all shards.

// counters/likes/shards/{0..N-1}
const shard = Math.floor(Math.random() * NUM_SHARDS);
await updateDoc(doc(db, &quot;counters/likes/shards&quot;, String(shard)),
                { count: increment(1) });

// read total
const snap = await getDocs(collection(db, &quot;counters/likes/shards&quot;));
const total = snap.docs.reduce((s, d) => s + d.data().count, 0);

With 50 shards you can sustain around 50 writes per second. Reading costs one read per shard, so you can also use a sum() aggregation query. Start with few shards and add more as traffic grows.

Shard count is a tuning knob. Too few shards means contention, too many makes each read more expensive. Size it from your peak write rate, and you can add shards later because new increments simply pick from the larger range.

Take quiz
A page needs about 50 counter updates per second. What is the right approach?
Wrap each increment in a transaction on one document
Use a larger document
Spread increments across 50 or more shard documents
Increment a single document faster
How do you read the total of a sharded counter?
Sum the count from every shard
Add a TTL to each shard
Read shard 0 only
Read the largest shard

41. What happens when a Firestore transaction conflicts?

It depends on the SDK. Client SDKs (web, mobile) use optimistic concurrency: if a document you read changes before commit, the transaction rolls back and your function runs again, five attempts by default, then fails with an aborted error.

Server SDKs (Admin, Java, Python, Go) use pessimistic locking. Documents you read are locked until commit, so competing writers wait, with a maximum transaction time of 270 seconds.

To cut conflicts, keep transactions short, touch few documents, and avoid hot documents. Heavily contended counters should be sharded.

A typical failure is many users hitting one hot document, say a shared inventory record. Each retry re-reads and re-runs your logic, and after the allowed attempts the caller sees an aborted error. Catch it and retry later, or redesign the hot spot, for example by sharding.

Take quiz
On conflict, a client SDK transaction:
Retries forever until it succeeds
Returns a partial result
Replays only the failed write
Re-runs the whole update function, five attempts by default
Server SDK transactions differ from client ones by using:
Optimistic version checks only
Pessimistic locks on documents they read
A client-side mutex
A vote across replicas per transaction

42. How does Firestore achieve strong consistency?

A read after a committed write returns that write, because data is replicated synchronously across several zones (or regions) and a write is only committed once a majority of replicas accept it.

Index entries are updated in the same atomic commit as the document, so a query never sees an index that is out of date compared with the data.

Location type Replication Trade-off
Regional Across zones in one region Lower latency and cost
Multi-region Across several regions Higher availability, higher write latency and cost

Client caches are the exception. A listener may show local pending writes, flagged by hasPendingWrites, before the server confirms them.

Strong consistency does not mean zero latency. A read must confirm the latest committed state, and cross-region writes pay a network round trip to reach the replica quorum, which is why multi-region writes are a little slower.

Take quiz
Right after a committed write, a server read returns:
The new value, because reads are strongly consistent
Possibly the old value for a few seconds
Either value at random
The old value until indexes rebuild
Choosing a multi-region location trades off:
Eventual consistency for lower latency
Better queries but no transactions
Higher availability for higher write latency and cost
Lower availability for cheaper writes

43. Explain the lifecycle of a Firestore real-time listener?

A listener moves through attach, initial sync, live updates, possible disconnection, and detach.

sequenceDiagram
participant App
participant SDK as Firestore SDK
participant Server
App->>SDK: onSnapshot(query)
SDK-->>App: Cached snapshot (fromCache = true)
SDK->>Server: Open listen stream
Server-->>SDK: Initial results
SDK-->>App: Snapshot (fromCache = false)
Server-->>SDK: Document changes
SDK-->>App: Updated snapshot
App->>SDK: unsubscribe()
SDK->>Server: Close listen target
  1. Attach: the SDK first emits cached results if it has any, marked fromCache: true.
  2. Initial sync: the server sends the full result, billed as one read per document.
  3. Live updates: each change arrives as added, modified, or removed.
  4. Local writes: your own writes fire instantly with hasPendingWrites.
  5. Disconnect: the SDK serves cache and resumes with a token. If offline under 30 minutes, you pay only for changes. Beyond that, it re-reads like a new query.
  6. Detach: calling the unsubscribe function closes the stream.
Take quiz
In a snapshot, fromCache = true means:
The listener has been detached
The data came from the local cache and is not yet confirmed by the server
The query was served from a CDN
The data is guaranteed to be invalid
A listener was offline for over 30 minutes and reconnects. Billing is:
A flat reconnect fee
Free
Only the changed documents
As a new query, re-reading the whole result set

44. How does Firestore offline sync handle write conflicts?

The SDK applies a write to the local cache immediately, fires listeners, then sends it to the server when online. This is latency compensation.

There is no merge logic for conflicts. When two clients change the same field offline, the last write the server processes wins for that field. A set without merge overwrites the whole document.

  • If security rules reject a queued write, the SDK rolls back the local change and listeners see the value revert.
  • Transactions need a connection and fail offline.
  • Use field transforms such as increment and arrayUnion for conflict-safe updates.
  • serverTimestamp() is estimated locally until the server sets the real value.

For quantities such as stock or a like count, never write a computed number from an offline client. Send increment(1) so the server combines every device's change instead of letting one overwrite the rest.

Take quiz
Two offline clients set the same field. Which value wins after sync?
The one with the lower timestamp
A merge of both values
Whichever write the server processes last
Neither, Firestore raises a conflict error
An offline write later fails security rules. What happens on the client?
The local change is rolled back and listeners see it revert
It retries forever
It is forced through as admin
The local value stays permanently

45. How would you model a chat app in Firestore?

Keep a small chat document and put the messages in a subcollection.

erDiagram
CHAT ||--o{ MESSAGE : contains
USER }o--o{ CHAT : participates
chats/{chatId}
  { participants: [uidA, uidB], lastMessage: &quot;See you&quot;, updatedAt: ts }
chats/{chatId}/messages/{messageId}
  { senderId: uidA, text: &quot;Hi&quot;, createdAt: serverTimestamp }

  • Inbox: where('participants','array-contains',uid).orderBy('updatedAt','desc') with a composite index.
  • Thread: orderBy('createdAt','desc').limit(50) with a listener, then startAfter for older pages.
  • Store lastMessage on the chat document so the inbox needs no extra reads.
  • Rules should allow access only to users listed in participants.
  • Typing and presence are short-lived, so put them in Realtime Database or throttle writes.

Control cost early. A listener on a thread bills one read per new message, so load only the latest 50 and fetch older pages on demand. Keep read receipts in one small document per chat member instead of rewriting every message.

Take quiz
Which query should power a user's chat list?
getDocs on every chat, then filter in the client
A collection group query on all messages
An unfiltered listener on the whole chats collection
where participants array-contains uid, ordered by updatedAt
How can the chat list show the last message without extra reads?
Fetch the full messages subcollection per chat
Store lastMessage on the chat document
Run an offset query per chat
Query the newest message of each chat on every render

46. How do you implement full-text search with Firestore?

Firestore has no built-in full-text search. You either approximate it or pair Firestore with a search engine.

  1. Prefix search: use a range query, where('name','>=',term).where('name','<=',term + '\uf8ff'). It matches the start of a string only.
  2. Token arrays: store lowercase words in an array and use array-contains for exact word hits.
  3. External engine: sync to Algolia, Typesense, Elasticsearch, or Meilisearch through a Firestore trigger or an official extension, and query that engine from the app.
  4. Semantic search: use Firestore vector search when meaning matters more than keywords.

For serious search needs, the external engine is the usual answer, since it adds ranking, typo tolerance, and highlighting.

Keep search records small. Index only searchable fields such as title and tags, and keep the full record in Firestore. The search call returns document IDs, and the app then fetches each document by ID, which keeps the external index cheap.

Take quiz
How does native prefix-only search work in Firestore?
A range filter between the prefix and the prefix plus '\uf8ff'
A contains() filter
A LIKE operator
A regex() query operator
How is data usually kept in sync with an external search index?
Clients dual-write on every screen
Firestore pushes events through TTL
Firestore triggers via Cloud Functions or an extension push changes
The search engine reads your security rules

47. What is vector search in Firestore?

Vector search finds documents whose stored embedding is closest to a query embedding. You store the embedding in a vector field, create a vector index, and call findNearest.

const coll = db.collection(&quot;docs&quot;);
const result = await coll.findNearest(&quot;embedding&quot;,
  FieldValue.vector(queryEmbedding),
  { limit: 5, distanceMeasure: &quot;COSINE&quot; }).get();

  • Distance measures: EUCLIDEAN, COSINE, and DOT_PRODUCT.
  • Vectors can have up to 2048 dimensions, and a query returns up to 1000 results.
  • You can pre-filter with where clauses before the nearest-neighbour step.

Typical uses are semantic search and retrieval for AI assistants.

Combine vector search with metadata filters, for example restricting to a tenant ID before ranking by similarity. Create the index with the same dimension as your embedding model, and regenerate every embedding if you switch models, because vectors from different models cannot be compared.

Take quiz
Which distance measure compares the angle between two embeddings?
EUCLIDEAN
COSINE
MANHATTAN
HAMMING
Before running findNearest you must:
Enable full-text search
Create a TTL policy
Convert vectors to strings
Create a vector index for the field

48. What are aggregation queries in Firestore?

Aggregation queries compute count(), sum(), and average() on the server and return only the result, without downloading the documents.

const snap = await getAggregateFromServer(
  query(collection(db, &quot;orders&quot;), where(&quot;status&quot;, &quot;==&quot;, &quot;paid&quot;)),
  { total: sum(&quot;amount&quot;), orders: count(), avg: average(&quot;amount&quot;) }
);
console.log(snap.data().total);

They are billed at one read per up to 1,000 index entries scanned, with a minimum of one read. Compare that with fetching 50,000 documents just to count them. They need a supporting index and do not support real-time listeners.

Use them for dashboards and totals, such as order count and revenue for a status filter, with one call instead of loading every order. sum and average ignore non-numeric values, so clean data first. For a continuously updating total, keep a running counter document maintained by a trigger.

Take quiz
What does a count() aggregation return to the client?
A live stream of the count
All matching documents plus a count
Only the number, not the documents
Document IDs only
How are aggregation queries billed?
One read per up to 1,000 index entries scanned
A flat write fee
One read per matched document
They are free

49. How do you enforce data validation and roles in security rules?

Validate incoming data with request.resource.data, and gate roles with custom claims or a lookup document.

function isAdmin() { return request.auth.token.admin == true; }

match /products/{id} {
  allow read: if true;
  allow create: if isAdmin()
    && request.resource.data.keys().hasOnly([&#x27;name&#x27;,&#x27;price&#x27;,&#x27;createdAt&#x27;])
    && request.resource.data.name is string
    && request.resource.data.price is number
    && request.resource.data.price >= 0;
  allow update: if isAdmin()
    && request.resource.data.createdAt == resource.data.createdAt;
}

hasOnly blocks unexpected fields, and comparing against resource.data makes a field immutable. Custom claims travel in the ID token, so they cost no extra read, but a claim change only applies after the token refreshes. A get() role lookup is current but adds a billed read.

Test rules in the emulator with signed-in, signed-out, and wrong-user cases. Rules that are too permissive rarely fail loudly, so a unit test proving that a non-owner cannot write is the only reliable safeguard.

Take quiz
Which check stops clients from adding unexpected fields?
request.auth.token.keys()
resource.data.size() < 10
request.time < timestamp
request.resource.data.keys().hasOnly([...])
What is an advantage of custom-claim roles over a get() lookup?
They cannot be set from the Admin SDK
No extra document read per rule evaluation
Changes apply instantly to tokens already issued
They have no size limit

50. How do you migrate from Realtime Database to Firestore?

This is a remodeling job, not a file copy. Realtime Database is one JSON tree, so the data must become documents and collections.

flowchart TD
A["Redesign data model"] --> B["Dual-write new changes"]
B --> C["Backfill history with a script"]
C --> D["Verify counts and samples"]
D --> E["Switch reads to Firestore"]
E --> F["Retire old paths"]
  1. Design collections and subcollections around your queries, not around the old tree.
  2. Dual-write through client code or a Realtime Database trigger so no changes are lost.
  3. Backfill history by exporting JSON and writing it with the Admin SDK BulkWriter, which throttles and retries.
  4. Rewrite security rules in the Firestore rules language.
  5. Verify, switch reads, and keep the old data until you are confident.

Both databases can coexist, so keep Realtime Database for presence if it fits.

Take quiz
Which approach gives the safest low-downtime migration?
Dual-write, backfill, verify, then switch reads
Delete the old data, then rebuild
Switch clients first and backfill later
Export JSON and import it as-is
Which tool suits high-volume backfill writes with throttling?
One huge transaction
Manual adds in the console
BulkWriter in the Admin SDK
A client-side onSnapshot loop
«
»

Comments & Discussions