Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,32 @@ maturin develop # compile Rust + install in dev mode
```

### Run Tests

```bash
# Rust unit tests (no DB needed)
cargo test

# Python unit tests (no DB needed)
python test.py

# Integration tests (SQLite)
python test.py --integration

# All tests
python test.py --all
```


### Run Benchmarks

To measure the performance of the query compiler:

```bash
cd ryx-query && cargo bench
```

### Type Check


```bash
mypy ryx/
```
Expand Down
66 changes: 15 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,70 +75,34 @@ async with ryx.transaction():
| **Backends** | All | All | **PG · MySQL · SQLite** |
| **Migrations** | Built-in | Alembic | **Built-in** |

## Performance
## Architecture

<p align="center">
<img src="ryx_architecture.svg" alt="Ryx Architecture" width="100%" />
</p>

Your Python queries are compiled to SQL in Rust, executed by sqlx, and decoded back — all without blocking the Python event loop.

Since v0.1.3, the query engine has been extracted into a standalone crate `ryx-query`. This decouples the SQL compilation logic from the PyO3 bindings, enabling extreme performance and independent testing.

## Performance

Benchmark of 1 000 rows on SQLite (lower is better):

| Operation | Ryx ORM | SQLAlchemy ORM | SQLAlchemy Core | Ryx raw |
|-----------|--------:|---------------:|----------------:|--------:|
| **bulk_create** | 0.0074 s | 0.1696 s | 0.0022 s | 0.0011 s |
| **bulk_update** | 0.0023 s | 0.0018 s | 0.0010 s | 0.0005 s |
| **bulk_delete** | 0.0005 s | 0.0012 s | 0.0009 s | 0.0004 s |
| **filter + order + limit** | 0.0009 s | 0.0019 s | 0.0008 s | 0.0004 s |
| **aggregate** | 0.0002 s | 0.0015 s | 0.0005 s | 0.0001 s |

Ryx ORM is **16× faster** than SQLAlchemy ORM on bulk inserts and **2× faster** on deletes — while keeping the same Django-style API. The raw SQL layer (`raw_execute` / `raw_fetch`) gives you near-C speed when you need it.

**Internal Compilation Speed**: Our query compiler is blindingly fast, with simple lookups compiled in **~248ns** and complex query trees in **~1µs**.

Run the benchmark yourself:

```bash
uv add sqlalchemy[asyncio] aiosqlite
uv run python examples/13_benchmark_sqlalchemy.py
```

## Quick Start

```bash
pip install maturin
maturin develop # compile Rust + install
```

```python
import asyncio, ryx
from ryx import Model, CharField

class Article(Model):
title = CharField(max_length=200)

async def main():
await ryx.setup("sqlite:///app.db")
await ryx.migrate([Article])
await Article.objects.create(title="Hello Ryx")
print(await Article.objects.all())

