fastapi-qengine


Namefastapi-qengine JSON
Version 0.7.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-10-22 23:39:31
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)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)

**fastapi-qengine** es un motor de consultas avanzado para FastAPI que permite a tus clientes construir consultas complejas directamente desde la URL, sin configuración por modelo. Inspirado en el sistema de filtros de Loopback 4, ofrece una arquitectura limpia basada en AST (Abstract Syntax Tree) para procesar, validar y compilar filtros hacia diferentes backends de base de datos.

## ¿Por qué fastapi-qengine?

En lugar de definir filtros manualmente para cada modelo y campo, **fastapi-qengine** proporciona:

- 🎯 **Zero Configuration**: No necesitas crear clases de filtro por cada modelo
- 🔒 **Seguridad incorporada**: Validación automática y políticas de seguridad configurables
- 🏗️ **Arquitectura basada en AST**: Pipeline robusto de parseo → normalización → validación → optimización → compilación
- 🔌 **Multi-backend**: Actualmente soporta Beanie/PyMongo (SQLAlchemy y otros en desarrollo)
- 📝 **Sintaxis flexible**: Soporta tanto parámetros URL anidados como JSON completo
- 🚀 **Alto rendimiento**: Optimización automática de consultas y caching opcional
- 📚 **Documentación OpenAPI automática**: Integración perfecta con FastAPI

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

## Arquitectura

fastapi-qengine implementa un pipeline de procesamiento en varias etapas:

```
URL/JSON Input → Parser → Normalizer → Validator → AST Builder → Optimizer → Compiler → Backend Query
```

### Componentes Principales

1. **Parser** (`core.parser`): Procesa la entrada desde diferentes formatos (JSON string, params anidados, dict)
2. **Normalizer** (`core.normalizer`): Normaliza la estructura de datos a un formato estándar
3. **Validator** (`core.validator`): Valida la seguridad y estructura de las consultas
4. **AST Builder** (`core.ast`): Construye un árbol de sintaxis abstracta tipado
5. **Optimizer** (`core.optimizer`): Optimiza el AST eliminando redundancias
6. **Compiler** (`core.compiler_base`): Interfaz base para compiladores de backend
7. **Backend Compilers** (`backends/`): Implementaciones específicas (Beanie, etc.)

### Tipos de Nodos AST

- **FieldCondition**: Condiciones sobre campos específicos (`price > 100`)
- **LogicalCondition**: Combinaciones lógicas (`and`, `or`, `nor`)
- **OrderNode**: Especificaciones de ordenamiento
- **FieldsNode**: Proyecciones de campos (selección)

## Características Principales

### 🎯 Sintaxis de Consulta
- **Dos formatos soportados**: Parámetros URL anidados o JSON stringificado
- **Operadores de comparación**: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `regex`, `exists`, `size`, `type`
- **Operadores lógicos**: `and`, `or`, `nor` con anidamiento ilimitado
- **Alias sin **: Acepta tanto `gt` como `gt` para mayor flexibilidad

### 🔒 Seguridad
- **Políticas de seguridad configurables**: Control de campos permitidos/prohibidos
- **Límites configurables**: Máximo de condiciones, profundidad de anidamiento, valores en arrays
- **Validación automática**: Tipos de datos, nombres de campos, estructura de consultas
- **Protección contra inyección**: Validación estricta de operadores y valores

### ⚡ Performance
- **Optimización automática**: Simplificación de operadores lógicos, combinación de rangos, eliminación de redundancias
- **Caching opcional**: Cache de filtros parseados y consultas compiladas
- **Pipeline eficiente**: Procesamiento en múltiples etapas con validación temprana

### 🔌 Integración
- **FastAPI native**: Integración como dependencia de FastAPI
- **OpenAPI automático**: Documentación generada automáticamente en Swagger UI
- **Multi-backend**: Arquitectura extensible para soportar múltiples ORMs
- **Pagination-agnostic**: Compatible con cualquier librería de paginación

## Instalación

```bash
pip install fastapi-qengine
```

### Dependencias Opcionales

Para usar con Beanie/MongoDB:
```bash
pip install fastapi-qengine fastapi beanie pymongo
```

Para desarrollo completo con testing:
```bash
pip install fastapi-qengine[dev]
```

Para paginación (recomendado):
```bash
pip install fastapi-pagination
```

## Uso Rápido

### 1. Configuración Básica

```python
from fastapi import FastAPI, Depends
from beanie import Document, init_beanie
from pymongo import AsyncMongoClient
from fastapi_pagination import Page, add_pagination
from fastapi_pagination.ext.beanie import paginate

from fastapi_qengine import create_qe_dependency, BeanieQueryEngine

# Define tu modelo Beanie
class Product(Document):
    name: str
    category: str
    price: float
    in_stock: bool

    class Settings:
        name = "products"

# Inicializa FastAPI
app = FastAPI()

# Crea el motor de consultas para tu modelo
engine = BeanieQueryEngine(Product)
qe_dep = create_qe_dependency(engine)

# Define tu endpoint
@app.get("/products", response_model=Page[Product])
async def get_products(q = Depends(qe_dep)):
    query, projection_model, sort = q
    return await paginate(query, projection_model=projection_model, sort=sort)

add_pagination(app)
```

### 2. Inicialización de Base de Datos

```python
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Conectar a MongoDB
    client = AsyncMongoClient("mongodb://localhost:27017")
    await init_beanie(database=client.db_name, document_models=[Product])
    yield
    # Cleanup si es necesario

app = FastAPI(lifespan=lifespan)
```

### 3. Realizar Consultas

fastapi-qengine soporta dos formatos para construir consultas, proporcionando flexibilidad según la complejidad.

#### Formato 1: Parámetros URL Anidados
Ideal para consultas simples y uso desde navegadores:

```bash
# Productos con precio mayor a 50
GET /products?filter[where][price][gt]=50

# Productos en stock de categoría "electronics"
GET /products?filter[where][category]=electronics&filter[where][in_stock]=true

# Con ordenamiento descendente por precio
GET /products?filter[where][in_stock]=true&filter[order]=-price

# Selección de campos específicos
GET /products?filter[where][category]=books&filter[fields][name]=1&filter[fields][price]=1
```

