The Problem
Traditional MongoDB queries rely on string-based field names, creating maintainability challenges. Strings. Everywhere. No autocomplete. No refactoring support. A single typo like "pirce" instead of "price" only surfaces at runtime.
# The old way — fragile
await collection.find({"pirce": {"$lt": 10}}) # typo only caught at runtime
The Vision
The goal was simple Pythonic query syntax:
products = await repo.find(Product.category == "electronics", Product.price < 10)
No operator strings. No dictionaries. Python operators directly on model fields — with full IDE autocomplete and compile-time validation.
Implementation
The FieldRef Class
Overloading comparison operators (<, >, ==, etc.) on a FieldRef class returns query expression objects rather than booleans. Product.price < 10 generates {"price": {"$lt": 10}} — not False.
Metaclass Integration
A custom metaclass intercepts attribute access on the model class. When you access Product.price, the metaclass checks if price exists in the model fields and returns a FieldRef instance instead of the raw value.
Expression Composition
Logical operators combine expressions naturally:
# AND
(Product.price < 10) & (Product.category == "electronics")
# OR
(Product.category == "food") | (Product.category == "drink")
# NOT
~(Product.status == "archived")
Extended Operators
Beyond comparisons, method calls cover MongoDB-specific operations:
Product.tags.in_(["mobile", "backend"])
Product.name.regex("^go", "i")
Product.metadata.exists()
Product.reviews.elem_match(Review.rating > 4)
Aggregation Pipeline Support
A resolve_agg_refs() function recursively converts FieldRef objects to MongoDB field references (e.g., "$total"), enabling type-safe aggregation pipeline construction without writing "$fieldname" strings manually.
Key Benefits
- Compile-time field validation — typos caught before the code runs
- IDE autocomplete — full intellisense on model fields
- Safe refactoring — rename a field, the query breaks visibly
- Composable logic — build queries incrementally as Python objects
- Gradual adoption — works alongside existing raw dictionary queries