Skip to main content
Firestore Pydantic ODM exposes a rich, type-safe query API built on top of Google Cloud Firestore’s async client. Every query method is a coroutine or async generator, so you can stream results without loading the entire collection into memory. Filters are expressed using plain Python comparison operators on your model’s class-level attributes — no string literals required for field names.

The find() Async Generator

find() is the primary method for querying a collection. It returns an async generator that yields model instances one at a time as Firestore streams them.

Signature


Filter Syntax

Filters are created by applying comparison operators directly to your model’s class-level field descriptors. Each expression returns a (field_name, operator, value) tuple that find() passes to Firestore.

Comparison Operators

All Available Operators (FirestoreOperators)

in_() — Match Any Value in a List

Use the .in_() helper method on a FirestoreField to generate an IN filter:
Similarly, .not_in_() generates a NOT_IN filter:

array_contains() — Filter by Array Membership

Use .array_contains() to find documents where an array field includes a specific value:
Use .array_contains_any() to match any of multiple values:

Multiple Filters

Pass a list of filter expressions to filters= to apply all of them. Firestore evaluates them as a logical AND:

find_one() — First Match or None

find_one() runs the same query as find() with limit=1 and returns the first matching instance, or None if nothing matches. It accepts the same filters, parent, projection, and order_by parameters.

get() — Fetch a Document by ID

When you already know a document’s ID, use get() instead of find(). It returns the model instance or None if the document does not exist.
For subcollections, pass the parent instance:

exists() — Check Document Existence

exists() returns True or False without fetching the document’s data. This is more efficient than get() when you only need to verify the document exists.

count() — Count Matching Documents

count() returns an integer representing the number of documents that match the given filters. It uses Firestore’s native count() aggregation when available, falling back to a lightweight select([]) fetch otherwise.

Ordering Results

Control the sort order of find() results with the order_by parameter.

Single Field

Pass a (field, direction) tuple using OrderByDirection.ASCENDING or OrderByDirection.DESCENDING:
You can also pass just a field descriptor to sort ascending by default:

Multiple Fields

Pass a list of (field, direction) tuples to sort by multiple fields. Firestore requires a composite index when combining multiple order_by fields with inequality filters:

Pagination with limit and offset

Use limit to cap the number of results and offset to skip documents — together they implement cursor-free pagination:
offset causes Firestore to read and discard skipped documents, which counts toward your read quota. For large collections, consider cursor-based pagination using Firestore’s start_after() on the underlying client for better cost efficiency.

collection_group_find() — Cross-Parent Queries

collection_group_find() queries across all subcollections with the same name, regardless of which parent document they belong to. This is equivalent to Firestore’s collectionGroup() API.
The _parent_path private attribute on each yielded instance is automatically set to the path of the parent document (e.g. "users/uid_123").
Collection group queries require a Firestore composite index on the __name__ field. Create it in the Firebase console or deploy it via firestore.indexes.json before running collection group queries in production.

Complete Query Example

Projections

Fetch only the fields you need to reduce bandwidth and cost.

Batch Operations

Perform atomic multi-document writes in a single round-trip.