or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

collections-queries.mdcore-database.mddata-snapshots.mddata-types.mddocument-references.mdindex.mdtransactions-batches.md
tile.json

collections-queries.mddocs/

Collections and Queries

Types for collection references, query building, and query result handling including filtering, ordering, and pagination.

Capabilities

CollectionReference Class

Reference to a Firestore collection, extending Query with collection-specific operations.

class CollectionReference<T = DocumentData, T2 extends DocumentData = DocumentData> extends Query<T, T2> {
  private constructor();
  
  /** Collection ID */
  readonly id: string;
  
  /** Parent document reference, or null for root collections */
  readonly parent: DocumentReference<DocumentData> | null;
  
  /** Full collection path */
  readonly path: string;
  
  /** Get a document reference in this collection */
  doc(documentPath?: string): DocumentReference<T>;
  
  /** Add a new document to this collection */
  add(data: T): Promise<DocumentReference<T>>;
  
  /** Check if this reference equals another */
  isEqual(other: CollectionReference<T>): boolean;
  
  /** Convert this reference to use a different data type */
  withConverter(converter: null): CollectionReference<DocumentData>;
  withConverter<U>(
    converter: FirestoreDataConverter<U>
  ): CollectionReference<U>;
}

Query Class

Base class for building and executing Firestore queries.

class Query<T = DocumentData, T2 extends DocumentData = DocumentData> {
  protected constructor();
  
  /** Firestore instance this query belongs to */
  readonly firestore: FirebaseFirestore;
  
  /** Filter documents where field matches condition */
  where(
    fieldPath: string | FieldPath,
    opStr: WhereFilterOp,
    value: any
  ): Query<T>;
  
  /** Order results by field */
  orderBy(
    fieldPath: string | FieldPath,
    directionStr?: OrderByDirection
  ): Query<T>;
  
  /** Limit number of results */
  limit(limit: number): Query<T>;
  
  /** Limit to last N results (requires orderBy) */
  limitToLast(limit: number): Query<T>;
  
  /** Start results at document snapshot */
  startAt(snapshot: DocumentSnapshot<any>): Query<T>;
  /** Start results at field values */
  startAt(...fieldValues: any[]): Query<T>;
  
  /** Start results after document snapshot */
  startAfter(snapshot: DocumentSnapshot<any>): Query<T>;
  /** Start results after field values */
  startAfter(...fieldValues: any[]): Query<T>;
  
  /** End results before document snapshot */
  endBefore(snapshot: DocumentSnapshot<any>): Query<T>;
  /** End results before field values */
  endBefore(...fieldValues: any[]): Query<T>;
  
  /** End results at document snapshot */
  endAt(snapshot: DocumentSnapshot<any>): Query<T>;
  /** End results at field values */
  endAt(...fieldValues: any[]): Query<T>;
  
  /** Check if this query equals another */
  isEqual(other: Query<T>): boolean;
  
  /** Execute query and get results */
  get(options?: GetOptions): Promise<QuerySnapshot<T>>;
  
  /** Listen for query result changes */
  onSnapshot(observer: {
    next?: (snapshot: QuerySnapshot<T>) => void;
    error?: (error: FirestoreError) => void;
    complete?: () => void;
  }): () => void;
  onSnapshot(
    options: SnapshotListenOptions,
    observer: {
      next?: (snapshot: QuerySnapshot<T>) => void;
      error?: (error: FirestoreError) => void;
      complete?: () => void;
    }
  ): () => void;
  onSnapshot(
    onNext: (snapshot: QuerySnapshot<T>) => void,
    onError?: (error: FirestoreError) => void,
    onCompletion?: () => void
  ): () => void;
  onSnapshot(
    options: SnapshotListenOptions,
    onNext: (snapshot: QuerySnapshot<T>) => void,
    onError?: (error: FirestoreError) => void,
    onCompletion?: () => void
  ): () => void;
  
  /** Convert this query to use a different data type */
  withConverter(converter: null): Query<DocumentData>;
  withConverter<U>(converter: FirestoreDataConverter<U>): Query<U>;
}

