Build a dialect-correct full-text-search WHERE-predicate SQL fragment.
Returns { $literal: sql } (see module header for the supported/verified
usage patterns for this shape).
Per-dialect behavior:
mysql/mariadb: MATCH(col1, col2) AGAINST('term' IN NATURAL LANGUAGE MODE | IN BOOLEAN MODE).
mode: 'phrase' uses boolean mode with the term wrapped in "..." for
an exact-phrase match (MySQL boolean-mode phrase syntax).
NOTE: requires a FULLTEXT index on the given column(s) — see
createFulltextIndex() in src/dialects/mysql/index.ts / mariadb/index.ts.
postgres/cockroachdb: to_tsvector('english', col1 || ' ' || col2) @@ plainto_tsquery('english', 'term').
mode: 'phrase' uses websearch_to_tsquery instead of plainto_tsquery
for more natural, phrase-aware parsing (supports quoted phrases, -,
OR). NOTE: works on any text column; a GIN index
(createFulltextIndex()) is only needed for performance, not correctness.
mssql: mode: 'natural' (default) uses FREETEXT(col, 'term');
mode: 'boolean' / mode: 'phrase' uses CONTAINS(col, 'term')
(phrase mode wraps the term in "...", which is exact-phrase syntax
for CONTAINS). NOTE: requires a full-text index/catalog — see
createFulltextIndex() in src/dialects/mssql/index.ts.
oracle: CONTAINS(col, 'term', 1) > 0 (Oracle Text). Multiple columns
are OR-ed together, since Oracle Text indexes and scores one column at
a time. NOTE: requires an Oracle Text index — see createFulltextIndex()
in src/dialects/oracle/index.ts.
sqlite: col MATCH 'term'. STRUCTURAL REQUIREMENT (unlike every other
dialect above): this only works against an FTS5 virtual table/column
(see createFulltextIndex() / createFTS5Table() in
src/dialects/sqlite/index.ts) — SQLite has no secondary full-text
index you can bolt onto an ordinary table the way MySQL/Postgres/MSSQL
do; the table itself must be an fts5 virtual table.
clickhouse: uses hasToken(col, 'term') for a single-token exact
match, or multiSearchAny(col, ['term1', 'term2', ...]) when
searchTerm contains multiple whitespace-separated words. Multiple
columns are OR-ed together. See createFulltextIndex() (ClickHouse
ngrambf_v1/tokenbf_v1 index) in src/dialects/clickhouse/index.ts.
db2: THROWS. Db2's optional Text Search feature (Net Search Extender)
has its own CONTAINS-like predicate syntax, but it could not be
verified with confidence here, and createFulltextIndex() in
src/dialects/db2/index.ts itself throws
"Fulltext indexes are not supported in Db2 LUW (requires Net Search
Extender)" — so there is no verified index-creation path to pair a
query against either. Throwing here rather than guessing avoids
shipping silently-wrong SQL.
snowflake: FALLBACK, not true full-text search. Snowflake has no
Postgres-style tsvector/tsquery full-text search primitive (its
closest offerings are Cortex Search / Search Optimization Service,
which are not plain-SQL predicates); createFulltextIndex() in
src/dialects/snowflake/index.ts is a documented no-op for the same
reason. Falls back to ILIKE '%term%' (case-insensitive substring
match — no tokenization, stemming, or relevance ranking).
redshift: FALLBACK, not true full-text search. Redshift (unlike
Postgres, which it forked from) does not implement tsvector/tsquery;
createFulltextIndex() in src/dialects/redshift/index.ts throws
accordingly. Falls back to ILIKE '%term%', same caveat as Snowflake.
Build a dialect-correct full-text-search WHERE-predicate SQL fragment.
Returns
{ $literal: sql }(see module header for the supported/verified usage patterns for this shape).Per-dialect behavior:
MATCH(col1, col2) AGAINST('term' IN NATURAL LANGUAGE MODE | IN BOOLEAN MODE).mode: 'phrase'uses boolean mode with the term wrapped in"..."for an exact-phrase match (MySQL boolean-mode phrase syntax). NOTE: requires aFULLTEXTindex on the given column(s) — seecreateFulltextIndex()insrc/dialects/mysql/index.ts/mariadb/index.ts.to_tsvector('english', col1 || ' ' || col2) @@ plainto_tsquery('english', 'term').mode: 'phrase'useswebsearch_to_tsqueryinstead ofplainto_tsqueryfor more natural, phrase-aware parsing (supports quoted phrases,-,OR). NOTE: works on any text column; a GIN index (createFulltextIndex()) is only needed for performance, not correctness.mode: 'natural'(default) usesFREETEXT(col, 'term');mode: 'boolean'/mode: 'phrase'usesCONTAINS(col, 'term')(phrase mode wraps the term in"...", which is exact-phrase syntax forCONTAINS). NOTE: requires a full-text index/catalog — seecreateFulltextIndex()insrc/dialects/mssql/index.ts.CONTAINS(col, 'term', 1) > 0(Oracle Text). Multiple columns are OR-ed together, since Oracle Text indexes and scores one column at a time. NOTE: requires an Oracle Text index — seecreateFulltextIndex()insrc/dialects/oracle/index.ts.col MATCH 'term'. STRUCTURAL REQUIREMENT (unlike every other dialect above): this only works against an FTS5 virtual table/column (seecreateFulltextIndex()/createFTS5Table()insrc/dialects/sqlite/index.ts) — SQLite has no secondary full-text index you can bolt onto an ordinary table the way MySQL/Postgres/MSSQL do; the table itself must be anfts5virtual table.hasToken(col, 'term')for a single-token exact match, ormultiSearchAny(col, ['term1', 'term2', ...])whensearchTermcontains multiple whitespace-separated words. Multiple columns are OR-ed together. SeecreateFulltextIndex()(ClickHousengrambf_v1/tokenbf_v1index) insrc/dialects/clickhouse/index.ts.CONTAINS-like predicate syntax, but it could not be verified with confidence here, andcreateFulltextIndex()insrc/dialects/db2/index.tsitself throws "Fulltext indexes are not supported in Db2 LUW (requires Net Search Extender)" — so there is no verified index-creation path to pair a query against either. Throwing here rather than guessing avoids shipping silently-wrong SQL.tsvector/tsqueryfull-text search primitive (its closest offerings are Cortex Search / Search Optimization Service, which are not plain-SQL predicates);createFulltextIndex()insrc/dialects/snowflake/index.tsis a documented no-op for the same reason. Falls back toILIKE '%term%'(case-insensitive substring match — no tokenization, stemming, or relevance ranking).tsvector/tsquery;createFulltextIndex()insrc/dialects/redshift/index.tsthrows accordingly. Falls back toILIKE '%term%', same caveat as Snowflake.