"Supabase Full Text Search Substring Matches: FTS Guide"

"Why supabase full text search substring matches fail: tsvector, GIN indexes, websearch_to_tsquery, ts_rank ranking, pg_trgm fuzzy search. Full code."

You type "flut" into your app's search box, expecting "Flutter" results. Nothing comes back. You try the full word "flutter" — results appear. This is the moment most developers discover that supabase full text search substring matching doesn't work the way they assumed: Postgres full-text search matches whole lexemes ("flutter"), not fragments ("flut"). In this tutorial you'll build proper full-text search on Supabase with Postgres: a generated tsvector column, GIN indexes, websearch_to_tsquery queries from Flutter via .textSearch() , relevance ranking with ts_rank , and — for the "flut" problem — pg_trgm fuzzy search. Runnable SQL + Dart throughout. Last updated: October 3, 2026 What you'll learn Why full-text search misses substrings and partial words Generated tsvector columns + GIN indexes (the fast path) Keeping the search index fresh with tsvector_update_trigger Querying from Flutter with .textSearch() and websearch type Ranking results by relevance with ts_rank Fuzzy/substring search with pg_trgm for typo tolerance websearch_to_tsquery vs plainto_tsquery vs to_tsquery Why Supabase Full Text Search Substring Matching Fails Postgres full-text search doesn't compare strings — it compares lexemes . When you index "Flutter tutorial", Postgres stems it into tokens like flutter and tutori . A query for flut produces the lexeme flut , which matches nothing, because flut is not a lexeme in the index. Full-text search answers "does this document contain these words" — not "does this text contain this substring." That means two different tools for two different jobs: Full-text search ( tsvector / tsquery ): "find documents about flutter" — fast, ranked, language-aware. Substring/fuzzy search ( pg_trgm , LIKE ): "find text containing…

Explore