$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, strips surrounding quotes, 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 stripping them is all they do. "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 - Generates a
WHEREclause withLIKE '%term%'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%' OR description LIKE '%widget%')The same implementation serves both read paths — a discoverModel() set and a custom SQL set search identically.
Interaction 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%' OR description LIKE '%widget%')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. %and_in the term reach SQL as wildcards. Searching for50%matches any row whose text starts with50, anda_bmatchesaxb. The term is parameter-bound, so this is a matching surprise and not an injection, but it is a surprise.- 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.