Query Types and Constants

type OrderByDirection = 'desc' | 'asc';

type WhereFilterOp =
  | '<'    // Less than
  | '<='   // Less than or equal
  | '=='   // Equal to
  | '!='   // Not equal to
  | '>='   // Greater than or equal
  | '>'    // Greater than
  | 'array-contains'     // Array contains value
  | 'in'                 // Field value in array
  | 'array-contains-any' // Array contains any of the values
  | 'not-in';            // Field value not in array

FieldPath Class

Type-safe field path construction for nested fields.

class FieldPath {
  /** Create a field path from field names */
  constructor(...fieldNames: string[]);
  
  /** Special field path representing document ID */
  static documentId(): FieldPath;
  
  /** Check if this field path equals another */
  isEqual(other: FieldPath): boolean;
}

Usage Examples

Basic Collection Operations

import type { CollectionReference, DocumentData } from "@firebase/firestore-types";

interface User {
  name: string;
  email: string;
  age: number;
}

async function manageCollection(collection: CollectionReference<User>) {
  // Add new document with auto-generated ID
  const docRef = await collection.add({
    name: "Alice",
    email: "alice@example.com",
    age: 30
  });
  
  // Get specific document reference
  const specificDoc = collection.doc("user-123");
  
  // Get all documents in collection
  const snapshot = await collection.get();
  snapshot.forEach(doc => {
    const userData = doc.data(); // Typed as User
    console.log('User:', userData.name);
  });
}

Query Building

import type { Query, WhereFilterOp, OrderByDirection } from "@firebase/firestore-types";

function buildQueries(usersCollection: CollectionReference<User>) {
  // Simple where clause
  const adults = usersCollection.where("age", ">=", 18);
  
  // Multiple filters
  const activeAdults = usersCollection
    .where("age", ">=", 18)
    .where("status", "==", "active");
  
  // Array queries
  const usersInCities = usersCollection
    .where("city", "in", ["New York", "San Francisco", "Seattle"]);
  
  // Ordering and limiting
  const topUsers = usersCollection
    .orderBy("score", "desc")
    .limit(10);
  
  // Pagination with cursors
  const paginatedQuery = usersCollection
    .orderBy("createdAt")
    .startAfter(lastDocSnapshot)
    .limit(20);
  
  return { adults, activeAdults, usersInCities, topUsers, paginatedQuery };
}

Field Paths for Nested Data

import type { FieldPath } from "@firebase/firestore-types";

interface UserProfile {
  basic: {
    name: string;
    email: string;
  };
  preferences: {
    theme: string;
    notifications: boolean;
  };
}

function queryNestedFields(collection: CollectionReference<UserProfile>) {
  // Query nested field using dot notation
  const darkThemeUsers = collection.where("preferences.theme", "==", "dark");
  
  // Query nested field using FieldPath
  const notificationUsers = collection.where(
    new FieldPath("preferences", "notifications"),
    "==",
    true
  );
  
  // Order by nested field
  const orderedByName = collection.orderBy("basic.name");
  
  return { darkThemeUsers, notificationUsers, orderedByName };
}

Real-time Query Listeners

import type { QuerySnapshot, DocumentChange } from "@firebase/firestore-types";

function listenToQuery(query: Query<User>) {
  const unsubscribe = query.onSnapshot({
    next: (snapshot: QuerySnapshot<User>) => {
      console.log(`Query returned ${snapshot.size} documents`);
      
      // Process changes
      const changes = snapshot.docChanges();
      changes.forEach((change: DocumentChange<User>) => {
        const userData = change.doc.data();
        
        switch (change.type) {
          case 'added':
            console.log('New user:', userData.name);
            break;
          case 'modified':
            console.log('Modified user:', userData.name);
            break;
          case 'removed':
            console.log('Removed user:', userData.name);
            break;
        }
      });
    },
    error: (error) => {
      console.error('Query error:', error);
    }
  });
  
  return unsubscribe;
}