#### Formato 2: JSON Stringificado
Recomendado para consultas complejas con operadores lógicos:

```bash
# OR lógico: electronics O precio < 20
GET /products?filter={"where":{"or":[{"category":"electronics"},{"price":{"lt":20}}]}}

# AND con múltiples condiciones
GET /products?filter={"where":{"and":[{"in_stock":true},{"price":{"gte":10,"lte":100}}]}}

# Consulta compleja anidada
GET /products?filter={"where":{"or":[{"category":"electronics","price":{"lt":1000}},{"category":"books","in_stock":true}]},"order":"-price","fields":{"name":1,"price":1,"category":1}}
```

**Nota**: En URLs reales, el JSON debe estar URL-encoded.

## Referencia de Sintaxis

### Estructura del Filtro

El objeto `filter` acepta tres claves principales:

```typescript
{
  "where": {...},      // Condiciones de búsqueda
  "order": "...",      // Ordenamiento
  "fields": {...}      // Proyección de campos
}
```

### Cláusula `where`

Define las condiciones de búsqueda usando operadores de MongoDB/PyMongo.

#### Operadores de Comparación

| Operador | Descripción | Ejemplo |
|----------|-------------|---------|
| `eq` o `=` | Igual a | `{"price": 100}` o `{"price": {"eq": 100}}` |
| `ne` | No igual a | `{"category": {"ne": "books"}}` |
| `gt` | Mayor que | `{"price": {"gt": 50}}` |
| `gte` | Mayor o igual que | `{"price": {"gte": 50}}` |
| `lt` | Menor que | `{"price": {"lt": 100}}` |
| `lte` | Menor o igual que | `{"price": {"lte": 100}}` |
| `in` | En array | `{"category": {"in": ["electronics", "books"]}}` |
| `nin` | No en array | `{"category": {"nin": ["toys"]}}` |
| `regex` | Expresión regular | `{"name": {"regex": "^Product"}}` |
| `exists` | Campo existe | `{"description": {"exists": true}}` |
| `size` | Tamaño de array | `{"tags": {"size": 3}}` |
| `type` | Tipo de campo | `{"price": {"type": "number"}}` |

#### Operadores Lógicos

| Operador | Descripción | Ejemplo |
|----------|-------------|---------|
| `and` | Y lógico | `{"and": [{"price": {"gt": 10}}, {"in_stock": true}]}` |
| `or` | O lógico | `{"or": [{"category": "electronics"}, {"price": {"lt": 20}}]}` |
| `nor` | NOR lógico | `{"nor": [{"category": "toys"}, {"in_stock": false}]}` |

#### Ejemplos de Consultas Complejas

```python
# Rango de valores
{"price": {"gte": 10, "lte": 50}}

# Múltiples condiciones (AND implícito)
{"category": "electronics", "in_stock": true, "price": {"lt": 1000}}

# OR con condiciones anidadas
{"or": [
    {"category": "electronics", "price": {"lt": 500}},
    {"category": "books", "in_stock": true}
]}

# Combinación de AND y OR
{"and": [
    {"in_stock": true},
    {"or": [
        {"category": "electronics"},
        {"price": {"lt": 30}}
    ]}
]}
```

### Cláusula `order`

Especifica el ordenamiento de resultados. Usa `-` como prefijo para orden descendente.

```python
# Ascendente
{"order": "price"}

# Descendente
{"order": "-price"}

# Múltiples campos (como string separado por comas)
{"order": "category,-price"}
```

### Cláusula `fields`

Define qué campos incluir en los resultados (proyección).

```python
# Incluir solo name y price
{"fields": {"name": 1, "price": 1}}

# Excluir campos específicos (usar 0)
{"fields": {"internal_id": 0, "metadata": 0}}
```

## Configuración Avanzada

### Políticas de Seguridad

Controla qué campos y operadores pueden usar tus clientes:

```python
from fastapi_qengine import SecurityPolicy, BeanieQueryEngine, create_qe_dependency

# Define política de seguridad
security_policy = SecurityPolicy(
    allowed_fields=["name", "category", "price", "in_stock"],  # Solo estos campos
    forbidden_fields=["internal_id", "secret_data"],           # Campos prohibidos
    allowed_operators=["eq", "gt", "lt", "in", "and"],    # Operadores permitidos
    max_conditions=10,                                          # Máximo de condiciones
    max_array_size=100,                                         # Tamaño máximo de arrays en in
    max_depth=5                                                 # Profundidad máxima de anidamiento
)

# Aplica al crear el motor
engine = BeanieQueryEngine(Product, security_policy=security_policy)
qe_dep = create_qe_dependency(engine)
```

### Configuración Personalizada

```python
from fastapi_qengine import QEngineConfig
from fastapi_qengine.core import ParserConfig, ValidatorConfig, OptimizerConfig

config = QEngineConfig(
    debug=True,
    parser=ParserConfig(
        max_nesting_depth=8,
        strict_mode=True,
        case_sensitive_operators=False
    ),
    validator=ValidatorConfig(
        validate_types=True,
        validate_operators=True
    ),
    optimizer=OptimizerConfig(
        enabled=True,
        simplify_logical_operators=True,
        remove_redundant_conditions=True,
        max_optimization_passes=3
    )
)

qe_dep = create_qe_dependency(engine, config=config)
```

### Uso del Pipeline Directo

Para casos avanzados, puedes usar el pipeline de procesamiento directamente:

```python
from fastapi_qengine import process_filter_to_ast
from fastapi_qengine.backends import compile_to_mongodb

# Procesa filtro a AST
filter_input = {"where": {"price": {"gt": 50}}, "order": "-price"}
ast = process_filter_to_ast(filter_input, config=config)

# Compila a MongoDB
mongodb_query = compile_to_mongodb(ast)
# Resultado: {"filter": {"price": {"gt": 50}}, "sort": [("price", -1)]}
```

