Repository navigation
Commit 9509ea1
docs(query-syntax): Filtering Across Relationships states the served nested-relation form (#20906)
Fixes #20876
Clause-②: no
## What changed
`content/docs/protocol/objectql/query-syntax.mdx`, section "Filtering
Across Relationships" only. The old callout said relation traversal in
`where` is "not supported" and blamed `SqlDriver.applyFilters()` for
compiling a nested object and emitting a dotted key to Knex. Both were
false: the headline since PR #20872 (`ca5408c62`), the mechanism
sentences since the engine refuses the nested form before any driver.
The section now states the served form, per triage 5914865117:
- `{ relation: { field: value } }` in `where`, one level, forward only,
evaluated as the caller; a multi-valued relation matches any member;
refused past 1000 ids; an unreadable related field answers `403`.
- Verbs and REST doors that serve it; second level and reverse direction
refused; dotted path still `INVALID_FIELD` / 400; an aggregation's
`filter` and `having` refuse the form.
- The two-step example is kept, now framed as the route past the cap and
for the reverse direction (a reverse snippet is added).
- No driver-mechanism prose.
## Code anchors (all at `origin/main` 33b6e8b)
- Lowering seam: `packages/objectql/src/engine.ts:11268`
(`resolveRelateThenLowerWhere`, calls `lowerRelationConditions` at
:11276); the related read is the engine's own `find` with the caller's
context, `:11322`.
- `$in` vs `$contains`:
`packages/objectql/src/relation-filter-lowering.ts:213-218`
(`lowerRelationSite`): `$in` for single-valued, an `$or` of one
`$contains` per id for `multiple: true`.
- Cap: `RELATION_FILTER_ID_CAP = 1000` at
`relation-filter-lowering.ts:84`; read asks for cap+1
(`engine.ts:11325`), refuses at `:11330` via `relationFilterCapError`
(`relation-filter-lowering.ts:297`) as `INVALID_FILTER` / 400
(`filter-comparand-shape.ts:78-85`).
- 403: the related read goes through the security layer's
`assertReadableQueryFields`
(`packages/plugins/plugin-security/src/predicate-guard.ts:106`, called
at `security-plugin.ts:3652`), `PERMISSION_DENIED` / 403. Pinned:
`engine-nested-relation-lowering.test.ts:227`,
`packages/rest/src/data-nested-relation-permission.test.ts`.
- One level: `admitRelationCondition`,
`relation-filter-lowering.ts:162`; dotted key inside the condition
`:189`, relation key inside `:197` (`second-level`), both
`INVALID_FILTER` / 400. Pinned:
`engine-nested-relation-lowering.test.ts:284`,
`packages/rest/src/data-nested-object-door.test.ts` (REFUSED table).
- Reverse direction: not served by the lowering
(`relation-filter-lowering.ts:16-17`); over REST the parent has no such
field, so `assertFilterFieldsExist` answers `INVALID_FIELD` / 400
(`packages/metadata-protocol/src/protocol.ts:10057`, called at
`:11510`). The direct `engine.find` has no field-name door
(`query-syntax.mdx` section 9, "Unknown Fields Are Tolerated"), so the
page states the reverse refusal for the REST doors only.
- Dotted path: `classifyDottedFilterHead`
(`packages/spec/src/data/filter-dotted-head.ts:119`); engine door
`assertFilterIsMaterializable` (`filter-comparand-shape.ts:204`, code
set at `:270`); REST ingress `protocol.ts:10114`.
- Verbs: `find` `engine.ts:11662`, `findOne` `:11935`, `update`
`:13558`, `delete` `:16227`, `count` `:16760`, `aggregate` `:17138`;
pinned for all six plus `judgeFilter` at
`engine-nested-relation-lowering.test.ts:190`. REST: `GET /data/:object`
and `POST /data/:object/query` (`packages/rest/src/rest-server.ts:8635`,
`:8818`) both call `findData` (`protocol.ts:11183`); the POST query door
is the one the REST test drives.
- `aggregations[i].filter` and `having` refuse the form:
`no-operator-object-door.ts:307`; pinned
`engine-nested-relation-lowering.test.ts:329`, REST
`data-nested-object-door.test.ts:256`.
## Shared wording with #20888
The skill half is PR #20902 (open, not merged). Two passages are quoted
verbatim from its `skills/objectstack-query/rules/filters.md`: the
served-form sentence ("A condition on a related record's fields beneath
a relation field ... any member when `multiple: true`).") and the
"Limits — one level: ..." sentence. The code sentence is identical; if
#20902 changes those sentences before landing, this page must be
re-quoted.
## Census of hand-written `content/docs/**`
Searched: "Relation traversal", "not supported" near where/relation,
`applyFilters`, dotted `'account.` examples, "two queries" / "$in its
ids" / "nested form".
- Fixed: `protocol/objectql/query-syntax.mdx` (the section above), the
only hit.
- Already correct, no change: `kernel/contracts/data-engine.mdx:187-215`
states the served form, the 1000 cap, one level, `403`, aggregation
`filter`/`having` and the dotted refusal (landed with #20872).
- Not hits: `permissions/*.mdx` dotted `'account.annual_revenue'` keys
are field-permission keys, not filters;
`protocol/objectql/types.mdx:1035` `'metadata.color'` is the deliberate
structured/JSON carve-out; `query-syntax.mdx` :1135/:1167 and
`data-modeling/queries.mdx:442,519`, `schema-design.mdx:111`,
`api/data-api.mdx:177` concern the search axis and `expand`/`fields`
paths, not `where`.
- Generated reference pages (`content/docs/references/**`) not touched.
Also fixed (contract review, second commit `a2a66881e2`): the
`query-syntax.mdx` line 4 frontmatter `description` listed "joins", a
tombstoned key (`packages/spec/src/data/query.zod.ts:562`; the page says
so at its Joins section); it now reads "expand", which the page's
section 4 (Relationships (Expand)) covers.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands` derived 40 commands at
head `a2a66881e2` (re-run after the second commit; the first pass was at
`bd718b653a`); 39 ran green (exit 0) after `pnpm install` and a
lint-closure build. `--ran` reconciliation: 39 of 40 run, 1 UNRUN: `pnpm
--filter @objectstack/spec run check:skill-examples` exited 3
(prerequisite: the client-SDK surface has no built output). NOT
MEASURED: it type-checks marked TypeScript examples in skills and docs,
and this diff adds no marked block. CI owns it. No changeset (docs only,
nothing published).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent cf684c9 commit 9509ea1
1 file changed
Lines changed: 55 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
600 | 600 | | |
601 | 601 | | |
602 | 602 | | |
603 | | - | |
604 | | - | |
605 | | - | |
606 | | - | |
607 | | - | |
608 | | - | |
609 | | - | |
610 | | - | |
611 | | - | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
612 | 612 | | |
613 | | - | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
614 | 643 | | |
615 | 644 | | |
| 645 | + | |
616 | 646 | | |
617 | 647 | | |
618 | 648 | | |
619 | 649 | | |
620 | 650 | | |
621 | 651 | | |
622 | | - | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
623 | 663 | | |
624 | 664 | | |
625 | 665 | | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
626 | 669 | | |
627 | 670 | | |
628 | 671 | | |
| |||
0 commit comments