Skip to content

v6.1.0

Choose a tag to compare

@github-actions github-actions released this 22 Jul 19:15
· 23 commits to master since this release

v6.1.0 — the modeling matrix, complete — and the documentation site

The store-neutral modeling vocabulary now lands on every store that has the concept, as that
store's native mechanism
— and the project has a full documentation site:
https://equantic.github.io/core-data/

The annotation × provider matrix is closed

Annotation Relational (PG/MySQL/MariaDB/SQL Server) Cosmos DB Cassandra MongoDB
[Entity] / [EntityKey] / [StoredAs] / [Unmapped] new new new
[PartitionKey] hierarchical multi-hash (≤ 3 levels), new
[ClusteringKey] ✅ ordered index new ✅ composite index new ✅ native ✅ compound index new
[ConcurrencyToken] ✅ ETag LWT, new conditional replace, new
[SearchIndex] GIN trigram (PG), new ✅ SASI
[TimeToLive] default_time_to_live, new TTL index, new
[Facet] new varchar(n) / numeric(p,s)

Highlights

Cosmos DB: one serializer contract, end to end

CosmosEntitySerializer (System.Text.Json) applies [StoredAs]/[Unmapped] through the
serialization contract and extends the SDK's CosmosLinqSerializer, so LINQ member translation
asks the same contract: filters, sorts and projections on a renamed member hit the stored element —
a rename can never desynchronize documents from queries. Queries on [Unmapped] members refuse
loudly instead of matching nothing. Type-level Converts<TMember, TStored> keeps filter constants
converting identically to documents. Hierarchical partition keys (up to three levels) create
multi-hash containers, point writes build the multi-value key transparently, and partition
inference declines rather than guessing.

MongoDB: the fluent model it was missing

MongoModelBuilderCollection / Key / Field / Ignore / per-member Converts /
ConcurrencyToken / ClusteringKey / TimeToLive — over the driver's class maps, so the LINQ
provider renders filters against renames and converted values. Plus MongoModel.Explain() reading
the actual driver state.

Optimistic concurrency: one exception, four native mechanisms

[ConcurrencyToken] now means the same thing everywhere — a stale write must fail, never
silently win
— as a versioned WHERE (relational), an _etag If-Match (Cosmos), a
lightweight transaction (Cassandra: INSERT … IF NOT EXISTS / UPDATE … IF version = old,
refusing the LOGGED BATCH instead of degrading), and a version-filtered conditional replace
(MongoDB). All four throw ConcurrencyConflictException on the lost race.

Native TTL and search

[TimeToLive] lands as Cassandra's default_time_to_live and MongoDB's per-document TTL index
(from the lifecycle CreatedAt, or an explicit member via fluent — the semantic difference is
documented, not hidden). [SearchIndex] on PostgreSQL materializes a GIN trigram index
(pg_trgm) in EnsureCollection() — substring LIKE stops scanning; dialects without an
equivalent ignore the declaration (same semantics, unindexed plan) and Explain() says which.

Relational: composite keys, facets, ordered reads

Key(x => new { x.OrderId, x.LineNo }) — composite PRIMARY KEY DDL, point lookups by tuple
(GetAsync((orderId, lineNo))), updates/deletes addressing all key columns; keyset paging and
reference includes refuse composite targets with guidance. [Facet] sizes the DDL
(varchar(n)/nvarchar(n), numeric(p,s)). [ClusteringKey] materializes a direction-aware
multi-column index.

Explain() everywhere

All four models now report every mapping decision — names, stored types, keys, tokens, TTLs,
indexes, lifecycle — the way Explain() reports a query. Pin the lines you rely on in a test.

Documentation site

https://equantic.github.io/core-data/ — getting started per provider, the concepts and the
pushdown gates explained didactically, the full modeling matrix, per-store deep dives with the
why of every rejection, cookbook (multi-tenancy, value objects, auditing, a coming-from-EF
translation table), architecture/SPI guides, and the complete API reference generated from the
source docs. Every API shape shown was verified against the source.

Verification

All integration suites green against real stores: PostgreSQL 51, Cosmos DB 41 (vNext emulator,
hierarchical keys proven end to end), MongoDB 131, Cassandra 94, SQL Server 13, MySQL 16, core 62 —
including lost-race writes throwing ConcurrencyConflictException, TTLs read back from
system_schema, trigram indexes in pg_indexes, and document-shape ground truth via raw JSON/BSON.

Compatibility: no breaking changes. Requires eQuantic.Core.DataModel 4.0.0 /
eQuantic.Core.Domain 4.0.0 (as since v6.0.0).