### Proyección Dinámica de Respuestas

Genera modelos de respuesta dinámicos basados en los campos solicitados:

```python
from fastapi_qengine import create_response_model

ProductResponse = create_response_model(Product)

@app.get("/products", response_model=Page[ProductResponse])
async def get_products(q = Depends(qe_dep)):
    query, projection_model, sort = q
    # projection_model es dinámico según los campos solicitados
    return await paginate(query, projection_model=projection_model, sort=sort)
```

## Operadores Personalizados

Extiende la funcionalidad con operadores personalizados:

```python
from fastapi_qengine.operators import register_custom_operator, create_simple_operator

# Operador simple
custom_op = create_simple_operator(
    name="contains",
    compile_func=lambda field, value, backend: {field: {"regex": f".*{value}.*"}}
)
register_custom_operator("contains", custom_op)

# Ahora puedes usar: {"name": {"contains": "Product"}}
```

## Backends Soportados

### Beanie/PyMongo (Actual)

Soporte completo para MongoDB a través de Beanie ODM:

```python
from fastapi_qengine import BeanieQueryEngine

engine = BeanieQueryEngine(YourDocument)
```

### Próximamente

- **SQLAlchemy**: Para bases de datos SQL (PostgreSQL, MySQL, SQLite)
- **Tortoise ORM**: Async ORM para múltiples backends
- **Motor**: Driver async de MongoDB puro

## Integración con FastAPI Pagination

fastapi-qengine está diseñado para trabajar sin problemas con `fastapi-pagination`:

```python
from fastapi_pagination import Page, add_pagination, paginate
from fastapi_pagination.ext.beanie import paginate as beanie_paginate

# Opción 1: Con Beanie
@app.get("/products", response_model=Page[Product])
async def list_products(q = Depends(qe_dep)):
    query, projection, sort = q
    return await beanie_paginate(query, projection_model=projection, sort=sort)

# Opción 2: Paginación manual
from fastapi_pagination import Params

@app.get("/products")
async def list_products(
    q = Depends(qe_dep),
    params: Params = Depends()
):
    query, projection, sort = q
    items = await query.skip(params.offset).limit(params.size).to_list()
    total = await query.count()
    return {"items": items, "total": total, "page": params.page, "size": params.size}

add_pagination(app)
```

## Ejemplos Completos

Consulta la carpeta `examples/` para ver implementaciones completas:

- **`basic.py`**: Ejemplo básico con Beanie
- **`security_policies.py`**: Uso avanzado de políticas de seguridad
- **`with_paginate.py`**: Integración con fastapi-pagination

## Comparación con Alternativas

| Característica | fastapi-qengine | fastapi-filter | Loopback 4 |
|----------------|-----------------|----------------|------------|
| Zero config | ✅ | ❌ | ✅ |
| Sintaxis flexible | ✅ | ⚠️ | ✅ |
| Operadores lógicos anidados | ✅ | ⚠️ | ✅ |
| AST-based | ✅ | ❌ | ✅ |
| Multi-backend | 🔄 | ✅ | ✅ |
| Políticas de seguridad | ✅ | ⚠️ | ✅ |
| Optimización de queries | ✅ | ❌ | ✅ |
| OpenAPI docs | ✅ | ✅ | ✅ |

✅ Soportado completamente | ⚠️ Parcialmente | ❌ No soportado | 🔄 En desarrollo

## Manejo de Errores

fastapi-qengine proporciona errores descriptivos para ayudar en debugging:

```python
from fastapi_qengine.core import QEngineError, ParseError, ValidationError, SecurityError

# Los errores se convierten automáticamente a HTTPException
# ParseError -> 400 Bad Request (JSON inválido o sintaxis incorrecta)
# ValidationError -> 400 Bad Request (estructura inválida)
# SecurityError -> 400 Bad Request (violación de política de seguridad)
```

Ejemplo de respuesta de error:

```json
{
  "detail": "Field 'secret_field' is not allowed by security policy"
}
```

## Testing

El proyecto incluye una suite completa de tests:

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

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

# Tests específicos
uv run pytest tests/test_basic.py
uv run pytest tests/core/test_parser.py -v

# Por palabra clave
uv run pytest -k "security"
```

**Estadísticas de Testing:**
- ✅ 66 tests
- 📊 78% de cobertura de código
- 🔐 Tests de seguridad y validación
- 🧪 Tests unitarios, integración y E2E

## Rendimiento

fastapi-qengine está optimizado para alto rendimiento:

- **Pipeline eficiente**: Validación temprana para fallar rápido
- **Optimización automática**: Simplifica consultas antes de compilar
- **Caching opcional**: Cache de ASTs parseados y consultas compiladas
- **Zero overhead**: Sin reflection en runtime para backends soportados

## Contribuciones

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

### Guía de Desarrollo

```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

# Lint y formato
ruff check fastapi_qengine/
ruff format fastapi_qengine/

