Ojasa Mirai

Ojasa Mirai

Cloud

Loading...

Learning Level

๐ŸŸข Beginner๐Ÿ”ต Advanced
๐Ÿ”ง Firebase Project Setup๐Ÿ”‘ Firebase Authentication๐Ÿ” Firestore Security Rules๐Ÿ” Firestore Advanced Queriesโšก Realtime Database๐Ÿ“ Firebase Storageโšก Firebase Functions Advanced๐Ÿงช Firebase Emulator๐Ÿ“Š Firebase Analyticsโšก Firebase Performance๐Ÿ“ฌ Firebase Cloud Messaging๐Ÿš€ Firebase Hosting Advanced
Cloud/Firebase Integration/Firestore Advanced Queries

๐Ÿ” Firestore Advanced Queries

Introduction

Advanced Firestore queries enable you to retrieve exactly the data you need with filtering, ordering, pagination, and real-time synchronization.

Key Learning Outcomes

By the end of this lesson, you'll understand:

  • Complex filtering with multiple conditions
  • Ordering and limiting results
  • Pagination patterns
  • Real-time listeners and snapshots
  • Query optimization
  • Compound queries
  • Aggregation queries

Basic Queries

import { collection, query, where, orderBy, limit, getDocs } from "firebase/firestore";
import { db } from "./firebase-config";

// Simple query
const q = query(
  collection(db, "posts"),
  where("published", "==", true),
  orderBy("createdAt", "desc"),
  limit(10)
);

const snapshot = await getDocs(q);
snapshot.forEach(doc => {
  console.log(doc.id, doc.data());
});

Multiple Filter Conditions

import { query, where, collection, getDocs } from "firebase/firestore";

// Multiple conditions (AND)
const q = query(
  collection(db, "posts"),
  where("author", "==", "userId123"),
  where("published", "==", true),
  where("category", "==", "tech"),
  orderBy("createdAt", "desc")
);

const results = await getDocs(q);

Range Queries

import { query, where, collection, getDocs } from "firebase/firestore";

// Range queries
const q = query(
  collection(db, "products"),
  where("price", ">=", 10),
  where("price", "<=", 100),
  where("inStock", "==", true)
);

const results = await getDocs(q);

Ordering and Pagination

import { query, orderBy, limit, startAfter, collection, getDocs } from "firebase/firestore";

let lastVisible;

// First page
const firstPageQuery = query(
  collection(db, "posts"),
  orderBy("createdAt", "desc"),
  limit(10)
);

const firstSnapshot = await getDocs(firstPageQuery);
lastVisible = firstSnapshot.docs[firstSnapshot.docs.length - 1];

// Next page
const nextPageQuery = query(
  collection(db, "posts"),
  orderBy("createdAt", "desc"),
  startAfter(lastVisible),
  limit(10)
);

const nextSnapshot = await getDocs(nextPageQuery);

Real-Time Listeners

import { query, collection, onSnapshot, where } from "firebase/firestore";

// Real-time listener
const q = query(
  collection(db, "posts"),
  where("author", "==", "userId123")
);

const unsubscribe = onSnapshot(q, (snapshot) => {
  console.log("Current posts:", snapshot.docs.length);
  
  snapshot.docChanges().forEach((change) => {
    if (change.type === "added") {
      console.log("New post:", change.doc.data());
    } else if (change.type === "modified") {
      console.log("Modified post:", change.doc.data());
    } else if (change.type === "removed") {
      console.log("Deleted post:", change.doc.data());
    }
  });
});

// Stop listening
unsubscribe();

Compound Queries

import { query, where, collection, getDocs } from "firebase/firestore";

// IN query
const q1 = query(
  collection(db, "posts"),
  where("category", "in", ["tech", "science", "health"])
);

// Array contains
const q2 = query(
  collection(db, "posts"),
  where("tags", "array-contains", "javascript")
);

// Array contains any
const q3 = query(
  collection(db, "posts"),
  where("tags", "array-contains-any", ["react", "vue", "angular"])
);

React Hook for Pagination

import { useEffect, useState } from 'react';
import { query, collection, orderBy, limit, startAfter, getDocs } from "firebase/firestore";
import { db } from './firebase-config';

export function usePaginatedQuery(collectionName, pageSize = 10) {
  const [posts, setPosts] = useState([]);
  const [lastVisible, setLastVisible] = useState(null);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  const loadMore = async () => {
    setLoading(true);
    
    let q;
    if (lastVisible) {
      q = query(
        collection(db, collectionName),
        orderBy("createdAt", "desc"),
        startAfter(lastVisible),
        limit(pageSize)
      );
    } else {
      q = query(
        collection(db, collectionName),
        orderBy("createdAt", "desc"),
        limit(pageSize)
      );
    }

    const snapshot = await getDocs(q);
    const newDocs = snapshot.docs.map(doc => ({
      id: doc.id,
      ...doc.data()
    }));

    setPosts(prev => [...prev, ...newDocs]);
    setLastVisible(snapshot.docs[snapshot.docs.length - 1]);
    setHasMore(snapshot.docs.length === pageSize);
    setLoading(false);
  };

  return { posts, loadMore, loading, hasMore };
}

Snapshot Listeners with Error Handling

import { query, collection, onSnapshot } from "firebase/firestore";

const q = query(collection(db, "posts"));

const unsubscribe = onSnapshot(
  q,
  (snapshot) => {
    console.log("Current documents:", snapshot.size);
  },
  (error) => {
    console.error("Error listening to posts:", error);
  }
);

Aggregation Queries

import { collection, query, where, aggregate, count } from "firebase/firestore";

// Count documents
const cq = query(
  collection(db, "posts"),
  where("published", "==", true)
);

const countSnapshot = await aggregate(cq, {
  count: count()
});

console.log("Published posts:", countSnapshot.data().count);

Query Performance Tips

// โœ“ Good: Create composite index for complex filters
const q = query(
  collection(db, "posts"),
  where("author", "==", "userId"),
  where("published", "==", true),
  orderBy("createdAt", "desc")
);

// โœ— Avoid: Too many subcollection reads
// Instead, denormalize data when appropriate

// โœ“ Good: Use limits to reduce document reads
const q = query(
  collection(db, "posts"),
  limit(10)
);

// โœ— Avoid: Reading all documents and filtering
const allPosts = await getDocs(collection(db, "posts"));

Key Takeaways

  • **Query filtering** enables precise data retrieval
  • **Ordering** controls result sequence
  • **Pagination** handles large datasets efficiently
  • **Real-time listeners** keep data synchronized
  • **Snapshot listeners** enable reactive UI updates
  • **Compound queries** combine multiple conditions
  • **Indexes** improve query performance
  • **Limits** reduce costs and improve responsiveness

Next Steps

Explore Realtime Database for alternative synchronization, or implement Firebase Storage for file management.


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep โ€ข Build Real โ€ข Verify Skills โ€ข Launch Forward

Courses

PythonFastapiReactJSCloud

ยฉ 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn