Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions content/docs/api/data-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Query records with filtering, sorting, selection, and pagination.
| `top` | query | Max records to return. No default — omitting it returns all matching records. |
| `skip` | query | Offset |
| `expand` | query | Comma-separated list of relations to eager-load. Must name a reference field (`lookup` / `master_detail` / `user` / `tree`) — otherwise `400 INVALID_FIELD`. |
| `search` | query | Full-text search term, scanned case-insensitively across the object's searchable fields (its declared `searchableFields`, or a text-like auto-default when none are declared). |
| `search` | query | Full-text search term, scanned case-insensitively across the object's searchable fields (its declared `searchableFields`, or a text-like auto-default when none are declared). When that set is empty — no `searchableFields` entry names a stored field, and the auto-default finds none — a non-blank term is `400 INVALID_FIELD`, never every row. |
| `searchFields` | query | Comma-separated subset of the searchable fields for `search` to scan — narrows the scan, never widens it. A name outside the searchable set is `400 INVALID_FIELD`. |

> **Note:** OData-style `$`-prefixed parameters (`$filter`, `$select`, `$orderby`, `$top`, `$skip`, `$expand`, `$count`, `$search`) are also accepted directly on this same endpoint as aliases — they're normalized internally to the parameter names above. There is no separate standalone OData endpoint.
Expand Down Expand Up @@ -156,14 +156,17 @@ corrupts something the earlier axes do not:
| `?search=alpha&searchFields=no_such_field` | `400 INVALID_FIELD` |
| `?search=alpha&searchFields=amount` | `400 INVALID_FIELD` — real field, but not searchable |
| `?search=alpha&searchFields=project_id.name` | `400 INVALID_FIELD` — search scans this object's own columns; mirror the related title instead (see below) |
| `?search=alpha` on an object with no searchable field (two lookups and a `formula` title, say) | `400 INVALID_FIELD` — nothing to scan; declare a stored field searchable or mirror the title (see below) |
| `groupBy: ["status"]` | one bucket per status value |
| `groupBy: ["no_such_field"]` | `400 INVALID_FIELD` |
| `aggregations: [{function:"sum", field:"amount", alias:"total"}]` | the real total |
| `aggregations: [{function:"sum", field:"no_such_field", alias:"total"}]` | `400 INVALID_FIELD` |
| `aggregations: [{function:"count", alias:"n"}]` | `count(*)` — the one legitimate field-less form |

- **`searchFields`** — the only parameter whose failure changed which **rows**
came back. An unknown name used to be dropped, and an override left empty
- **`search`** and **`searchFields`** — the only parameters whose failure
changed which **rows** came back. A `search` over an object with nothing to
scan added no predicate, so every row came back as if every row matched. An
unknown `searchFields` name used to be dropped, and an override left empty
fell back to scanning *every* searchable column: a parameter that exists
only to **narrow** a search failed by **widening** it. Three causes get
three messages, because the fixes differ: a name that is no field (a typo in
Expand Down
5 changes: 4 additions & 1 deletion content/docs/data-modeling/queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,10 @@ dotted `fields` path (`'customer.name'`) for a single related column:

`search` takes the query text; the server resolves which fields to search from object
metadata (ADR-0061). Pass `searchFields` to narrow that set — it is intersected with what
the object allows, so it can only narrow, never widen.
the object allows, so it can only narrow, never widen. When the object allows nothing —
no `searchableFields` entry names a stored field, and the default search scans none
(`internal: true` fields never count) — a non-blank `search` is refused with
`400 INVALID_FIELD` instead of answering every row, engine calls included.

```typescript
{
Expand Down
5 changes: 4 additions & 1 deletion content/docs/data-modeling/schema-design.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ To control exactly which fields are matched, set `searchableFields` on the objec
Queries then pass a top-level `$search` parameter to match across `searchableFields`
(e.g. `{ $search: 'acme' }`); a request may narrow the set with
`$searchFields`. When `searchableFields` is unset, search falls back to the
name/title field plus short-text fields.
name/title field plus short-text fields. When neither leaves a stored field to scan — a
junction object of lookups with a `formula` title, say — a list search is refused
`400 INVALID_FIELD` rather than answered with every row, and global search skips the
object; mirror the title onto a stored text field, as below.

#### Searching by a related record's title — mirror the value

Expand Down
2 changes: 1 addition & 1 deletion content/docs/protocol/kernel/error-handling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ knows which object was addressed.

The [error catalog's `INVALID_FIELD` entry](/docs/api/error-catalog#invalid_field) carries
the authoritative cause text — it enumerates every read axis this one code answers on
(`select`, `expand`, `searchFields`, `groupBy`, `aggregations[].field`) and the
(`select`, `expand`, `search`, `searchFields`, `groupBy`, `aggregations[].field`) and the
off-request `backfillSummaryNulls` case.

### Resource Errors
Expand Down
6 changes: 5 additions & 1 deletion content/docs/protocol/objectql/query-syntax.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,11 @@ plus short-text/enum fields), so naming a field outside that set can never widen
search — and over the REST/protocol ingress it is `400 INVALID_FIELD` outright (#4254),
because the engine-side intersection alone used to drop the unknown name and fall back to
scanning the full searchable set. Internal callers reaching `engine.find()` directly keep
the tolerant intersection. Multiple whitespace-separated terms are AND-ed and
the tolerant intersection, but not a search with nothing to scan: when no
`searchableFields` entry names a stored field and the auto-default finds none
(`internal: true` fields never count), the engine itself refuses a non-blank `search`
with `400 INVALID_FIELD` (`param: 'search'`) on `find`, `findOne`, `count` and
`aggregate` alike, rather than answering every row. Multiple whitespace-separated terms are AND-ed and
fields are OR-ed. Case sensitivity comes from the operator the expansion emits, not
from the expansion: it emits `$icontains`, which ignores **ASCII case** by the
rule in [Case Sensitivity](#case-sensitivity) above. So a user typing `acme` does
Expand Down
2 changes: 1 addition & 1 deletion content/docs/ui/views.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ all.
| What you write on the view | `os validate` | Toolbar search at runtime |
| :--- | :--- | :--- |
| a subset of the allowed set | clean | scans exactly those columns |
| key omitted, or `searchableFields: []` | clean | scans the object's full allowed set |
| key omitted, or `searchableFields: []` | clean | scans the object's full allowed set — `400 INVALID_FIELD` when that set is empty |
| a renamed / mistyped column, or a dotted path | `searchable-field-unknown` | `400 INVALID_FIELD` |
| a real column outside the allowed set | `searchable-field-unsearchable` | `400 INVALID_FIELD` |
| a virtual `formula` column — no stored column to scan (#6674) | `searchable-field-unsearchable` | `400 INVALID_FIELD` |
Expand Down
Loading