Skip to main content

Enhanced SQLAlchemy query builder with advanced filtering, sorting, pagination, and search capabilities.

Project description

StrapAlchemy

Enhanced SQLAlchemy query builder with advanced filtering, sorting, pagination, and search capabilities.

StrapAlchemy is a powerful query builder library for SQLAlchemy that provides Strapi-style query syntax for building complex database queries with ease.

Features

  • Advanced Filtering: Strapi-style operators ($eq, $in, $contains, $between, etc.)
  • Nested Relationship Filtering: Filter through related models with dot notation
  • Flexible Sorting: Sort by direct fields or relationship fields
  • Pagination: Support for both page-based and offset-based pagination
  • Full-Text Search: BM25 search with ParadeDB integration and ILIKE fallback
  • Field Selection: Select specific fields to optimize query performance
  • Relationship Population: Eager load relationships to prevent N+1 queries
  • Query Optimization: Built-in caching and optimization for better performance
  • Model Serialization: Convert SQLAlchemy models to dictionaries easily

Installation

pip install strapalchemy

Quick Start

from sqlalchemy import select
from strapalchemy import FilterBuilder, SortBuilder, Paginator, SearchEngine
from strapalchemy.models import Base
from sqlalchemy import Column, Integer, String

# Define your model
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)
    status = Column(String)

# Build your query
query = select(User)

# Apply filters
filter_builder = FilterBuilder(User)
query = await filter_builder.apply_filters(query, {
    "name": {"$contains": "John"},
    "status": {"$eq": "active"}
})

# Apply sorting (sync - no await needed)
sort_builder = SortBuilder(User)
query = sort_builder.apply_sorting(query, ["name:asc", "created_at:desc"])

# Apply search (sync - no await needed)
search_engine = SearchEngine()
query = search_engine.apply_search(query, User, "search term")

# Apply pagination
paginator = Paginator(session, User)
query, meta = await paginator.apply_pagination(query, {"page": 1, "page_size": 20})

# Execute
result = await session.execute(query)
users = result.scalars().all()

Filtering

StrapAlchemy supports Strapi-style filtering operators:

Operator Description Example
$eq Equal {"status": {"$eq": "active"}}
$ne Not equal {"status": {"$ne": "deleted"}}
$lt Less than {"age": {"$lt": 18}}
$lte Less than or equal {"age": {"$lte": 18}}
$gt Greater than {"age": {"$gt": 18}}
$gte Greater than or equal {"age": {"$gte": 18}}
$in In list {"status": {"$in": ["active", "pending"]}}
$notIn Not in list {"status": {"$notIn": ["deleted"]}}
$contains Contains {"name": {"$contains": "John"}}
$containsi Contains (case insensitive) {"name": {"$containsi": "john"}}
$startsWith Starts with {"email": {"$startsWith": "admin"}}
$endsWith Ends with {"email": {"$endsWith": "@example.com"}}
$null Is null {"deleted_at": {"$null": true}}
$notNull Is not null {"email": {"$notNull": true}}
$between Between {"created_at": {"$between": ["2024-01-01", "2024-12-31"]}}
$or Logical OR {"$or": [{"status": {"$eq": "active"}}, {"status": {"$eq": "pending"}}]}
$and Logical AND {"$and": [{"status": {"$eq": "active"}}, {"verified": {"$eq": true}}]}

Nested Relationship Filtering

# Filter by relationship fields (async)
query = await filter_builder.apply_filters(query, {
    "organization": {"slug": {"$eq": "acme"}}
})

# Or use dot notation (async)
query = await filter_builder.apply_filters(query, {
    "organization.slug": {"$eq": "acme"}
})

Sorting

# Sort by single field (sync - no await needed)
query = sort_builder.apply_sorting(query, "name:asc")

# Sort by multiple fields (sync - no await needed)
query = sort_builder.apply_sorting(query, ["name:asc", "created_at:desc"])

# Sort by relationship field (sync - no await needed)
query = sort_builder.apply_sorting(query, ["organization.name:asc"])

Pagination

Page-based Pagination

query, meta = await paginator.apply_pagination(query, {
    "page": 1,
    "page_size": 20
})

# meta contains:
# {
#     "page": 1,
#     "page_size": 20,
#     "page_count": 5,
#     "total": 100,
#     "has_next": True,
#     "has_previous": False
# }

Offset-based Pagination

query, meta = await paginator.apply_pagination(query, {
    "start": 0,
    "limit": 20
})

Field Selection

from strapalchemy import FieldSelector

field_selector = FieldSelector(User)
query = field_selector.apply_field_selection(query, ["id", "name", "email"])

# Select relationship fields (sync - no await needed)
query = field_selector.apply_field_selection(query, ["id", "name", "organization.slug"])

Model Serialization

from strapalchemy import ModelSerializer

# Serialize a single model
data = ModelSerializer.serialize(user, fields=["id", "name", "email"])

# Serialize a list
data = ModelSerializer.serialize(users, fields=["id", "name"])

# Serialize with relationships
data = ModelSerializer.serialize(user, populate="organization")

# Serialize with nested relationships
data = ModelSerializer.serialize(user, populate=["organization", "user.role"])

Search

from strapalchemy import SearchEngine

search_engine = SearchEngine()

# Add searchable fields to your model
class User(Base):
    __tablename__ = "users"
    __searchable__ = {
        "text_fields": ["name", "email", "bio"]
    }
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)
    bio = Column(String)

# Apply search (sync - no await needed)
query = search_engine.apply_search(query, User, "John Doe")

Advanced Usage

Combining Multiple Builders

async def get_users(filters=None, sort=None, search=None, page=None):
    query = select(User)

    if filters:
        query = await filter_builder.apply_filters(query, filters)

    if sort:
        query = sort_builder.apply_sorting(query, sort)  # sync

    if search:
        query = search_engine.apply_search(query, User, search)  # sync

    if page:
        query, meta = await paginator.apply_pagination(query, page)

    result = await session.execute(query)
    return result.scalars().all(), meta

Requirements

  • Python >= 3.12
  • SQLAlchemy >= 2.0.45
  • python-dateutil >= 2.9.0
  • rich >= 13.0.0

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please use the GitHub issue tracker.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

strapalchemy-0.2.2.tar.gz (64.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

strapalchemy-0.2.2-py3-none-any.whl (33.0 kB view details)

Uploaded Python 3

File details

Details for the file strapalchemy-0.2.2.tar.gz.

File metadata

  • Download URL: strapalchemy-0.2.2.tar.gz
  • Upload date:
  • Size: 64.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for strapalchemy-0.2.2.tar.gz
Algorithm Hash digest
SHA256 d07d5c85af01f9861e326d5c094eb2b95c3b797e41447d63b4ae448d95aa837e
MD5 e32e3bd014dff73d41aadc148c634788
BLAKE2b-256 420008b5e18892c2679bf47c13eadf6e0e8f9469e1d0bd089751decc4f74a319

See more details on using hashes here.

File details

Details for the file strapalchemy-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: strapalchemy-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 33.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for strapalchemy-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e5af12d66937c4e3a05b3e96e2ae6fe8144d200f3d07a90b50cfc7f23a6d64d4
MD5 e07625076bd22120e1ba86f5b677265f
BLAKE2b-256 2d7e04d54fc610f95cae72df59fd195bebc3fe91762864d77e69cadfba3c1d77

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page