Skip to content

Commit 76fde58

Browse files
committed
docs(spec): a declared never-string-capable search column is admitted but contributes no clause
The declared-branch comment in search-fields.ts and its test comment still called a declared json column "a scan that CAN match". Since the search expansion skips a column whose declared type can never hold a string (the text-operator door's door-refusal verdict), such a column is admitted to the set but contributes no clause, and a set made only of such columns answers no rows. Comment-only; no behaviour change. The docblock ships in the spec package's .d.ts, so a patch changeset rides. Claude-Session: https://claude.ai/code/session_01JQwxbWkmiSMGh5SFWcG7XY Co-authored-by: Claude <noreply@anthropic.com>
1 parent ff827bc commit 76fde58

3 files changed

Lines changed: 45 additions & 12 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): the `resolveSearchFieldResolution` docblock says a declared search column whose type can never hold a string is admitted to the set but contributes no clause
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The declared-branch text on `resolveSearchFieldResolution` (`@objectstack/spec`, shipped in its `.d.ts`) still said a `json` column declared in `searchableFields` is "a scan that CAN match". The `$search` expansion in `@objectstack/objectql` gives such a column no clause: its declared type can never hold a string, which is the text-operator door's `door-refusal` verdict (`textOperatorDoorVerdict`). The docblock now says so:
11+
12+
- a declared `json` column (or any numeric, boolean, date or time, or structured-JSON column, or a `formula` whose `returnType` names one) is still admitted to the set, but contributes no clause;
13+
- a set made only of such columns answers no rows, never an error;
14+
- a declared single-valued `lookup` column still matches by substring over the stored foreign key;
15+
- only an entry that names no column at all is dropped, as before.
16+
17+
Comment-only: no behaviour, schema or accepted value moves.

‎packages/spec/src/data/search-fields.test.ts‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,17 @@ describe('a virtual field declared in searchableFields — not admitted, since n
309309

310310
it('CONTROL — the declared branch still bypasses the auto-default exclusions', () => {
311311
// The dividing line is STORAGE, not search quality. `lookup` and `json` are
312-
// auto-EXCLUDED types, and declaring them is still the author's choice: the
313-
// engine runs a `$contains` over the stored foreign key / the stored JSON
314-
// text. Narrow and rarely useful, but a scan that CAN match — so it must not
315-
// be swept up by the virtual filter. This control is what keeps #6674 from
316-
// silently becoming "the declared branch is type-filtered after all", which
317-
// would reject metadata the runtime accepts (ADR-0072 D1, and the #4830
318-
// changeset's explicit carve-out).
312+
// auto-EXCLUDED types, and declaring them is still the author's choice, so
313+
// both are ADMITTED here. What each then contributes is the expansion's call
314+
// (`expandSearchToFilter`, `@objectstack/objectql`): `lookup` becomes an
315+
// `$icontains` over the stored foreign key, a narrow scan that can match;
316+
// [#29394] `json` can never hold a string (the text-operator door's
317+
// `door-refusal` verdict), so it contributes NO clause, and a set made only
318+
// of such columns answers no rows. Neither is swept up by the virtual
319+
// filter, which drops only an entry with no column at all. This control is
320+
// what keeps #6674 from silently becoming "the declared branch is
321+
// type-filtered after all", which would reject metadata the runtime accepts
322+
// (ADR-0072 D1, and the #4830 changeset's explicit carve-out).
319323
expect(
320324
resolveSearchFieldResolution({
321325
fields,

‎packages/spec/src/data/search-fields.ts‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,23 @@ function autoDefaultFields(fields: Record<string, SearchFieldMeta>, displayField
283283
*
284284
* Filter 2 is deliberately NOT a type allow-list on the declared branch. The
285285
* declaration remains the author's explicit choice and still bypasses the
286-
* auto-default's exclusions: a `json` or `lookup` column declared here is
287-
* executed by the engine as a `$contains` over the stored JSON text / stored
288-
* foreign key — narrow, rarely useful, but a scan that CAN match. Only "there
289-
* is no column at all" is refused, which is why the vocabulary is a storage
290-
* fact rather than a search-quality one.
286+
* auto-default's exclusions, so a `lookup` or `json` column declared here is
287+
* ADMITTED to the set. What an admitted column then contributes to a search is
288+
* the expansion's call (`expandSearchToFilter`, `@objectstack/objectql`), not
289+
* this function's:
290+
*
291+
* - a single-valued `lookup` column becomes an `$icontains` over the stored
292+
* foreign key — narrow, rarely useful, but a scan that can match;
293+
* - [#29394] a column whose DECLARED type can never hold a string — the
294+
* text-operator door's `door-refusal` verdict (`textOperatorDoorVerdict`,
295+
* `./filter-text-operator-declared-type`: numeric, boolean, calendar-date,
296+
* instant, clock-time and structured-JSON types such as `json`, plus a
297+
* `formula` whose readable `returnType` names one) — contributes NO clause.
298+
* A set made only of such columns answers no rows: never an error, never
299+
* every row.
300+
*
301+
* Only "there is no column at all" is refused, which is why the vocabulary is
302+
* a storage fact rather than a search-quality one.
291303
*
292304
* Why the drop is not the whole fix. Dropping alone would only make the
293305
* declaration silently NARROWER — the failure mode #4254 exists to close on

0 commit comments

Comments
 (0)