# Ver cobertura
uv run pytest --cov=fastapi_qengine --cov-report=html
# Abre htmlcov/index.html en tu navegador
```

Consulta [DEVELOPMENT.md](DEVELOPMENT.md) para más detalles.

## Roadmap

- [x] Soporte completo para Beanie/PyMongo
- [x] Operadores de comparación y lógicos
- [x] Políticas de seguridad configurables
- [x] Optimización de AST
- [x] Documentación OpenAPI automática
- [ ] Backend para SQLAlchemy
- [ ] Backend para Tortoise ORM
- [ ] Soporte para agregaciones
- [ ] Cache de consultas con Redis
- [ ] Métricas y observabilidad

## Recursos

- **Documentación**: [https://github.com/urielcuriel/fastapi-qengine](https://github.com/urielcuriel/fastapi-qengine)
- **PyPI**: [https://pypi.org/project/fastapi-qengine/](https://pypi.org/project/fastapi-qengine/)
- **Issues**: [https://github.com/urielcuriel/fastapi-qengine/issues](https://github.com/urielcuriel/fastapi-qengine/issues)
- **Changelog**: [CHANGELOG.md](CHANGELOG.md)

## Licencia

Este proyecto está bajo la Licencia MIT.

## Agradecimientos

Inspirado por el excelente sistema de filtros de [Loopback 4](https://loopback.io/doc/en/lb4/Querying-data.html), adaptado para el ecosistema Python/FastAPI.

---

**¿Necesitas ayuda?** Abre un [issue](https://github.com/urielcuriel/fastapi-qengine/issues) o inicia una [discusión](https://github.com/urielcuriel/fastapi-qengine/discussions).

**¿Te gusta el proyecto?** Dale una ⭐ en [GitHub](https://github.com/urielcuriel/fastapi-qengine)!

            

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/f5/99/95ba69e9c0cf04378d5f96decef5227bc3f4599deda6b7d0a4b8c166c9ab/fastapi_qengine-0.7.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[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)\r\n\r\n**fastapi-qengine** es un motor de consultas avanzado para FastAPI que permite a tus clientes construir consultas complejas directamente desde la URL, sin configuraci\u00f3n por modelo. Inspirado en el sistema de filtros de Loopback 4, ofrece una arquitectura limpia basada en AST (Abstract Syntax Tree) para procesar, validar y compilar filtros hacia diferentes backends de base de datos.\r\n\r\n## \u00bfPor qu\u00e9 fastapi-qengine?\r\n\r\nEn lugar de definir filtros manualmente para cada modelo y campo, **fastapi-qengine** proporciona:\r\n\r\n- \ud83c\udfaf **Zero Configuration**: No necesitas crear clases de filtro por cada modelo\r\n- \ud83d\udd12 **Seguridad incorporada**: Validaci\u00f3n autom\u00e1tica y pol\u00edticas de seguridad configurables\r\n- \ud83c\udfd7\ufe0f **Arquitectura basada en AST**: Pipeline robusto de parseo \u2192 normalizaci\u00f3n \u2192 validaci\u00f3n \u2192 optimizaci\u00f3n \u2192 compilaci\u00f3n\r\n- \ud83d\udd0c **Multi-backend**: Actualmente soporta Beanie/PyMongo (SQLAlchemy y otros en desarrollo)\r\n- \ud83d\udcdd **Sintaxis flexible**: Soporta tanto par\u00e1metros URL anidados como JSON completo\r\n- \ud83d\ude80 **Alto rendimiento**: Optimizaci\u00f3n autom\u00e1tica de consultas y caching opcional\r\n- \ud83d\udcda **Documentaci\u00f3n OpenAPI autom\u00e1tica**: Integraci\u00f3n perfecta con FastAPI\r\n\r\nEste proyecto se enfoca en la **generaci\u00f3n de consultas**, delegando la paginaci\u00f3n a librer\u00edas especializadas como [fastapi-pagination](https://github.com/uriyyo/fastapi-pagination).\r\n\r\n## Arquitectura\r\n\r\nfastapi-qengine implementa un pipeline de procesamiento en varias etapas:\r\n\r\n```\r\nURL/JSON Input \u2192 Parser \u2192 Normalizer \u2192 Validator \u2192 AST Builder \u2192 Optimizer \u2192 Compiler \u2192 Backend Query\r\n```\r\n\r\n### Componentes Principales\r\n\r\n1. **Parser** (`core.parser`): Procesa la entrada desde diferentes formatos (JSON string, params anidados, dict)\r\n2. **Normalizer** (`core.normalizer`): Normaliza la estructura de datos a un formato est\u00e1ndar\r\n3. **Validator** (`core.validator`): Valida la seguridad y estructura de las consultas\r\n4. **AST Builder** (`core.ast`): Construye un \u00e1rbol de sintaxis abstracta tipado\r\n5. **Optimizer** (`core.optimizer`): Optimiza el AST eliminando redundancias\r\n6. **Compiler** (`core.compiler_base`): Interfaz base para compiladores de backend\r\n7. **Backend Compilers** (`backends/`): Implementaciones espec\u00edficas (Beanie, etc.)\r\n\r\n### Tipos de Nodos AST\r\n\r\n- **FieldCondition**: Condiciones sobre campos espec\u00edficos (`price > 100`)\r\n- **LogicalCondition**: Combinaciones l\u00f3gicas (`and`, `or`, `nor`)\r\n- **OrderNode**: Especificaciones de ordenamiento\r\n- **FieldsNode**: Proyecciones de campos (selecci\u00f3n)\r\n\r\n## Caracter\u00edsticas Principales\r\n\r\n### \ud83c\udfaf Sintaxis de Consulta\r\n- **Dos formatos soportados**: Par\u00e1metros URL anidados o JSON stringificado\r\n- **Operadores de comparaci\u00f3n**: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `regex`, `exists`, `size`, `type`\r\n- **Operadores l\u00f3gicos**: `and`, `or`, `nor` con anidamiento ilimitado\r\n- **Alias sin **: Acepta tanto `gt` como `gt` para mayor flexibilidad\r\n\r\n### \ud83d\udd12 Seguridad\r\n- **Pol\u00edticas de seguridad configurables**: Control de campos permitidos/prohibidos\r\n- **L\u00edmites configurables**: M\u00e1ximo de condiciones, profundidad de anidamiento, valores en arrays\r\n- **Validaci\u00f3n autom\u00e1tica**: Tipos de datos, nombres de campos, estructura de consultas\r\n- **Protecci\u00f3n contra inyecci\u00f3n**: Validaci\u00f3n estricta de operadores y valores\r\n\r\n### \u26a1 Performance\r\n- **Optimizaci\u00f3n autom\u00e1tica**: Simplificaci\u00f3n de operadores l\u00f3gicos, combinaci\u00f3n de rangos, eliminaci\u00f3n de redundancias\r\n- **Caching opcional**: Cache de filtros parseados y consultas compiladas\r\n- **Pipeline eficiente**: Procesamiento en m\u00faltiples etapas con validaci\u00f3n temprana\r\n\r\n### \ud83d\udd0c Integraci\u00f3n\r\n- **FastAPI native**: Integraci\u00f3n como dependencia de FastAPI\r\n- **OpenAPI autom\u00e1tico**: Documentaci\u00f3n generada autom\u00e1ticamente en Swagger UI\r\n- **Multi-backend**: Arquitectura extensible para soportar m\u00faltiples ORMs\r\n- **Pagination-agnostic**: Compatible con cualquier librer\u00eda de paginaci\u00f3n\r\n\r\n## Instalaci\u00f3n\r\n\r\n```bash\r\npip install fastapi-qengine\r\n```\r\n\r\n### Dependencias Opcionales\r\n\r\nPara usar con Beanie/MongoDB:\r\n```bash\r\npip install fastapi-qengine fastapi beanie pymongo\r\n```\r\n\r\nPara desarrollo completo con testing:\r\n```bash\r\npip install fastapi-qengine[dev]\r\n```\r\n\r\nPara paginaci\u00f3n (recomendado):\r\n```bash\r\npip install fastapi-pagination\r\n```\r\n\r\n## Uso R\u00e1pido\r\n\r\n### 1. Configuraci\u00f3n B\u00e1sica\r\n\r\n```python\r\nfrom fastapi import FastAPI, Depends\r\nfrom beanie import Document, init_beanie\r\nfrom pymongo import AsyncMongoClient\r\nfrom fastapi_pagination import Page, add_pagination\r\nfrom fastapi_pagination.ext.beanie import paginate\r\n\r\nfrom fastapi_qengine import create_qe_dependency, BeanieQueryEngine\r\n\r\n# Define tu modelo Beanie\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\n# Inicializa FastAPI\r\napp = FastAPI()\r\n\r\n# Crea el motor de consultas para tu modelo\r\nengine = BeanieQueryEngine(Product)\r\nqe_dep = create_qe_dependency(engine)\r\n\r\n# Define tu endpoint\r\n@app.get(\"/products\", response_model=Page[Product])\r\nasync def get_products(q = Depends(qe_dep)):\r\n    query, projection_model, sort = q\r\n    return await paginate(query, projection_model=projection_model, sort=sort)\r\n\r\nadd_pagination(app)\r\n```\r\n\r\n### 2. Inicializaci\u00f3n de Base de Datos\r\n\r\n```python\r\nfrom contextlib import asynccontextmanager\r\n\r\n@asynccontextmanager\r\nasync def lifespan(app: FastAPI):\r\n    # Conectar a MongoDB\r\n    client = AsyncMongoClient(\"mongodb://localhost:27017\")\r\n    await init_beanie(database=client.db_name, document_models=[Product])\r\n    yield\r\n    # Cleanup si es necesario\r\n\r\napp = FastAPI(lifespan=lifespan)\r\n```\r\n\r\n### 3. Realizar Consultas\r\n\r\nfastapi-qengine soporta dos formatos para construir consultas, proporcionando flexibilidad seg\u00fan la complejidad.\r\n\r\n#### Formato 1: Par\u00e1metros URL Anidados\r\nIdeal para consultas simples y uso desde navegadores:\r\n\r\n```bash\r\n# Productos con precio mayor a 50\r\nGET /products?filter[where][price][gt]=50\r\n\r\n# Productos en stock de categor\u00eda \"electronics\"\r\nGET /products?filter[where][category]=electronics&filter[where][in_stock]=true\r\n\r\n# Con ordenamiento descendente por precio\r\nGET /products?filter[where][in_stock]=true&filter[order]=-price\r\n\r\n# Selecci\u00f3n de campos espec\u00edficos\r\nGET /products?filter[where][category]=books&filter[fields][name]=1&filter[fields][price]=1\r\n```\r\n\r\n#### Formato 2: JSON Stringificado\r\nRecomendado para consultas complejas con operadores l\u00f3gicos:\r\n\r\n```bash\r\n# OR l\u00f3gico: electronics O precio < 20\r\nGET /products?filter={\"where\":{\"or\":[{\"category\":\"electronics\"},{\"price\":{\"lt\":20}}]}}\r\n\r\n# AND con m\u00faltiples condiciones\r\nGET /products?filter={\"where\":{\"and\":[{\"in_stock\":true},{\"price\":{\"gte\":10,\"lte\":100}}]}}\r\n\r\n# Consulta compleja anidada\r\nGET /products?filter={\"where\":{\"or\":[{\"category\":\"electronics\",\"price\":{\"lt\":1000}},{\"category\":\"books\",\"in_stock\":true}]},\"order\":\"-price\",\"fields\":{\"name\":1,\"price\":1,\"category\":1}}\r\n```\r\n\r\n**Nota**: En URLs reales, el JSON debe estar URL-encoded.\r\n\r\n## Referencia de Sintaxis\r\n\r\n### Estructura del Filtro\r\n\r\nEl objeto `filter` acepta tres claves principales:\r\n\r\n```typescript\r\n{\r\n  \"where\": {...},      // Condiciones de b\u00fasqueda\r\n  \"order\": \"...\",      // Ordenamiento\r\n  \"fields\": {...}      // Proyecci\u00f3n de campos\r\n}\r\n```\r\n\r\n### Cl\u00e1usula `where`\r\n\r\nDefine las condiciones de b\u00fasqueda usando operadores de MongoDB/PyMongo.\r\n\r\n#### Operadores de Comparaci\u00f3n\r\n\r\n| Operador | Descripci\u00f3n | Ejemplo |\r\n|----------|-------------|---------|\r\n| `eq` o `=` | Igual a | `{\"price\": 100}` o `{\"price\": {\"eq\": 100}}` |\r\n| `ne` | No igual a | `{\"category\": {\"ne\": \"books\"}}` |\r\n| `gt` | Mayor que | `{\"price\": {\"gt\": 50}}` |\r\n| `gte` | Mayor o igual que | `{\"price\": {\"gte\": 50}}` |\r\n| `lt` | Menor que | `{\"price\": {\"lt\": 100}}` |\r\n| `lte` | Menor o igual que | `{\"price\": {\"lte\": 100}}` |\r\n| `in` | En array | `{\"category\": {\"in\": [\"electronics\", \"books\"]}}` |\r\n| `nin` | No en array | `{\"category\": {\"nin\": [\"toys\"]}}` |\r\n| `regex` | Expresi\u00f3n regular | `{\"name\": {\"regex\": \"^Product\"}}` |\r\n| `exists` | Campo existe | `{\"description\": {\"exists\": true}}` |\r\n| `size` | Tama\u00f1o de array | `{\"tags\": {\"size\": 3}}` |\r\n| `type` | Tipo de campo | `{\"price\": {\"type\": \"number\"}}` |\r\n\r\n#### Operadores L\u00f3gicos\r\n\r\n| Operador | Descripci\u00f3n | Ejemplo |\r\n|----------|-------------|---------|\r\n| `and` | Y l\u00f3gico | `{\"and\": [{\"price\": {\"gt\": 10}}, {\"in_stock\": true}]}` |\r\n| `or` | O l\u00f3gico | `{\"or\": [{\"category\": \"electronics\"}, {\"price\": {\"lt\": 20}}]}` |\r\n| `nor` | NOR l\u00f3gico | `{\"nor\": [{\"category\": \"toys\"}, {\"in_stock\": false}]}` |\r\n\r\n#### Ejemplos de Consultas Complejas\r\n\r\n```python\r\n# Rango de valores\r\n{\"price\": {\"gte\": 10, \"lte\": 50}}\r\n\r\n# M\u00faltiples condiciones (AND impl\u00edcito)\r\n{\"category\": \"electronics\", \"in_stock\": true, \"price\": {\"lt\": 1000}}\r\n\r\n# OR con condiciones anidadas\r\n{\"or\": [\r\n    {\"category\": \"electronics\", \"price\": {\"lt\": 500}},\r\n    {\"category\": \"books\", \"in_stock\": true}\r\n]}\r\n\r\n# Combinaci\u00f3n de AND y OR\r\n{\"and\": [\r\n    {\"in_stock\": true},\r\n    {\"or\": [\r\n        {\"category\": \"electronics\"},\r\n        {\"price\": {\"lt\": 30}}\r\n    ]}\r\n]}\r\n```\r\n\r\n### Cl\u00e1usula `order`\r\n\r\nEspecifica el ordenamiento de resultados. Usa `-` como prefijo para orden descendente.\r\n\r\n```python\r\n# Ascendente\r\n{\"order\": \"price\"}\r\n\r\n# Descendente\r\n{\"order\": \"-price\"}\r\n\r\n# M\u00faltiples campos (como string separado por comas)\r\n{\"order\": \"category,-price\"}\r\n```\r\n\r\n### Cl\u00e1usula `fields`\r\n\r\nDefine qu\u00e9 campos incluir en los resultados (proyecci\u00f3n).\r\n\r\n```python\r\n# Incluir solo name y price\r\n{\"fields\": {\"name\": 1, \"price\": 1}}\r\n\r\n# Excluir campos espec\u00edficos (usar 0)\r\n{\"fields\": {\"internal_id\": 0, \"metadata\": 0}}\r\n```\r\n\r\n## Configuraci\u00f3n Avanzada\r\n\r\n### Pol\u00edticas de Seguridad\r\n\r\nControla qu\u00e9 campos y operadores pueden usar tus clientes:\r\n\r\n```python\r\nfrom fastapi_qengine import SecurityPolicy, BeanieQueryEngine, create_qe_dependency\r\n\r\n# Define pol\u00edtica de seguridad\r\nsecurity_policy = SecurityPolicy(\r\n    allowed_fields=[\"name\", \"category\", \"price\", \"in_stock\"],  # Solo estos campos\r\n    forbidden_fields=[\"internal_id\", \"secret_data\"],           # Campos prohibidos\r\n    allowed_operators=[\"eq\", \"gt\", \"lt\", \"in\", \"and\"],    # Operadores permitidos\r\n    max_conditions=10,                                          # M\u00e1ximo de condiciones\r\n    max_array_size=100,                                         # Tama\u00f1o m\u00e1ximo de arrays en in\r\n    max_depth=5                                                 # Profundidad m\u00e1xima de anidamiento\r\n)\r\n\r\n# Aplica al crear el motor\r\nengine = BeanieQueryEngine(Product, security_policy=security_policy)\r\nqe_dep = create_qe_dependency(engine)\r\n```\r\n\r\n### Configuraci\u00f3n Personalizada\r\n\r\n```python\r\nfrom fastapi_qengine import QEngineConfig\r\nfrom fastapi_qengine.core import ParserConfig, ValidatorConfig, OptimizerConfig\r\n\r\nconfig = QEngineConfig(\r\n    debug=True,\r\n    parser=ParserConfig(\r\n        max_nesting_depth=8,\r\n        strict_mode=True,\r\n        case_sensitive_operators=False\r\n    ),\r\n    validator=ValidatorConfig(\r\n        validate_types=True,\r\n        validate_operators=True\r\n    ),\r\n    optimizer=OptimizerConfig(\r\n        enabled=True,\r\n        simplify_logical_operators=True,\r\n        remove_redundant_conditions=True,\r\n        max_optimization_passes=3\r\n    )\r\n)\r\n\r\nqe_dep = create_qe_dependency(engine, config=config)\r\n```\r\n\r\n### Uso del Pipeline Directo\r\n\r\nPara casos avanzados, puedes usar el pipeline de procesamiento directamente:\r\n\r\n```python\r\nfrom fastapi_qengine import process_filter_to_ast\r\nfrom fastapi_qengine.backends import compile_to_mongodb\r\n\r\n# Procesa filtro a AST\r\nfilter_input = {\"where\": {\"price\": {\"gt\": 50}}, \"order\": \"-price\"}\r\nast = process_filter_to_ast(filter_input, config=config)\r\n\r\n# Compila a MongoDB\r\nmongodb_query = compile_to_mongodb(ast)\r\n# Resultado: {\"filter\": {\"price\": {\"gt\": 50}}, \"sort\": [(\"price\", -1)]}\r\n```\r\n\r\n### Proyecci\u00f3n Din\u00e1mica de Respuestas\r\n\r\nGenera modelos de respuesta din\u00e1micos basados en los campos solicitados:\r\n\r\n```python\r\nfrom fastapi_qengine import create_response_model\r\n\r\nProductResponse = create_response_model(Product)\r\n\r\n@app.get(\"/products\", response_model=Page[ProductResponse])\r\nasync def get_products(q = Depends(qe_dep)):\r\n    query, projection_model, sort = q\r\n    # projection_model es din\u00e1mico seg\u00fan los campos solicitados\r\n    return await paginate(query, projection_model=projection_model, sort=sort)\r\n```\r\n\r\n## Operadores Personalizados\r\n\r\nExtiende la funcionalidad con operadores personalizados:\r\n\r\n```python\r\nfrom fastapi_qengine.operators import register_custom_operator, create_simple_operator\r\n\r\n# Operador simple\r\ncustom_op = create_simple_operator(\r\n    name=\"contains\",\r\n    compile_func=lambda field, value, backend: {field: {\"regex\": f\".*{value}.*\"}}\r\n)\r\nregister_custom_operator(\"contains\", custom_op)\r\n\r\n# Ahora puedes usar: {\"name\": {\"contains\": \"Product\"}}\r\n```\r\n\r\n## Backends Soportados\r\n\r\n### Beanie/PyMongo (Actual)\r\n\r\nSoporte completo para MongoDB a trav\u00e9s de Beanie ODM:\r\n\r\n```python\r\nfrom fastapi_qengine import BeanieQueryEngine\r\n\r\nengine = BeanieQueryEngine(YourDocument)\r\n```\r\n\r\n### Pr\u00f3ximamente\r\n\r\n- **SQLAlchemy**: Para bases de datos SQL (PostgreSQL, MySQL, SQLite)\r\n- **Tortoise ORM**: Async ORM para m\u00faltiples backends\r\n- **Motor**: Driver async de MongoDB puro\r\n\r\n## Integraci\u00f3n con FastAPI Pagination\r\n\r\nfastapi-qengine est\u00e1 dise\u00f1ado para trabajar sin problemas con `fastapi-pagination`:\r\n\r\n```python\r\nfrom fastapi_pagination import Page, add_pagination, paginate\r\nfrom fastapi_pagination.ext.beanie import paginate as beanie_paginate\r\n\r\n# Opci\u00f3n 1: Con Beanie\r\n@app.get(\"/products\", response_model=Page[Product])\r\nasync def list_products(q = Depends(qe_dep)):\r\n    query, projection, sort = q\r\n    return await beanie_paginate(query, projection_model=projection, sort=sort)\r\n\r\n# Opci\u00f3n 2: Paginaci\u00f3n manual\r\nfrom fastapi_pagination import Params\r\n\r\n@app.get(\"/products\")\r\nasync def list_products(\r\n    q = Depends(qe_dep),\r\n    params: Params = Depends()\r\n):\r\n    query, projection, sort = q\r\n    items = await query.skip(params.offset).limit(params.size).to_list()\r\n    total = await query.count()\r\n    return {\"items\": items, \"total\": total, \"page\": params.page, \"size\": params.size}\r\n\r\nadd_pagination(app)\r\n```\r\n\r\n## Ejemplos Completos\r\n\r\nConsulta la carpeta `examples/` para ver implementaciones completas:\r\n\r\n- **`basic.py`**: Ejemplo b\u00e1sico con Beanie\r\n- **`security_policies.py`**: Uso avanzado de pol\u00edticas de seguridad\r\n- **`with_paginate.py`**: Integraci\u00f3n con fastapi-pagination\r\n\r\n## Comparaci\u00f3n con Alternativas\r\n\r\n| Caracter\u00edstica | fastapi-qengine | fastapi-filter | Loopback 4 |\r\n|----------------|-----------------|----------------|------------|\r\n| Zero config | \u2705 | \u274c | \u2705 |\r\n| Sintaxis flexible | \u2705 | \u26a0\ufe0f | \u2705 |\r\n| Operadores l\u00f3gicos anidados | \u2705 | \u26a0\ufe0f | \u2705 |\r\n| AST-based | \u2705 | \u274c | \u2705 |\r\n| Multi-backend | \ud83d\udd04 | \u2705 | \u2705 |\r\n| Pol\u00edticas de seguridad | \u2705 | \u26a0\ufe0f | \u2705 |\r\n| Optimizaci\u00f3n de queries | \u2705 | \u274c | \u2705 |\r\n| OpenAPI docs | \u2705 | \u2705 | \u2705 |\r\n\r\n\u2705 Soportado completamente | \u26a0\ufe0f Parcialmente | \u274c No soportado | \ud83d\udd04 En desarrollo\r\n\r\n## Manejo de Errores\r\n\r\nfastapi-qengine proporciona errores descriptivos para ayudar en debugging:\r\n\r\n```python\r\nfrom fastapi_qengine.core import QEngineError, ParseError, ValidationError, SecurityError\r\n\r\n# Los errores se convierten autom\u00e1ticamente a HTTPException\r\n# ParseError -> 400 Bad Request (JSON inv\u00e1lido o sintaxis incorrecta)\r\n# ValidationError -> 400 Bad Request (estructura inv\u00e1lida)\r\n# SecurityError -> 400 Bad Request (violaci\u00f3n de pol\u00edtica de seguridad)\r\n```\r\n\r\nEjemplo de respuesta de error:\r\n\r\n```json\r\n{\r\n  \"detail\": \"Field 'secret_field' is not allowed by security policy\"\r\n}\r\n```\r\n\r\n## Testing\r\n\r\nEl proyecto incluye una suite completa de tests:\r\n\r\n```bash\r\n# Ejecutar todos los tests\r\nuv run pytest\r\n\r\n# Con cobertura\r\nuv run pytest --cov=fastapi_qengine --cov-report=html\r\n\r\n# Tests espec\u00edficos\r\nuv run pytest tests/test_basic.py\r\nuv run pytest tests/core/test_parser.py -v\r\n\r\n# Por palabra clave\r\nuv run pytest -k \"security\"\r\n```\r\n\r\n**Estad\u00edsticas de Testing:**\r\n- \u2705 66 tests\r\n- \ud83d\udcca 78% de cobertura de c\u00f3digo\r\n- \ud83d\udd10 Tests de seguridad y validaci\u00f3n\r\n- \ud83e\uddea Tests unitarios, integraci\u00f3n y E2E\r\n\r\n## Rendimiento\r\n\r\nfastapi-qengine est\u00e1 optimizado para alto rendimiento:\r\n\r\n- **Pipeline eficiente**: Validaci\u00f3n temprana para fallar r\u00e1pido\r\n- **Optimizaci\u00f3n autom\u00e1tica**: Simplifica consultas antes de compilar\r\n- **Caching opcional**: Cache de ASTs parseados y consultas compiladas\r\n- **Zero overhead**: Sin reflection en runtime para backends soportados\r\n\r\n## Contribuciones\r\n\r\nLas contribuciones son bienvenidas. Por favor, abre un issue o pull request para discutir cambios.\r\n\r\n### Gu\u00eda de Desarrollo\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# Lint y formato\r\nruff check fastapi_qengine/\r\nruff format fastapi_qengine/\r\n\r\n# Ver cobertura\r\nuv run pytest --cov=fastapi_qengine --cov-report=html\r\n# Abre htmlcov/index.html en tu navegador\r\n```\r\n\r\nConsulta [DEVELOPMENT.md](DEVELOPMENT.md) para m\u00e1s detalles.\r\n\r\n## Roadmap\r\n\r\n- [x] Soporte completo para Beanie/PyMongo\r\n- [x] Operadores de comparaci\u00f3n y l\u00f3gicos\r\n- [x] Pol\u00edticas de seguridad configurables\r\n- [x] Optimizaci\u00f3n de AST\r\n- [x] Documentaci\u00f3n OpenAPI autom\u00e1tica\r\n- [ ] Backend para SQLAlchemy\r\n- [ ] Backend para Tortoise ORM\r\n- [ ] Soporte para agregaciones\r\n- [ ] Cache de consultas con Redis\r\n- [ ] M\u00e9tricas y observabilidad\r\n\r\n## Recursos\r\n\r\n- **Documentaci\u00f3n**: [https://github.com/urielcuriel/fastapi-qengine](https://github.com/urielcuriel/fastapi-qengine)\r\n- **PyPI**: [https://pypi.org/project/fastapi-qengine/](https://pypi.org/project/fastapi-qengine/)\r\n- **Issues**: [https://github.com/urielcuriel/fastapi-qengine/issues](https://github.com/urielcuriel/fastapi-qengine/issues)\r\n- **Changelog**: [CHANGELOG.md](CHANGELOG.md)\r\n\r\n## Licencia\r\n\r\nEste proyecto est\u00e1 bajo la Licencia MIT.\r\n\r\n## Agradecimientos\r\n\r\nInspirado por el excelente sistema de filtros de [Loopback 4](https://loopback.io/doc/en/lb4/Querying-data.html), adaptado para el ecosistema Python/FastAPI.\r\n\r\n---\r\n\r\n**\u00bfNecesitas ayuda?** Abre un [issue](https://github.com/urielcuriel/fastapi-qengine/issues) o inicia una [discusi\u00f3n](https://github.com/urielcuriel/fastapi-qengine/discussions).\r\n\r\n**\u00bfTe gusta el proyecto?** Dale una \u2b50 en [GitHub](https://github.com/urielcuriel/fastapi-qengine)!\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.7.0",
    "project_urls": {
        "Repository": "https://github.com/urielcuriel/fastapi-qengine"
    },
    "split_keywords": [
        "fastapi",
        " beanie",
        " mongodb",
        " query",
        " filter",
        " engine"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8e63021da9baf6304a374e6ffeb6439d78da761fe1688c8e1a9e42382baa45a7",
                "md5": "35cf1289b3a857c6537c1a07f3b03f3d",
                "sha256": "ba336063ff7afc2ee9caff1223946c4fa9d2462a755e424b0dbbf9559585e68e"
            },
            "downloads": -1,
            "filename": "fastapi_qengine-0.7.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "35cf1289b3a857c6537c1a07f3b03f3d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 48695,
            "upload_time": "2025-10-22T23:39:30",
            "upload_time_iso_8601": "2025-10-22T23:39:30.552178Z",
            "url": "https://files.pythonhosted.org/packages/8e/63/021da9baf6304a374e6ffeb6439d78da761fe1688c8e1a9e42382baa45a7/fastapi_qengine-0.7.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f59995ba69e9c0cf04378d5f96decef5227bc3f4599deda6b7d0a4b8c166c9ab",
                "md5": "d783cd303e1e00bbaa1cc6eedef69884",
                "sha256": "af8144474d22169caebd0188ff1f1c89ed5d0cfe209d507542f8e25d698d13cb"
            },
            "downloads": -1,
            "filename": "fastapi_qengine-0.7.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d783cd303e1e00bbaa1cc6eedef69884",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 48732,
            "upload_time": "2025-10-22T23:39:31",
            "upload_time_iso_8601": "2025-10-22T23:39:31.716968Z",
            "url": "https://files.pythonhosted.org/packages/f5/99/95ba69e9c0cf04378d5f96decef5227bc3f4599deda6b7d0a4b8c166c9ab/fastapi_qengine-0.7.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-22 23:39:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "urielcuriel",
    "github_project": "fastapi-qengine",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "fastapi-qengine"
}
        
Elapsed time: 3.19930s