diff --git a/CHANGELOG.md b/CHANGELOG.md index eb0aab3..4e17ef2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). --- +## v26.06.74 (2026-06-07) + +### Docs (data — query-mechanism decision guide + Spring-Data parity matrix) + +Following a full Spring-Data parity audit (CrudRepository / Pageable / derived queries / @query / +Specifications / Query-by-Example, relational **and** document — all present and behavior-tested), +`docs/modules/data.md` gains two teaching sections so users know **when** to use each tool and +where the boundaries are: + +- **Choosing a Query Mechanism** — a decision table for derived methods vs `@query` vs + Specifications vs Query-by-Example (and when *not* to use each), plus how `Pageable`/`Sort` + layer on top. +- **Spring Data Parity & Current Limitations** — a relational-vs-document capability matrix + (soft-delete, optimistic locking, auditing, transactions differ by backend) and an explicit list + of Spring features **not yet** implemented (`@Modifying` bulk writes, `Slice`, DTO/open + projections, the `StartingWith`/`IgnoreCase`/`Top` derived keywords, etc.) — so the docs set + accurate expectations rather than implying parity that isn't there. + +The companion plugin's `implement-data-repository` skill gains the same decision table + capability +matrix + limitations (verified against the source: every cited symbol and operator exists). + ## v26.06.73 (2026-06-07) ### Changed (benchmarks — rigor overhaul) diff --git a/README.md b/README.md index 9596b10..58d2dc5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Firefly Framework Python 3.12+ License: Apache 2.0 - Version: 26.06.73 + Version: 26.06.74 Type Checked: mypy strict Code Style: Ruff Async First diff --git a/docs/modules/data.md b/docs/modules/data.md index 62fca1f..54bed29 100644 --- a/docs/modules/data.md +++ b/docs/modules/data.md @@ -1045,6 +1045,49 @@ Source files: --- +## Choosing a Query Mechanism + +PyFly offers four ways to express a read, plus pagination that layers on top of any of them. +Reach for the simplest one that fits, and escalate only when it can't express the query: + +| Mechanism | Use it when | Avoid it when | Example | +|-----------|-------------|---------------|---------| +| **Derived query methods** (`find_by_…`) | The predicate is a fixed combination of fields and the [supported operators](#operators), joined by AND/OR, optionally ordered. Type-safe, no SQL. **Start here.** | The query needs joins, aggregation, `OR` across more than a couple of fields, or an operator PyFly doesn't derive (see limitations). | `find_by_status_and_total_greater_than(...)` | +| **`@query`** (JPQL-like or `native=True`) | The query is too complex to name — joins, aggregates, hand-tuned SQL, or a MongoDB filter document. | A derived method would read clearly — don't write SQL for `find_by_id`. | `@query("SELECT o FROM Order o WHERE o.total > :min")` | +| **Specification** | The predicate is **dynamic** — built at runtime from optional filters and composed with `&` / `\|` / `~`. | The predicate is fixed (use a derived method) or static (use `@query`). | `active & (admin \| owner)` | +| **Query-by-Example** (`FilterUtils.from_example` / `from_dict`) | You have a populated example object or a dict of equality filters (e.g. straight from API query params). | You need ranges, `OR`, or anything beyond field-equality `AND`. | `FilterUtils.from_dict({"status": "ACTIVE"})` | + +Then wrap any of the above with **`Pageable` + `Sort`** for paginated, ordered results +(`find_paginated`, `find_all_by_spec_paged`). Use `Pageable.unpaged()` to fetch everything. + +## Spring Data Parity & Current Limitations + +PyFly's repositories are Spring-Data-equivalent for the everyday feature set — across **both** +backends: CRUD + batch ops, `Pageable`/`Sort`/`Page`, derived queries, `@query`, Specifications, +Query-by-Example, projections, repository auto-implementation, and generic-type extraction are all +implemented and behavior-tested on relational **and** document. + +Some capabilities are **backend-specific** today: + +| Capability | Relational (SQLAlchemy) | Document (MongoDB) | +|------------|:-----------------------:|:------------------:| +| CRUD + batch, Pageable/Sort/Page, derived queries, `@query`, Specifications, QBE | ✅ | ✅ | +| Projections | ✅ | ✅ (closed/field-subset) | +| Soft delete (`SoftDeleteRepository`) | ✅ | ❌ not yet | +| Optimistic locking (`VersionedMixin` / `@Version`) | ✅ | ❌ not yet | +| Auditing auto-population | ✅ `created/updated_at` **and** `created/updated_by` | ⚠️ timestamps at insert only | +| `@transactional` (propagation, isolation, read-only, `rollback_for`) | ✅ | ⚠️ basic commit/abort (`@mongo_transactional`, replica set required) | + +**Not yet implemented on either backend** (so you don't reach for them): `@Modifying`-style +declarative bulk `UPDATE`/`DELETE`; `Slice` (count-less paging) and streaming/reactive result +types; DTO / open (SpEL) / dynamic / association-traversing projections; the derived-query keywords +`StartingWith` / `EndingWith` / `IgnoreCase` / `True` / `False` / `Distinct` / `Top` / +`After` / `Before` (use `_like`/`_containing`/`@query` instead); `ExampleMatcher` string-match +modes; named queries and `Pageable`/SpEL injection into `@query`. For these, fall back to `@query` +(or `native=True`) — it covers every case the derived parser doesn't. + +--- + ## Available Adapters | Adapter | Package | Backend | Guide | diff --git a/pyproject.toml b/pyproject.toml index e7591d4..bcca708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pyfly" # CalVer YY.MM.PATCH — package metadata uses PEP 440 normalized form (26.5.4); # git tag, GitHub release and human-readable display use leading-zero form # (v26.05.04) to match the Java/.NET/Go siblings. -version = "26.6.73" +version = "26.6.74" description = "The official Python implementation of the Firefly Framework — DI, CQRS, EDA, hexagonal architecture, and more." readme = "README.md" license = "Apache-2.0" diff --git a/src/pyfly/__init__.py b/src/pyfly/__init__.py index 2457128..2972c1b 100644 --- a/src/pyfly/__init__.py +++ b/src/pyfly/__init__.py @@ -13,4 +13,4 @@ # limitations under the License. """PyFly — Enterprise Python Framework.""" -__version__ = "26.06.73" +__version__ = "26.06.74" diff --git a/uv.lock b/uv.lock index 69a3c34..0f13d25 100644 --- a/uv.lock +++ b/uv.lock @@ -1981,7 +1981,7 @@ wheels = [ [[package]] name = "pyfly" -version = "26.6.73" +version = "26.6.74" source = { editable = "." } dependencies = [ { name = "pydantic" },