Filtering
This example demonstrates declarative filtering with FilterSpec, And/Or groups, and the FilterDep FastAPI dependency.
FilterSpec Basics
Each FilterSpec declares a single condition:
from pypaginate import FilterSpec
# field = value (default operator is "eq")
FilterSpec(field="status", value="active")
# field >= value
FilterSpec(field="age", operator="gte", value=18)
# field contains substring
FilterSpec(field="name", operator="contains", value="alice")
In-Memory Filtering
Apply filter specs to a Python list via the pipeline:
from pypaginate import FilterSpec, OffsetParams
from pypaginate.adapters.memory import MemoryBackend, MemoryFilterBackend
from pypaginate.engine.paginator import Paginator
from pypaginate.engine.pipeline import SyncPipeline
products = [
{"name": "iPhone 15", "category": "Electronics", "price": 999.99, "stock": 50},
{"name": "MacBook Pro", "category": "Electronics", "price": 2499.99, "stock": 25},
{"name": "AirPods Pro", "category": "Electronics", "price": 249.99, "stock": 100},
{"name": "Python Book", "category": "Books", "price": 49.99, "stock": 200},
{"name": "Standing Desk", "category": "Furniture", "price": 599.99, "stock": 15},
{"name": "Headphones", "category": "Electronics", "price": 199.99, "stock": 0},
]
pipeline = SyncPipeline(
Paginator(MemoryBackend()),
filter_backend=MemoryFilterBackend(),
)
# Filter: Electronics only
page = pipeline.execute(
products,
OffsetParams(page=1, limit=10),
filters=[FilterSpec(field="category", value="Electronics")],
)
print(f"Electronics: {len(page)} items") # 4
# Filter: price between 50 and 300
page = pipeline.execute(
products,
OffsetParams(page=1, limit=10),
filters=[
FilterSpec(field="price", operator="gte", value=50),
FilterSpec(field="price", operator="lte", value=300),
],
)
print(f"$50-$300: {len(page)} items") # 2
# Filter: in stock (stock > 0)
page = pipeline.execute(
products,
OffsetParams(page=1, limit=10),
filters=[FilterSpec(field="stock", operator="gt", value=0)],
)
print(f"In stock: {len(page)} items") # 5
Nested Groups with And / Or
Use And() and Or() for complex boolean logic:
from pypaginate import And, Or, FilterSpec
# (category = "Electronics" OR category = "Books") AND price < 500
group = And(
Or(
FilterSpec(field="category", value="Electronics"),
FilterSpec(field="category", value="Books"),
),
FilterSpec(field="price", operator="lt", value=500),
)
Groups nest up to 5 levels deep for safety against runaway recursion.
Nested group example
from pypaginate import And, Or, FilterSpec
# Complex: (Electronics AND price < 300) OR (Books AND stock > 100)
group = Or(
And(
FilterSpec(field="category", value="Electronics"),
FilterSpec(field="price", operator="lt", value=300),
),
And(
FilterSpec(field="category", value="Books"),
FilterSpec(field="stock", operator="gt", value=100),
),
)
Available Operators
Operator |
Description |
Example Value |
|---|---|---|
|
Equals |
|
|
Not equals |
|
|
Greater than |
|
|
Greater or equal |
|
|
Less than |
|
|
Less or equal |
|
|
Value in list |
|
|
Value not in list |
|
|
Substring match |
|
|
Starts with prefix |
|
|
Ends with suffix |
|
|
SQL LIKE pattern |
|
|
Case-insensitive LIKE |
|
|
Between two values |
|
|
Is None |
(no value needed) |
|
Is not None |
(no value needed) |
|
Regex match |
|
|
Is None, |
(no value needed) |
|
Is not empty |
(no value needed) |
|
Field exists |
(no value needed) |
SQLAlchemy Filtering
Use SQLAlchemyFilterBackend for database queries:
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from pypaginate import FilterSpec, OffsetParams
from pypaginate.adapters.sqlalchemy import (
SQLAlchemyBackend,
SQLAlchemyFilterBackend,
)
from pypaginate.engine.paginator import AsyncPaginator
from pypaginate.engine.pipeline import AsyncPipeline
async def list_products(session: AsyncSession, category: str | None = None):
query = select(Product).order_by(Product.id)
filters = []
if category:
filters.append(FilterSpec(field="category", value=category))
pipeline = AsyncPipeline(
AsyncPaginator(SQLAlchemyBackend(session)),
filter_backend=SQLAlchemyFilterBackend(),
)
return await pipeline.execute(
query,
OffsetParams(page=1, limit=20),
filters=filters,
)
FastAPI FilterDep
FilterDep lets you declare filters as FastAPI query parameters. Subclass it, define fields with FilterField, and non-None values are automatically converted to FilterSpec objects via .to_specs().
For a complete runnable example with FilterDep, SortDep, and SearchDep, see FastAPI Integration.
Next Steps
Keyset Pagination – Cursor-based pagination
FastAPI Example – Full app with filtering, sorting, and search