Skip to content

fix(query): compile relation filters inside where groups and nested relation constraints - #55

Merged
3m1n3nc3 merged 2 commits into
mainfrom
fix/relation-filters-in-groups
Oct 10, 2026
Merged

3m1n3nc3 merged 2 commits into
mainfrom
fix/relation-filters-in-groups

Conversation

@3m1n3nc3

Copy link
Copy Markdown
Member

Summary

Two relation-filter bugs, both found while building a permission-based audience query in an app.

1. whereHas inside a where(callback) group was discarded

A grouped where callback kept only the group's plain conditions. Any whereHas / orWhereHas inside it was dropped without an error, so results came back wider than asked for.

User.query()
  .where({ id: 2 })
  .where((query) => query.whereHas('posts', (posts) => posts.where({ title: 'A' })))
// before: matched user 2; after: matches nobody

2. whereHas nested inside another relation's callback did not compile

A relation constraint that itself contained a relation filter couldn't be turned into an adapter spec. So the query was refused or handled outside SQL, and toSql() didn't show the filter.

Fix

  • New QueryRelationCondition ({ type: 'relation', filter }) where-tree node.
  • Relation filters inside a group, and inside another relation's constraint, are folded into the where tree. They keep the and/or they were added with.
  • The Kysely adapter compiles the node as the same correlated count(*) subquery used by top-level whereHas.
  • The Prisma compatibility adapter rejects the node with UnsupportedAdapterFeatureException instead of silently ignoring it.

Also

  • Corrected the update() / updateFrom() docblocks. update() changes the first match only, as the guide already says; updateFrom() changes every match.
  • Documented nested and grouped relation filters in the relationships guide.
  • Pre-existing fixes:
    • cast-set-symmetry.spec.ts failed tsc because its model borrows the user schema, which has no unitPrice.
    • packages/plugin-clear-router/package.json failed format:check.

Test plan

  • New Postgres tests for grouped and nested relation filters. Both fail on main and pass here; the nested test also asserts each query runs as a single SQL statement.
  • pnpm build, pnpm lint, tsc --noEmit (0 errors), pnpm format:check
  • pnpm test:all: 567 passed, 2 skipped

…lation constraints

- whereHas/orWhereHas inside a where(callback) group were discarded, because
  only the group's plain conditions were kept, which widened results
  silently. They now travel in the group as relation conditions.
- whereHas inside another whereHas callback could not be compiled, so the
  query was refused or handled outside SQL. Nested filters now compile to
  correlated subqueries on the related table.
- Add the QueryRelationCondition where-tree node, compiled by the Kysely
  adapter; the Prisma compatibility adapter rejects it explicitly.
- Correct the update()/updateFrom() docblocks: update() changes the first
  match, updateFrom() changes every match.
- Document nested and grouped relation filters.
The test model borrows the user schema, which has no unitPrice, so raw
attribute reads failed the typecheck. Read them through a loosely typed
helper. Add the missing trailing newline Prettier expects.
@3m1n3nc3
3m1n3nc3 merged commit 053711f into main Oct 10, 2026
5 checks passed
@codecov

codecov Bot commented Oct 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/QueryBuilder.ts 93.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

3m1n3nc3 added a commit that referenced this pull request Oct 10, 2026
fix(query): compile relation filters inside where groups and nested relation constraints
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant