PracticeVideosBlogFlashcards

7.2 Firestore Data, Indexes, and Transactions

Key Takeaways

  • Collection queries do not recursively download subcollections.

  • Collection-group scope and the actual filter/order pattern determine required indexes.

  • Transactions support read-dependent writes; client/database concurrency modes differ.

Last updated: October 2026

NoSQL & Object Storage: Firestore, Bigtable, and Cloud Storage

Cloud-native architectures frequently process unstructured media, real-time hierarchical documents, and high-frequency analytical telemetry that exceed the operational boundaries of traditional relational databases. Google Cloud provides three specialized storage solutions for these workloads: Cloud Firestore for flexible serverless document storage, Cloud Bigtable for massive petabyte-scale wide-column time-series analytics, and Cloud Storage (GCS) for durable, highly available object storage. The Google Cloud Professional Cloud Developer exam tests your ability to engineer optimal row keys, configure transactional concurrency, manage data lifecycles, and implement secure direct client file transfers.


Cloud Firestore: Architecture and Operating Modes

Cloud Firestore is a fully managed, serverless, document-oriented NoSQL database featuring regional or multi-region replication according to database location, strong consistency, and rich querying capabilities.

Operating Modes: Native Mode vs. Datastore Mode

When creating a Firestore database, you must choose between two mutually exclusive operating modes (mode-conversion eligibility is constrained; do not assume an existing populated database can simply be switched):

CapabilityFirestore Native ModeFirestore in Datastore Mode
Data ModelDocuments, Collections, and SubcollectionsEntities, Kinds, and Properties
Client AccessWeb, iOS, Android, Unity SDKs + Server Client LibrariesServer Client Libraries only (No web/mobile SDKs)
Real-Time SyncSupported (Real-time snapshots and listeners)Not supported
Offline PersistenceAutomatic offline caching in mobile and web SDKsNot supported
Security RulesFirestore Security Rules based on user identityIAM-based server access; do not confuse this with client Security Rules
Write ThroughputOptimized for collaborative web/mobile appsOptimized for massive server-to-server write throughput
Primary Use CasesMobile, web, gaming, and real-time collaborative stateLegacy App Engine Datastore migration, high-throughput server backends

The Document-Collection Model & Shallow Queries

In Firestore Native Mode, data is stored in documents, which are organized into collections. Documents can contain nested fields (primitive types, arrays, maps) and can host nested subcollections.

/users (Collection)
  └── user_42 (Document)
        ├── name: "Jane Doe"
        ├── role: "engineer"
        └── /orders (Subcollection)
              └── order_881 (Document)
                    ├── item: "Laptop"
                    └── total: 1200

Exam Trap: Shallow Queries: In Firestore Native Mode, all queries are shallow. Querying a collection retrieves documents within that specific collection only. It never returns documents located in nested subcollections. If your application queries /users, it receives user documents; it will never download the nested /orders documents. To query documents across all orders subcollections simultaneously, you must use a Collection Group Query, which needs a collection-group-scope index appropriate for its filters and ordering; a simple collection-group query does not always require a composite index.


Firestore Indexing, Atomic Operations & Security Rules

Single-Field vs. Composite Indexes

  • Single-Field Indexes: Firestore automatically maintains ascending and descending indexes for every scalar field in every document, as well as array-contains indexes for array fields. This enables immediate single-clause filtering (e.g., WHERE status == 'active').
  • Index Exemptions: Automatic indexing consumes storage and incurs write latency. For fields that contain large strings, frequently updated counters, or unindexed metadata maps, developers should configure Index Exemptions to reduce storage costs and accelerate write throughput.
  • Composite Indexes: Complex combinations of filters and ordering can require a composite index; multiple equality filters can sometimes use index merging. When combining equality with inequality operators (<, <=, >, >=) or sort orders (ORDER BY), Firestore requires a composite index:
    Query:   db.collection('tickets').where('status', '==', 'open').orderBy('priority', 'desc')
    Index:   tickets [ status: ASC, priority: DESC ]
    
    If a composite index is missing, Firestore rejects the query and returns an error containing a direct URL to generate the required index in the Google Cloud Console.

Atomic Operations: Transactions vs. Batched Writes

FeatureFirestore Transactions (runTransaction)Firestore Batched Writes (db.batch())
Atomic GuaranteesAll-or-nothing atomicity across all operationsAll-or-nothing atomicity across all operations
Read OperationsSupports reads and writes (All reads must precede writes)Write-only (No reads permitted inside the batch)
Concurrency ModelMobile/web SDKs use optimistic concurrency; server clients honor the database concurrency mode, commonly pessimistic in Standard edition. Retryable contention can rerun the transaction callbackNo read-dependent transaction callback; write requests can still fail and need suitable retry handling
Operation LimitSubject to request-size, time, index, and documented transaction limitsSubject to the current API/SDK batch and request-size limits; chunk bulk workloads
Use CaseRead-modify-write workflows (e.g., debiting an account balance)Bulk ingestion, mass deletions, multi-document imports
// Atomic Transaction Example (All Reads Must Precede Writes)
await db.runTransaction(async (transaction) => {
  const accountRef = db.collection('accounts').doc('user_123');
  const doc = await transaction.get(accountRef);
  
  if (!doc.exists) throw new Error("Account does not exist!");
  const currentBalance = doc.data().balance;
  if (currentBalance < 500) throw new Error("Insufficient funds!");
  
  // Writes occur after all reads are evaluated
  transaction.update(accountRef, { balance: currentBalance - 500 });
  const logRef = db.collection('audit_logs').doc();
  transaction.set(logRef, { user: 'user_123', amount: 500, timestamp: new Date() });
});

Firestore Security Rules

When mobile or web applications access Firestore directly using client SDKs, Firestore Security Rules enforce authentication, authorization, and data validation:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
      match /orders/{orderId} {
        allow read: if request.auth != null && request.auth.uid == userId;
        allow create, update: if request.auth != null && request.auth.uid == userId
                              && request.resource.data.total > 0;
        allow delete: if request.auth != null && request.auth.uid == userId;
      }
    }
  }
}

Official references checked October 18, 2026: Google Cloud 1.

Test Your Knowledge

An online gaming application stores player inventories in Firestore Native Mode. When a player purchases a virtual loot box, the application must verify the player's currency balance in their user document, deduct 500 coins, insert a new item document into the player's inventory subcollection, and update a global items-purchased counter document. If another purchase occurs simultaneously, the balance must not fall below zero. Which Firestore operation pattern must the developer use?

A

Execute a Firestore batched write using db.batch() committing all three operations simultaneously

B

Execute a Firestore transaction using db.runTransaction() where all document reads are completed before issuing writes

C

Issue concurrent asynchronous document updates using Promise.all() without transaction wrappers

D

Execute a Collection Group query across all player subcollections with optimistic offline persistence enabled

Sections you finish are checked off in the contents.