Skip to content

Bug: filterMany() expressions when additional nested fetch has predicates in wrong place#3851

Merged
rbygrave merged 5 commits into
masterfrom
fix/filter-many-nested-fetch-path-join-order
Jul 15, 2026
Merged

Bug: filterMany() expressions when additional nested fetch has predicates in wrong place#3851
rbygrave merged 5 commits into
masterfrom
fix/filter-many-nested-fetch-path-join-order

Conversation

@rbygrave

Copy link
Copy Markdown
Member

When a filterMany() expression only references the many-root's own properties (e.g. .eq("status", ...)), but the query also has an additional nested fetch beneath that many-path (e.g. .fetch("orders.customer") or .fetch("contacts.group")), the filter predicate was incorrectly attached to the last/deepest join's ON clause instead of the many-root's own join — silently misapplying the filter.

Fix: A hybrid approach:

  • If the filterMany expression references only root-level properties → attach the predicate directly on the many-root's own join (correct).

  • If it references deeper/nested properties requiring "extra joins" (a separate existing mechanism) → fall back to the original end-of-subtree behavior, since no SqlTreeNode exists to attach to.

  • includeFilterMany() made idempotent so both mechanisms coexist safely.

…ates in wrong place

When a filterMany() expression only references the many-root's own properties (e.g. .eq("status", ...)),
but the query also has an additional nested fetch beneath that many-path (e.g. .fetch("orders.customer")
or .fetch("contacts.group")), the filter predicate was incorrectly attached to the last/deepest join's
ON clause instead of the many-root's own join — silently misapplying the filter.

Fix: A hybrid approach:

- If the filterMany expression references only root-level properties → attach the predicate directly on the many-root's own join (correct).

 - If it references deeper/nested properties requiring "extra joins" (a separate existing mechanism) → fall back to the original end-of-subtree behavior, since no SqlTreeNode exists to attach to.

 - includeFilterMany() made idempotent so both mechanisms coexist safely.
@rbygrave rbygrave self-assigned this Jul 15, 2026
filterMany() expressions referencing a nested/dotted property (e.g.
.eq("group.name", "x"), alone or mixed with root properties) were
attached to a LEFT JOIN's ON clause via the "extra join" mechanism.

That only nulls the extra join's own columns on non-match - it does
not exclude the parent/many-side row, so the predicate had no actual
filtering effect (confirmed by data, not just SQL shape).

## Fix:

force any many-property fetch whose filterMany expression
references a nested property onto a query-join (secondary select
restoring filterMany's original always-fetchQuery behaviour for this
with a genuine WHERE clause) instead of leaving it as an inline JOIN, case.

- SpiExpressionValidation: track all visited property names (allProperties()), not just unknown ones.
- OrmQueryProperties: filterManyHasNestedProperty() walks the filterMany expression for any dotted property reference.
- OrmQueryDetail.markQueryJoins(): route nested-property filterMany chunks to markForQueryJoin() instead of the inline fetch-join slot, without consuming that slot for other many-paths.

Also simplifies the earlier root-only-predicate join-placement fix
(CQueryPredicates/DefaultDbSqlContext/SqlTreeNodeManyRoot) now that
a nested-property filterMany can never reach the JOIN-based code
path: removed the now-unreachable "deepest path" fallback branch,
the includeFilterMany() idempotency guard, and the redundant fallback
call in SqlTreeNodeManyRoot - isFilterManyAttachPoint() remains as
the single, always-used attach mechanism.

Updated TestQueryFilterMany/TestQueryFilterManySimple assertions to
expect the corrected query-join (2 statement) behaviour.
@rbygrave rbygrave added the bug label Jul 15, 2026
@rbygrave rbygrave added this to the 18.3.0 milestone Jul 15, 2026
@rbygrave rbygrave merged commit 84a311e into master Jul 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants