# Fractal Projections

> A comprehensive projection system for defining how data should be shaped, aggregated, grouped, ordered, and limited in database queries.

[![PyPI Version][pypi-image]][pypi-url]
[![Build Status][build-image]][build-url]

<!-- Badges -->

[pypi-image]: https://img.shields.io/pypi/v/fractal-projections
[pypi-url]: https://pypi.org/project/fractal-projections/
[build-image]: https://github.com/Fractal-Forge/fractal-projections/actions/workflows/build.yml/badge.svg
[build-url]: https://github.com/Fractal-Forge/fractal-projections/actions/workflows/build.yml

This library complements [fractal-specifications](https://pypi.org/project/fractal-specifications/) (which handles filtering) by providing database-agnostic data shaping capabilities.

## Features

- **Database-Agnostic**: Write projections once, use them across different databases
- **Type-Safe**: Fully typed Python API for better IDE support and fewer errors
- **Flexible**: Support for field selection, aggregations, grouping, ordering, and limiting
- **Multi-Database Support**: Built-in builders for:
  - PostgreSQL
  - MongoDB
  - Firestore
  - Elasticsearch

## Installation

```bash
pip install fractal-projections
```

For specific database support:

```bash
# PostgreSQL
pip install fractal-projections[postgres]

# MongoDB
pip install fractal-projections[mongo]

# Firestore
pip install fractal-projections[firestore]

# Elasticsearch
pip install fractal-projections[elasticsearch]

# All databases
pip install fractal-projections[all]
```

## Quick Start

```python
from fractal_projections import (
    QueryProjection,
    FieldProjection,
    ProjectionList,
    OrderingProjection,
    LimitProjection,
)
from fractal_projections.builders import PostgresProjectionBuilder
from fractal_specifications.generic.operators import EqualsSpecification

# Define a database-agnostic projection
query = QueryProjection(
    filter=EqualsSpecification("status", "active"),
    projection=ProjectionList([
        FieldProjection("id"),
        FieldProjection("name"),
        FieldProjection("created_at"),
    ]),
    ordering=OrderingProjection("created_at", descending=True),
    limit=LimitProjection(10),
)

# Convert to database-specific query
builder = PostgresProjectionBuilder("users")
sql, params = builder.build(query)
print(sql)
# SELECT id, name, created_at FROM users WHERE status = $1 ORDER BY created_at DESC LIMIT 10
```

## Core Concepts

### Field Projection

Select specific fields from your data:

```python
from fractal_projections import FieldProjection, ProjectionList

projection = ProjectionList([
    FieldProjection("user_id"),
    FieldProjection("email"),
])
```

### Aggregation

Perform aggregations like COUNT, SUM, AVG:

```python
from fractal_projections import AggregateProjection, AggregateFunction

projection = AggregateProjection(
    field="revenue",
    function=AggregateFunction.SUM,
    alias="total_revenue"
)
```

### Grouping

Group results by one or more fields:

```python
from fractal_projections import GroupingProjection

grouping = GroupingProjection(["organization_id", "status"])
```

### Ordering

Sort results by fields:

```python
from fractal_projections import OrderingProjection, OrderingList

ordering = OrderingList([
    OrderingProjection("created_at", descending=True),
    OrderingProjection("name", descending=False),
])
```

### Limiting

Limit and offset results:

```python
from fractal_projections import LimitProjection

limit = LimitProjection(limit=20, offset=10)
```

## Architecture

The library follows a builder pattern:

1. **Projections**: Database-agnostic definitions of how data should be shaped
2. **Builders**: Database-specific converters that translate projections into native queries

```
QueryProjection (agnostic)
    ↓
PostgresProjectionBuilder → SQL
MongoProjectionBuilder → MongoDB Pipeline
FirestoreProjectionBuilder → Firestore Query
ElasticsearchProjectionBuilder → ES Query DSL
```

This separation allows you to:
- Write business logic once
- Switch databases without changing application code
- Get optimized native queries for each backend

## Advanced Usage

See the [examples.py](fractal_projections/examples.py) file for comprehensive examples including:
- Complex aggregations with grouping
- Multi-field ordering
- Combining filters with projections
- Database-specific optimizations

## Development

```bash
# Clone the repository
git clone https://github.com/Fractal-Forge/fractal-projections.git
cd fractal-projections

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black .
isort .
ruff check --fix .

# Lint code
ruff check .
mypy fractal_projections
```

## License

MIT License - see LICENSE file for details

## Contributing

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

## Related Projects

- [fractal-projections](https://github.com/Fractal-Forge/fractal-projections) - Database-agnostic filtering system