fastapi-qengine


Namefastapi-qengine JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
Summaryfastapi-qengine is an advanced query engine for FastAPI, inspired by Loopback 4's filtering system. It allows building complex queries directly from URLs with a flexible syntax.
upload_time2025-08-28 22:28:01
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords fastapi beanie mongodb query filter engine
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # fastapi-qengine

[![PyPI version](https://badge.fury.io/py/fastapi-qengine.svg)](https://badge.fury.io/py/fastapi-qengine)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Un motor de consultas avanzado para FastAPI, inspirado en el poderoso sistema de filtros de Loopback 4. Diseñado inicialmente para Beanie/PyMongo, con planes de expansión a otros ORMs en el futuro.

`fastapi-qengine` te permite construir consultas complejas para tus modelos directamente desde la URL con una sintaxis flexible, ofreciendo una alternativa más potente y con menos configuración que `fastapi-filter`.

## Motivación

Mientras que librerías como `fastapi-filter` son excelentes, a menudo requieren una configuración detallada por cada modelo y campo. `fastapi-qengine` trae la flexibilidad del sistema de filtros de Loopback 4 al ecosistema de FastAPI, permitiendo a los clientes construir consultas complejas con operadores lógicos, selección de campos y ordenamiento desde la URL.

Este proyecto se enfoca únicamente en la **creación de la consulta**, delegando la paginación a librerías especializadas como [fastapi-pagination](https://github.com/uriyyo/fastapi-pagination).

## Características

- **Sintaxis de filtro flexible:** Soporte para JSON anidado en los parámetros de la URL y para JSON completo en formato string.
- **Operadores de consulta avanzados:** Soporte para operadores como `$gt`, `$gte`, `$in`, `$nin`, `$lt`, `$lte`, `$ne`, y más.
- **Combinaciones lógicas:** Soporte completo para consultas `$and` y `$or`.
- **Selección de campos (Proyección):** Elige qué campos devolver en los resultados con `fields`.
- **Ordenamiento dinámico:** Ordena los resultados con `order`.
- **Integración mínima:** Diseñado para funcionar con Beanie y FastAPI con una configuración mínima.
- **Enfocado en consultas:** No se encarga de la paginación, permitiendo la integración con librerías dedicadas.

## Instalación

```bash
pip install fastapi-qengine
pip install fastapi-pagination # Recomendado para la paginación
```

## Ejemplo Rápido

### 1. Define tu modelo Beanie

```python
# main.py
from beanie import Document
from pymongo import AsyncMongoClient

class Product(Document):
    name: str
    category: str
    price: float
    in_stock: bool

    class Settings:
        name = "products"

async def init_db():
    client = AsyncMongoClient("mongodb://localhost:27017")
    await Document.init_all(database=client.db_name, documents=[Product])
```

### 2. Crea tu endpoint de FastAPI

```python
# main.py
from fastapi import FastAPI, Depends
from fastapi_pagination import Page, add_pagination
from fastapi_pagination.ext.beanie import apaginate
from contextlib import asynccontextmanager

from fastapi_qengine import create_qe_dependency
from fastapi_qengine.backends.beanie import BeanieQueryEngine

app = FastAPI()


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    await init_db()
    yield

app = FastAPI(lifespan=lifespan)

# Engine explícito por backend
beanie_engine = BeanieQueryEngine(Product)
qe_dep = create_qe_dependency(beanie_engine)

@app.get("/products", response_model=Page[Product])
async def get_products(q = Depends(qe_dep)):
    # q es una tupla (query, projection_model, sort) lista para apaginate
    query, projection_model, sort = q
    return await apaginate(query, projection_model=projection_model, sort=sort)

add_pagination(app)
```

### 3. Realiza consultas desde la URL

`fastapi-qengine` soporta dos formatos para pasar el filtro, dándote flexibilidad según la complejidad de la consulta.

#### Formato 1: Parámetros de URL anidados (Ideal para consultas simples)

Para consultas directas o para usar desde un navegador, puedes usar la sintaxis de corchetes. Es más legible para filtros sencillos.

*   **Buscar productos con un precio mayor a 50:**
    `/products?filter[where][price][$gt]=50`

*   **Buscar productos en stock de la categoría "electronics":**
    `/products?filter[where][category]=electronics&filter[where][in_stock]=true`

*   **Buscar productos y ordenarlos por precio (descendente):**
    `/products?filter[where][in_stock]=true&filter[order]=-price`

#### Formato 2: Stringified JSON (Recomendado para consultas complejas)

Para consultas que involucran operadores lógicos como `$or`, `$and` o estructuras anidadas complejas, el formato de JSON como string es la mejor opción. Recuerda codificar el JSON para la URL.

*   **Buscar productos en la categoría "electronics" O que cuesten menos de 20:**
    *   **JSON Filter:** `{"where": {"$or": [{"category": "electronics"}, {"price": {"$lt": 20}}]}}`
    *   **URL Codificada:** `/products?filter=%7B%22where%22%3A%20%7B%22%24or%22%3A%20%5B%7B%22category%22%3A%20%22electronics%22%7D%2C%20%7B%22price%22%3A%20%7B%22%24lt%22%3A%2020%7D%7D%5D%7D%7D`

## Sintaxis de Filtro Soportada

El objeto `filter` acepta las siguientes claves, inspiradas en la especificación de Loopback.

### `where`

Un objeto que define las condiciones de búsqueda. Utiliza la sintaxis de operadores de PyMongo.

- `{"where": {"category": "books"}}`
- `{"where": {"price": {"$gte": 10, "$lte": 50}}}`
- `{"where": {"category": {"$in": ["electronics", "appliances"]}}}`
- `{"where": {"$and": [{"in_stock": true}, {"price": {"$lt": 100}}]}}`

### `order`

Una cadena de texto para ordenar los resultados. Usa el prefijo `-` para orden descendente.

- `{"order": "price"}` (ascendente)
- `{"order": "-price"}` (descendente)

### `fields`

Un objeto para especificar qué campos incluir (proyección).

- `{"fields": {"name": 1, "price": 1}}` (incluye solo `name` y `price`)

## Comparación con otras librerías

#### vs `fastapi-filter`

`fastapi-filter` es una librería excelente, pero requiere definir clases de filtro para cada modelo. `fastapi-qengine` adopta un enfoque diferente al permitir que el cliente construya la consulta completa en un solo objeto JSON (o vía parámetros anidados), reduciendo la configuración en el backend.

#### vs `fastapi-querybuilder`

`fastapi-qengine` comparte el objetivo de `fastapi-querybuilder` de facilitar la creación de consultas complejas. La principal diferencia es que `fastapi-qengine` está diseñado específicamente para Beanie en su versión inicial y sigue de cerca la especificación de filtros de Loopback 4, una solución probada y robusta en el ecosistema de Node.js.

## Contribuciones

Las contribuciones son bienvenidas. Por favor, abre un issue o un pull request para discutir cualquier cambio.

### Desarrollo

Para contribuir al proyecto:

```bash
# Clonar el repositorio
git clone https://github.com/urielcuriel/fastapi-qengine.git
cd fastapi-qengine

# Instalar dependencias de desarrollo
uv pip install -e ".[dev]"

# Ejecutar tests
uv run pytest

# Ejecutar tests con cobertura
uv run pytest --cov=fastapi_qengine --cov-report=html
```

Ver [DEVELOPMENT.md](DEVELOPMENT.md) para más detalles sobre desarrollo y testing.

### Testing

El proyecto utiliza pytest para testing:

- **66 tests** cubriendo toda la funcionalidad
- **78% de cobertura** de código
- Tests unitarios, de integración y end-to-end
- Validación de seguridad y manejo de errores

```bash
# Ejecutar todos los tests
uv run pytest

# Tests específicos
uv run pytest tests/test_basic.py
uv run pytest -k "parser"

# Con cobertura detallada
uv run pytest --cov=fastapi_qengine --cov-report=html
```

## Licencia

Este proyecto está bajo la Licencia MIT.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "fastapi-qengine",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "fastapi, beanie, mongodb, query, filter, engine",
    "author": null,
    "author_email": "Uriel Curiel <kratoz.00616@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/24/64/5351f097874422445c5bbd8e5c060ccb9010bff0287f146504778425f947/fastapi_qengine-0.2.0.tar.gz",
    "platform": null,
    "description": "# fastapi-qengine\r\n\r\n[![PyPI version](https://badge.fury.io/py/fastapi-qengine.svg)](https://badge.fury.io/py/fastapi-qengine)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\nUn motor de consultas avanzado para FastAPI, inspirado en el poderoso sistema de filtros de Loopback 4. Dise\u00f1ado inicialmente para Beanie/PyMongo, con planes de expansi\u00f3n a otros ORMs en el futuro.\r\n\r\n`fastapi-qengine` te permite construir consultas complejas para tus modelos directamente desde la URL con una sintaxis flexible, ofreciendo una alternativa m\u00e1s potente y con menos configuraci\u00f3n que `fastapi-filter`.\r\n\r\n## Motivaci\u00f3n\r\n\r\nMientras que librer\u00edas como `fastapi-filter` son excelentes, a menudo requieren una configuraci\u00f3n detallada por cada modelo y campo. `fastapi-qengine` trae la flexibilidad del sistema de filtros de Loopback 4 al ecosistema de FastAPI, permitiendo a los clientes construir consultas complejas con operadores l\u00f3gicos, selecci\u00f3n de campos y ordenamiento desde la URL.\r\n\r\nEste proyecto se enfoca \u00fanicamente en la **creaci\u00f3n de la consulta**, delegando la paginaci\u00f3n a librer\u00edas especializadas como [fastapi-pagination](https://github.com/uriyyo/fastapi-pagination).\r\n\r\n## Caracter\u00edsticas\r\n\r\n- **Sintaxis de filtro flexible:** Soporte para JSON anidado en los par\u00e1metros de la URL y para JSON completo en formato string.\r\n- **Operadores de consulta avanzados:** Soporte para operadores como `$gt`, `$gte`, `$in`, `$nin`, `$lt`, `$lte`, `$ne`, y m\u00e1s.\r\n- **Combinaciones l\u00f3gicas:** Soporte completo para consultas `$and` y `$or`.\r\n- **Selecci\u00f3n de campos (Proyecci\u00f3n):** Elige qu\u00e9 campos devolver en los resultados con `fields`.\r\n- **Ordenamiento din\u00e1mico:** Ordena los resultados con `order`.\r\n- **Integraci\u00f3n m\u00ednima:** Dise\u00f1ado para funcionar con Beanie y FastAPI con una configuraci\u00f3n m\u00ednima.\r\n- **Enfocado en consultas:** No se encarga de la paginaci\u00f3n, permitiendo la integraci\u00f3n con librer\u00edas dedicadas.\r\n\r\n## Instalaci\u00f3n\r\n\r\n```bash\r\npip install fastapi-qengine\r\npip install fastapi-pagination # Recomendado para la paginaci\u00f3n\r\n```\r\n\r\n## Ejemplo R\u00e1pido\r\n\r\n### 1. Define tu modelo Beanie\r\n\r\n```python\r\n# main.py\r\nfrom beanie import Document\r\nfrom pymongo import AsyncMongoClient\r\n\r\nclass Product(Document):\r\n    name: str\r\n    category: str\r\n    price: float\r\n    in_stock: bool\r\n\r\n    class Settings:\r\n        name = \"products\"\r\n\r\nasync def init_db():\r\n    client = AsyncMongoClient(\"mongodb://localhost:27017\")\r\n    await Document.init_all(database=client.db_name, documents=[Product])\r\n```\r\n\r\n### 2. Crea tu endpoint de FastAPI\r\n\r\n```python\r\n# main.py\r\nfrom fastapi import FastAPI, Depends\r\nfrom fastapi_pagination import Page, add_pagination\r\nfrom fastapi_pagination.ext.beanie import apaginate\r\nfrom contextlib import asynccontextmanager\r\n\r\nfrom fastapi_qengine import create_qe_dependency\r\nfrom fastapi_qengine.backends.beanie import BeanieQueryEngine\r\n\r\napp = FastAPI()\r\n\r\n\r\n@asynccontextmanager\r\nasync def lifespan(app: FastAPI):\r\n    # Startup\r\n    await init_db()\r\n    yield\r\n\r\napp = FastAPI(lifespan=lifespan)\r\n\r\n# Engine expl\u00edcito por backend\r\nbeanie_engine = BeanieQueryEngine(Product)\r\nqe_dep = create_qe_dependency(beanie_engine)\r\n\r\n@app.get(\"/products\", response_model=Page[Product])\r\nasync def get_products(q = Depends(qe_dep)):\r\n    # q es una tupla (query, projection_model, sort) lista para apaginate\r\n    query, projection_model, sort = q\r\n    return await apaginate(query, projection_model=projection_model, sort=sort)\r\n\r\nadd_pagination(app)\r\n```\r\n\r\n### 3. Realiza consultas desde la URL\r\n\r\n`fastapi-qengine` soporta dos formatos para pasar el filtro, d\u00e1ndote flexibilidad seg\u00fan la complejidad de la consulta.\r\n\r\n#### Formato 1: Par\u00e1metros de URL anidados (Ideal para consultas simples)\r\n\r\nPara consultas directas o para usar desde un navegador, puedes usar la sintaxis de corchetes. Es m\u00e1s legible para filtros sencillos.\r\n\r\n*   **Buscar productos con un precio mayor a 50:**\r\n    `/products?filter[where][price][$gt]=50`\r\n\r\n*   **Buscar productos en stock de la categor\u00eda \"electronics\":**\r\n    `/products?filter[where][category]=electronics&filter[where][in_stock]=true`\r\n\r\n*   **Buscar productos y ordenarlos por precio (descendente):**\r\n    `/products?filter[where][in_stock]=true&filter[order]=-price`\r\n\r\n#### Formato 2: Stringified JSON (Recomendado para consultas complejas)\r\n\r\nPara consultas que involucran operadores l\u00f3gicos como `$or`, `$and` o estructuras anidadas complejas, el formato de JSON como string es la mejor opci\u00f3n. Recuerda codificar el JSON para la URL.\r\n\r\n*   **Buscar productos en la categor\u00eda \"electronics\" O que cuesten menos de 20:**\r\n    *   **JSON Filter:** `{\"where\": {\"$or\": [{\"category\": \"electronics\"}, {\"price\": {\"$lt\": 20}}]}}`\r\n    *   **URL Codificada:** `/products?filter=%7B%22where%22%3A%20%7B%22%24or%22%3A%20%5B%7B%22category%22%3A%20%22electronics%22%7D%2C%20%7B%22price%22%3A%20%7B%22%24lt%22%3A%2020%7D%7D%5D%7D%7D`\r\n\r\n## Sintaxis de Filtro Soportada\r\n\r\nEl objeto `filter` acepta las siguientes claves, inspiradas en la especificaci\u00f3n de Loopback.\r\n\r\n### `where`\r\n\r\nUn objeto que define las condiciones de b\u00fasqueda. Utiliza la sintaxis de operadores de PyMongo.\r\n\r\n- `{\"where\": {\"category\": \"books\"}}`\r\n- `{\"where\": {\"price\": {\"$gte\": 10, \"$lte\": 50}}}`\r\n- `{\"where\": {\"category\": {\"$in\": [\"electronics\", \"appliances\"]}}}`\r\n- `{\"where\": {\"$and\": [{\"in_stock\": true}, {\"price\": {\"$lt\": 100}}]}}`\r\n\r\n### `order`\r\n\r\nUna cadena de texto para ordenar los resultados. Usa el prefijo `-` para orden descendente.\r\n\r\n- `{\"order\": \"price\"}` (ascendente)\r\n- `{\"order\": \"-price\"}` (descendente)\r\n\r\n### `fields`\r\n\r\nUn objeto para especificar qu\u00e9 campos incluir (proyecci\u00f3n).\r\n\r\n- `{\"fields\": {\"name\": 1, \"price\": 1}}` (incluye solo `name` y `price`)\r\n\r\n## Comparaci\u00f3n con otras librer\u00edas\r\n\r\n#### vs `fastapi-filter`\r\n\r\n`fastapi-filter` es una librer\u00eda excelente, pero requiere definir clases de filtro para cada modelo. `fastapi-qengine` adopta un enfoque diferente al permitir que el cliente construya la consulta completa en un solo objeto JSON (o v\u00eda par\u00e1metros anidados), reduciendo la configuraci\u00f3n en el backend.\r\n\r\n#### vs `fastapi-querybuilder`\r\n\r\n`fastapi-qengine` comparte el objetivo de `fastapi-querybuilder` de facilitar la creaci\u00f3n de consultas complejas. La principal diferencia es que `fastapi-qengine` est\u00e1 dise\u00f1ado espec\u00edficamente para Beanie en su versi\u00f3n inicial y sigue de cerca la especificaci\u00f3n de filtros de Loopback 4, una soluci\u00f3n probada y robusta en el ecosistema de Node.js.\r\n\r\n## Contribuciones\r\n\r\nLas contribuciones son bienvenidas. Por favor, abre un issue o un pull request para discutir cualquier cambio.\r\n\r\n### Desarrollo\r\n\r\nPara contribuir al proyecto:\r\n\r\n```bash\r\n# Clonar el repositorio\r\ngit clone https://github.com/urielcuriel/fastapi-qengine.git\r\ncd fastapi-qengine\r\n\r\n# Instalar dependencias de desarrollo\r\nuv pip install -e \".[dev]\"\r\n\r\n# Ejecutar tests\r\nuv run pytest\r\n\r\n# Ejecutar tests con cobertura\r\nuv run pytest --cov=fastapi_qengine --cov-report=html\r\n```\r\n\r\nVer [DEVELOPMENT.md](DEVELOPMENT.md) para m\u00e1s detalles sobre desarrollo y testing.\r\n\r\n### Testing\r\n\r\nEl proyecto utiliza pytest para testing:\r\n\r\n- **66 tests** cubriendo toda la funcionalidad\r\n- **78% de cobertura** de c\u00f3digo\r\n- Tests unitarios, de integraci\u00f3n y end-to-end\r\n- Validaci\u00f3n de seguridad y manejo de errores\r\n\r\n```bash\r\n# Ejecutar todos los tests\r\nuv run pytest\r\n\r\n# Tests espec\u00edficos\r\nuv run pytest tests/test_basic.py\r\nuv run pytest -k \"parser\"\r\n\r\n# Con cobertura detallada\r\nuv run pytest --cov=fastapi_qengine --cov-report=html\r\n```\r\n\r\n## Licencia\r\n\r\nEste proyecto est\u00e1 bajo la Licencia MIT.\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "fastapi-qengine is an advanced query engine for FastAPI, inspired by Loopback 4's filtering system. It allows building complex queries directly from URLs with a flexible syntax.",
    "version": "0.2.0",
    "project_urls": null,
    "split_keywords": [
        "fastapi",
        " beanie",
        " mongodb",
        " query",
        " filter",
        " engine"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d2b19912c8db2505de64d815952bb4fec713108d1a458a0a1ded71b44b366b4e",
                "md5": "0e752035b83aacbfcc586509a5c2fa7c",
                "sha256": "e329b34a995eee69d0eacb96e759ec5c39a53c4544f9308ddb13afdff0cca80f"
            },
            "downloads": -1,
            "filename": "fastapi_qengine-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0e752035b83aacbfcc586509a5c2fa7c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 35806,
            "upload_time": "2025-08-28T22:28:00",
            "upload_time_iso_8601": "2025-08-28T22:28:00.139843Z",
            "url": "https://files.pythonhosted.org/packages/d2/b1/9912c8db2505de64d815952bb4fec713108d1a458a0a1ded71b44b366b4e/fastapi_qengine-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "24645351f097874422445c5bbd8e5c060ccb9010bff0287f146504778425f947",
                "md5": "c5691d640c470e393580289923f69c15",
                "sha256": "83d260cdfd01011ae6b9aa69405b0dc8935b42f6f13f05bdcce18d8efb1092b4"
            },
            "downloads": -1,
            "filename": "fastapi_qengine-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "c5691d640c470e393580289923f69c15",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 36591,
            "upload_time": "2025-08-28T22:28:01",
            "upload_time_iso_8601": "2025-08-28T22:28:01.513124Z",
            "url": "https://files.pythonhosted.org/packages/24/64/5351f097874422445c5bbd8e5c060ccb9010bff0287f146504778425f947/fastapi_qengine-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-28 22:28:01",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "fastapi-qengine"
}
        
Elapsed time: 0.98655s