Repository navigation
feat(database): relation filters some/none/every and relation counts - #116
Conversation
Correlated EXISTS / NOT EXISTS conditions and a correlated count(*) over a declared relation, on the builder (Db.related) and in findMany's where. findMany's _count counts a many relation with one grouped statement per level. A subquery that correlates on the outer tenant column takes that column's tenant binding.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 46 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…imit A level with more keys than D1's 100 bindings failed to compile. Each level now sends one IN statement per batch that fits maxParameters next to its other bindings. Adds SQLite and D1 coverage and updates the docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in origin/main and MapleTechLabs#110's IN-list batching. The count level and the loaded level now share runBatched, so _count is split to the dialect's bound-value limit too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Correlated EXISTS, count() and _count run on node:sqlite inside the outer tenant, and a 250-key _count level on D1 splits into statements of at most 100 bindings. Docs and changeset name SQLite and D1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Brought up to date for merge (head
Verified locally: CI run 41473306346: 26.2 passed. On 26.8 the only failure was "compiled row codec retains generated values (postgres)", the known codec-fuzz flake fixed in #131. It is unrelated to this PR. Left for the maintainer: please review the |
findManyand the builder cannot filter rows by their related rows or count related rows. Prisma code uses_countandsome/every/nonea lot, and Drizzle needs raw SQL for most of them.Closes #80.
Part of #52.
Depends on #110; review only the commits after it.
Changes
Db.related(schema.table, $)gives the relations of a table on the$of a builder query. Each relation hassome(where?),none(where?),every(where)andcount(where?).someisEXISTS (…),noneisNOT EXISTS (…),everyisNOT EXISTS (… AND NOT (where)). The subquery reads the target under the alias<outer alias>__<relation>, so a self-relation works. Filters nest: thewhereof a filter gets its ownr.count(where?)is anExpr<number>:coalesce((SELECT count(*) …), 0). It works in aselectand in awhere.findMany'swhere(at every level) gets the same object as its second argument:where: ($, r) => [r.posts.some()]._count: { posts: true | { where } }infindManyadds_count: { posts: number }to the rows of that level. It keeps feat(database): relational reads with defineRelations and findMany #110's loading model: one grouped statement per counted relation,SELECT <to>, count(*) … WHERE <to> IN (<keys>) GROUP BY <to>, with the same tenant pin as a loaded level. Onlymanyrelations can be counted (type error otherwise).<inner tenant> = <outer tenant>. A small change incompile.tsgives such a subquery the binding of the outer tenant column, so a filter under$.orgId.eq("a")stays"single-tenant". Only the relation filters set this internal mark, and they always write the equality. A filter does not scope an unscoped outer query. A filter from an untenanted table into a tenanted one is"cross-tenant"unless its ownwherepins the tenant.QueryBuilderDefect, so it never correlates a column to itself.docs/relations.md, section 6 indesign/relations.md,docs/reference.md, one changeset.ClickHouse (tested live on 26.8.2.7)
some,none,every, and nested filters work (correlated subqueries are on by default in 26.8).count()in awhereworks.count()in aselectnext to awhereworks only when the outer columns it correlates on are selected too. Otherwise ClickHouse fails withNOT_IMPLEMENTED("can't find correlated column … in current header"). The docs say this, and the test selects the key and tenant columns.count()over no rows returns NULL on ClickHouse, not 0. Thecoalescehandles it._countinfindManyneeds no correlation, so it works on every version.Left out
_countof aonerelation:r.author.some()says the same.update/delete: their accessor is not qualified, so there is no alias to correlate on. Refused with a defect.orderBy: { posts: { _count: "desc" } }). On a builder query, selectcount()and order by its alias.EXISTSandcount(*), so no dialect work is expected.Validation
Run on the head commit against a live ClickHouse 26.8.2.7 (the CI matrix version), with
EFFECT_ORM_CLICKHOUSE_URLset, so no suite is skipped:bun run build: pass.bun run typecheck: pass.bunx vitest run --testTimeout=60000 --hookTimeout=60000: pass. Test files: 64 passed (64). Tests: 1065 passed (1065).node scripts/check-doc-citations.mjs,check-exports-documented.mjs,check-doc-examples.mjs: pass.bun scripts/check-package.ts(tarball, types, CommonJS consumer): pass.Need help on this PR? Tag
@codesmith-botwith what you need.