$search
The $search query option performs a free-text search across all string properties.
Syntax
GET /odata/Products?$search=widget
GET /odata/Products?$search="multi word search"The engine does not parse the OData $search grammar. It takes the raw value, removes one surrounding pair of matching quotes ("…" or '…'), and matches what remains as one literal substring. So the quotes are optional — they are the conventional way to write a term containing spaces, and removing them is all they do. A quote inside the term, or a lone one at its end (rolls'), stays part of it. "multi word search" searches for exactly that sequence of characters, spaces included; it is never split into words.
That one decision explains every limitation below: there are no operators to parse, no tokens to combine, and no relevance to rank.
How it works
The resolver:
- Inspects the entity type's declared properties
- Identifies all properties typed as
Edm.String - Escapes
%,_and!in the term, so they match themselves - Generates a
WHEREclause withLIKE '%term%' ESCAPE '!'for each string column - Combines them with
OR
For example, if the entity type has name (String), description (String), and price (Decimal), searching for widget produces:
WHERE (name LIKE '%widget%' ESCAPE '!' OR description LIKE '%widget%' ESCAPE '!')! is the escape character because it means the same in every supported database; a backslash in a SQL string literal does not.
The same implementation serves both read paths — a discoverModel() set and a custom SQL set search identically.
A string property the installation maps to an expression is searched by that expression instead of its column — see A property that is an expression.
since 3.2Interaction with $filter
$search and $filter are combined with AND. A row must match both the filter and the search term.
GET /odata/Products?$filter=price gt 10&$search=widgetWHERE price > 10 AND (name LIKE '%widget%' ESCAPE '!' OR description LIKE '%widget%' ESCAPE '!')Limitations
- Case sensitivity depends on the database collation.
- No boolean operators.
A AND Bis not parsed — it is searched for, literally, including the wordAND. - A quoted phrase matches contiguously, and only contiguously. There is no tokenisation, so word order and adjacency are fixed:
"red widget"does not find widget, red. There is no proximity search and no relevance ranking — rows come back in whatever order$orderbyasks for. - There are no wildcards.
%and_match themselves:50%finds50% off, not500 units. (Before 3.1.0 they reached SQL as wildcards.) - Searches only
Edm.Stringproperties — not navigation properties, and not enum, numeric or temporal columns.
For anything beyond this — stemming, ranking, operators — put a real search index in front of the service and expose its result as its own entity set.