asyncio.run(main())
```

## Key Features

- **30+ field types** — from `AutoField` to `JSONField`, with validation built in
- **Q objects** — complex `AND` / `OR` / `NOT` expressions with nesting
- **Aggregations** — `Count`, `Sum`, `Avg`, `Min`, `Max` with `GROUP BY` and `HAVING`
- **Relationships** — `ForeignKey`, `OneToOneField`, `ManyToManyField` with `select_related` / `prefetch_related`
- **Transactions** — async context managers with nested savepoints
- **Signals** — `pre_save`, `post_save`, `pre_delete`, `post_delete` and more
- **Migrations** — autodetect schema changes, generate and apply
- **Validation** — field-level + model-level, collects all errors before raising
- **Sync/async bridge** — use from sync or async code seamlessly
- **CLI** — `python -m ryx migrate`, `makemigrations`, `shell`, `inspectdb`

## Architecture

<p align="center">
<img src="ryx_architecture.svg" alt="Ryx Architecture" width="100%" />
</p>

Your Python queries are compiled to SQL in Rust, executed by sqlx, and decoded back — all without blocking the Python event loop.

## Documentation

Expand Down
10 changes: 7 additions & 3 deletions docs/doc/internals/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar_position: 2
Ryx is built in three layers, each with a clear responsibility.

## Layer Diagram

```
┌──────────────────────────────────────────────────────────┐
│ Python Layer (ryx/) │
Expand All @@ -17,8 +17,11 @@ Ryx is built in three layers, each with a clear responsibility.
│ PyO3 Boundary (src/lib.rs) │
│ QueryBuilder · TransactionHandle · Type Bridge · Async │
├──────────────────────────────────────────────────────────┤
│ Rust Core (src/) │
│ AST · Q-Trees · SQL Compiler · Executor · Pool · Tx │
│ Modular Query Engine (ryx-query crate) │
│ AST · Q-Trees · SQL Compiler · Lookup Registry │
├──────────────────────────────────────────────────────────┤
│ Rust Core (src/) │
│ Executor · Pool · Transaction Logic │
├──────────────────────────────────────────────────────────┤
│ sqlx 0.8.6 + tokio 1.40 │
│ AnyPool · Async Drivers · Transactions │
Expand All @@ -27,6 +30,7 @@ Ryx is built in three layers, each with a clear responsibility.
└──────────────────────────────────────────────────────────┘
```


## Query Execution Flow

```
Expand Down
59 changes: 30 additions & 29 deletions docs/doc/internals/query-compiler.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,41 @@ sidebar_position: 4
---

# Query Compiler

The heart of Ryx — transforms Python query expressions into optimized SQL.

The heart of Ryx — transforms Python query expressions into optimized SQL.

Since v0.1.3, the compiler resides in the standalone `ryx-query` crate, decoupled from the Python bindings for maximum performance and testability.
## Pipeline

```
Python QuerySet methods
QueryNode (Rust AST)
compiler::compile()
ryx_query::compiler::compile()
CompiledQuery { sql: String, values: Vec<SqlValue> }
```

## AST Types

### QueryNode

The root of every query:

```rust
pub struct QueryNode {
pub operation: QueryOperation, // Select, Aggregate, Count, Delete, Update, Insert
pub table: String,
pub columns: Vec<String>,
pub backend: Backend, // DB backend for SQL generation
pub operation: QueryOperation, // Select, Aggregate, Count, Delete, Update, Insert
pub filters: Vec<FilterNode>,
pub q_tree: Option<QNode>,
pub q_filter: Option<QNode>,
pub joins: Vec<JoinClause>,
pub annotations: Vec<AggregateExpr>,
pub group_by: Vec<String>,
pub having: Vec<FilterNode>,
pub order_by: Vec<OrderByClause>,
Expand All @@ -43,48 +46,46 @@ pub struct QueryNode {
pub distinct: bool,
}
```

### QNode — Boolean Expression Tree

```rust
pub enum QNode {
Leaf { field: String, lookup: String, value: SqlValue, negated: bool },
And { left: Box<QNode>, right: Box<QNode> },
Or { left: Box<QNode>, right: Box<QNode> },
Not { inner: Box<QNode> },
And(Vec<QNode>),
Or(Vec<QNode>),
Not(Box<QNode>),
}
```

### SqlValue — Type-Safe Values

```rust
pub enum SqlValue {
Null,
Bool(bool),
Int(i64),
Float(f64),
Text(String),
Bytes(Vec<u8>),
Date(chrono::NaiveDate),
Time(chrono::NaiveTime),
DateTime(chrono::NaiveDateTime),
Json(serde_json::Value),
List(Vec<SqlValue>),
}
```

### JoinClause

```rust
pub enum JoinKind { Inner, LeftOuter, RightOuter, FullOuter, Cross }

pub enum JoinKind { Inner, LeftOuter, RightOuter, FullOuter, CrossJoin }
pub struct JoinClause {
pub table: String,
pub condition: String,
pub kind: JoinKind,
pub table: String,
pub alias: Option<String>,
pub on_left: String,
pub on_right: String,
}
```


## Compilation Process

1. **SELECT clause** — `columns` or `*`
Expand Down
Loading