AsyncIO MongoDB Object Document Mapper for Python using type hints
—
ODMantic provides a functional query building system using Python operators and functions to construct MongoDB queries and sort expressions.
Functions for building comparison queries against model fields.
def eq(field, value):
"""
Equality comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB equality query
"""
def ne(field, value):
"""
Not equal comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB not equal query
"""
def gt(field, value):
"""
Greater than comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB greater than query
"""
def gte(field, value):
"""
Greater than or equal comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB greater than or equal query
"""
def lt(field, value):
"""
Less than comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB less than query
"""
def lte(field, value):
"""
Less than or equal comparison.
Args:
field: FieldProxy for the model field
value: Value to compare against
Returns:
QueryExpression: MongoDB less than or equal query
"""Functions for set-based queries.
def in_(field, sequence):
"""
Value in sequence query.
Args:
field: FieldProxy for the model field
sequence: Iterable of values to match against
Returns:
QueryExpression: MongoDB $in query
"""
def not_in(field, sequence):
"""
Value not in sequence query.
Args:
field: FieldProxy for the model field
sequence: Iterable of values to exclude
Returns:
QueryExpression: MongoDB $nin query
"""Functions for pattern and text matching.
def match(field, pattern):
"""
Regular expression pattern matching.
Args:
field: FieldProxy for the model field
pattern: Regular expression pattern (str or compiled Pattern)
Returns:
QueryExpression: MongoDB regex query
"""Functions for combining multiple query expressions.
def and_(*elements):
"""
Logical AND of multiple query expressions.
Args:
*elements: QueryExpression objects to combine with AND
Returns:
QueryExpression: MongoDB $and query
"""
def or_(*elements):
"""
Logical OR of multiple query expressions.
Args:
*elements: QueryExpression objects to combine with OR
Returns:
QueryExpression: MongoDB $or query
"""
def nor_(*elements):
"""
Logical NOR of multiple query expressions.
Args:
*elements: QueryExpression objects to combine with NOR
Returns:
QueryExpression: MongoDB $nor query
"""Functions for building sort expressions.
def asc(field):
"""
Ascending sort order.
Args:
field: FieldProxy for the model field
Returns:
SortExpression: MongoDB ascending sort
"""
def desc(field):
"""
Descending sort order.
Args:
field: FieldProxy for the model field
Returns:
SortExpression: MongoDB descending sort
"""Core query and sort expression types.
class QueryExpression(dict):
"""MongoDB query expression dictionary."""
class SortExpression(dict):
"""MongoDB sort expression dictionary."""from odmantic import Model, AIOEngine
from odmantic.query import eq, ne, gt, gte, lt, lte
class Product(Model):
name: str
price: float
category: str
stock: int
async def comparison_examples(engine: AIOEngine):
# Equality (can also use == operator)
expensive = await engine.find(Product, eq(Product.price, 100.0))
# Or: expensive = await engine.find(Product, Product.price == 100.0)
# Not equal
not_electronics = await engine.find(Product, ne(Product.category, "electronics"))
# Or: not_electronics = await engine.find(Product, Product.category != "electronics")
# Greater than
expensive_items = await engine.find(Product, gt(Product.price, 50.0))
# Or: expensive_items = await engine.find(Product, Product.price > 50.0)
# Less than or equal
affordable = await engine.find(Product, lte(Product.price, 25.0))
# Or: affordable = await engine.find(Product, Product.price <= 25.0)
# Range query
mid_range = await engine.find(
Product,
gte(Product.price, 20.0),
lte(Product.price, 50.0)
)
# Or: mid_range = await engine.find(Product, Product.price >= 20.0, Product.price <= 50.0)from odmantic.query import in_, not_in
async def set_examples(engine: AIOEngine):
# Find products in specific categories
tech_products = await engine.find(
Product,
in_(Product.category, ["electronics", "computers", "phones"])
)
# Exclude out-of-stock items
available_products = await engine.find(
Product,
not_in(Product.stock, [0])
)
# Find products with specific names
featured_products = await engine.find(
Product,
in_(Product.name, ["iPhone", "MacBook", "iPad"])
)from odmantic.query import match
import re
async def pattern_examples(engine: AIOEngine):
# Find products with names starting with "Pro"
pro_products = await engine.find(
Product,
match(Product.name, r"^Pro")
)
# Case-insensitive search
apple_products = await engine.find(
Product,
match(Product.name, re.compile(r"apple", re.IGNORECASE))
)
# Find products with email pattern in description (if you had a description field)
# email_mentions = await engine.find(Product, match(Product.description, r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"))from odmantic.query import and_, or_, nor_
async def logical_examples(engine: AIOEngine):
# AND: expensive electronics
expensive_electronics = await engine.find(
Product,
and_(
eq(Product.category, "electronics"),
gt(Product.price, 100.0)
)
)
# OR: either cheap or high stock
bargain_or_available = await engine.find(
Product,
or_(
lt(Product.price, 20.0),
gt(Product.stock, 100)
)
)
# NOR: neither expensive nor out of stock
reasonable_available = await engine.find(
Product,
nor_(
gt(Product.price, 200.0),
eq(Product.stock, 0)
)
)
# Complex nested logical operations
complex_query = await engine.find(
Product,
and_(
or_(
eq(Product.category, "electronics"),
eq(Product.category, "computers")
),
gt(Product.price, 50.0),
gt(Product.stock, 0)
)
)from odmantic.query import asc, desc
async def sorting_examples(engine: AIOEngine):
# Sort by price ascending
cheap_first = await engine.find(Product, sort=asc(Product.price))
# Sort by price descending
expensive_first = await engine.find(Product, sort=desc(Product.price))
# Multiple sort criteria (tuple of sort expressions)
sorted_products = await engine.find(
Product,
sort=(desc(Product.category), asc(Product.price))
)
# Sort with query
electronics_by_price = await engine.find(
Product,
eq(Product.category, "electronics"),
sort=desc(Product.price)
)async def complex_examples(engine: AIOEngine):
# Find available, reasonably priced electronics
good_electronics = await engine.find(
Product,
and_(
eq(Product.category, "electronics"),
gte(Product.price, 10.0),
lte(Product.price, 500.0),
gt(Product.stock, 0)
),
sort=asc(Product.price)
)
# Find products that match multiple criteria
filtered_products = await engine.find(
Product,
or_(
and_(
eq(Product.category, "books"),
lt(Product.price, 30.0)
),
and_(
in_(Product.category, ["electronics", "computers"]),
gte(Product.stock, 5),
lte(Product.price, 200.0)
)
),
sort=(desc(Product.stock), asc(Product.price)),
limit=20
)
# Text search with multiple patterns
search_results = await engine.find(
Product,
or_(
match(Product.name, r"(?i)(pro|premium|deluxe)"),
and_(
match(Product.category, r"(?i)electronics"),
gt(Product.price, 100.0)
)
),
sort=desc(Product.price)
)def build_product_filter(min_price=None, max_price=None, categories=None, in_stock_only=False):
"""Build a dynamic query based on parameters."""
conditions = []
if min_price is not None:
conditions.append(gte(Product.price, min_price))
if max_price is not None:
conditions.append(lte(Product.price, max_price))
if categories:
conditions.append(in_(Product.category, categories))
if in_stock_only:
conditions.append(gt(Product.stock, 0))
if len(conditions) == 1:
return conditions[0]
elif len(conditions) > 1:
return and_(*conditions)
else:
return {} # No conditions
async def dynamic_search(engine: AIOEngine):
# Use the dynamic filter
query = build_product_filter(
min_price=20.0,
max_price=100.0,
categories=["electronics", "books"],
in_stock_only=True
)
products = await engine.find(Product, query, sort=asc(Product.price))from datetime import datetime, timedelta
from odmantic import ObjectId
class Order(Model):
customer_id: ObjectId
created_at: datetime
total: float
async def date_queries(engine: AIOEngine):
# Find recent orders
week_ago = datetime.utcnow() - timedelta(days=7)
recent_orders = await engine.find(
Order,
gte(Order.created_at, week_ago)
)
# Find orders for specific customer
customer_orders = await engine.find(
Order,
eq(Order.customer_id, ObjectId("507f1f77bcf86cd799439011"))
)
# Find large orders this month
month_start = datetime.utcnow().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
large_orders = await engine.find(
Order,
and_(
gte(Order.created_at, month_start),
gt(Order.total, 500.0)
),
sort=desc(Order.total)
)Install with Tessl CLI
npx tessl i tessl/pypi-odmantic