Skip to content

Commit 691a158

Browse files
committed
docs(spec): apply contract-review should-fixes to the search-fields comments
- Drop the formula mention from the no-clause bullet: Filter 2 drops every formula before admission, so no formula reaches the expansion from the declared branch (same fix in the changeset bullet). - Say "substring scan" for the generic scan on the docblocks, and correct the SEARCH_VIRTUAL_TYPES docblock, which still said a declared json column is a scan that can match. - Test CONTROL comment: "single-valued" lookup, as in the docblock. Comment-only; no behaviour change. Claude-Session: https://claude.ai/code/session_01JQwxbWkmiSMGh5SFWcG7XY Co-authored-by: Claude <noreply@anthropic.com>
1 parent 76fde58 commit 691a158

3 files changed

Lines changed: 13 additions & 13 deletions

File tree

‎.changeset/29546-declared-search-column-no-clause-comment.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Metadata-protocol: none
99

1010
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:
1111

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;
12+
- a declared `json` column (or any numeric, boolean, date or time, or structured-JSON column) is still admitted to the set, but contributes no clause;
1313
- a set made only of such columns answers no rows, never an error;
1414
- a declared single-valued `lookup` column still matches by substring over the stored foreign key;
1515
- only an entry that names no column at all is dropped, as before.

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,9 @@ describe('a virtual field declared in searchableFields — not admitted, since n
311311
// The dividing line is STORAGE, not search quality. `lookup` and `json` are
312312
// auto-EXCLUDED types, and declaring them is still the author's choice, so
313313
// 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;
314+
// (`expandSearchToFilter`, `@objectstack/objectql`): a single-valued
315+
// `lookup` becomes an `$icontains` over the stored foreign key, a narrow scan
316+
// that can match;
316317
// [#29394] `json` can never hold a string (the text-operator door's
317318
// `door-refusal` verdict), so it contributes NO clause, and a set made only
318319
// of such columns answers no rows. Neither is swept up by the virtual

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ export const SEARCH_AUTO_EXCLUDED_TYPES: ReadonlySet<string> = new Set([
8989
]);
9090
/**
9191
* [#6674] Field types with NO STORED COLUMN — the value is computed on read, so
92-
* a `$contains` scan has nothing to look at. Exactly `formula` today.
92+
* a substring scan has nothing to look at. Exactly `formula` today.
9393
*
9494
* This is a STORAGE fact, not a taste judgment, and that is what separates it
9595
* from the three vocabularies above. `SEARCH_AUTO_EXCLUDED_TYPES` says "the
9696
* auto-default does not GUESS this type" — an author may still declare a `json`
97-
* or `lookup` column in `searchableFields` and the engine executes it (a
98-
* `$contains` over the stored JSON text, over the stored foreign key), which is
99-
* a choice that can match. A `formula` entry cannot: the drivers materialize no
100-
* column for it (`driver-sql/src/schema-drift.ts` `fieldHasColumn`,
97+
* or `lookup` column in `searchableFields` and it is admitted, because a stored
98+
* column exists (what each then contributes to a search is set out on
99+
* {@link resolveSearchFieldResolution}). A `formula` entry is not: the drivers
100+
* materialize no column for it (`driver-sql/src/schema-drift.ts` `fieldHasColumn`,
101101
* `driver-turso/src/remote-transport.ts` "Virtual — no column"), and the engine
102102
* excludes it from the projection it sends down (`objectql/src/engine.ts`
103103
* `buildFormulaPlan`) because the driver would fail on the column name.
@@ -279,7 +279,7 @@ function autoDefaultFields(fields: Record<string, SearchFieldMeta>, displayField
279279
* 2. [#6674] SCANNABILITY — an entry naming a VIRTUAL field
280280
* ({@link isVirtualSearchField}) is dropped. The entry names a real field,
281281
* passes existence, and can still never match, because the field has no
282-
* stored column for `$contains` to scan.
282+
* stored column for a substring scan to read.
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
@@ -293,10 +293,9 @@ function autoDefaultFields(fields: Record<string, SearchFieldMeta>, displayField
293293
* - [#29394] a column whose DECLARED type can never hold a string — the
294294
* text-operator door's `door-refusal` verdict (`textOperatorDoorVerdict`,
295295
* `./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.
296+
* instant, clock-time and structured-JSON types such as `json`) —
297+
* contributes NO clause. A set made only of such columns answers no rows:
298+
* never an error, never every row.
300299
*
301300
* Only "there is no column at all" is refused, which is why the vocabulary is
302301
* a storage fact rather than a search-quality one.

0 commit comments

Comments
 (0)