DEV Community

kanta13jp1
kanta13jp1

Posted on

Supabase Full-Text Search — PostgreSQL tsvector, Multilingual Support, and Index Optimization

Supabase Full-Text Search — PostgreSQL tsvector, Multilingual Support, and Index Optimization

Implement serious full-text search in Supabase. English and multilingual configurations explained.

Basics: tsvector Column and Index

-- Full-text search column for English
ALTER TABLE articles ADD COLUMN search_vector tsvector
  GENERATED ALWAYS AS (
    to_tsvector('english', coalesce(title, '') || ' ' || coalesce(content, ''))
  ) STORED;

CREATE INDEX articles_search_idx ON articles USING GIN(search_vector);
Enter fullscreen mode Exit fullscreen mode
// Search from Flutter
final results = await supabase
  .from('articles')
  .select('id, title, content')
  .textSearch('search_vector', query, config: 'english')
  .limit(20);
Enter fullscreen mode Exit fullscreen mode

Multilingual Full-Text Search with pgroonga

-- Supabase supports the pgroonga extension
CREATE EXTENSION IF NOT EXISTS pgroonga;

CREATE INDEX articles_pgroonga_idx ON articles
  USING pgroonga(title, content)
  WITH (tokenizer='TokenNgram');  -- N-gram tokenizer for CJK languages

-- Search query
SELECT id, title,
  pgroonga_score(tableoid, ctid) AS score
FROM articles
WHERE title &@~ 'Flutter performance'
   OR content &@~ 'Flutter performance'
ORDER BY score DESC
LIMIT 20;
Enter fullscreen mode Exit fullscreen mode

Search Results with Highlights

-- Highlight matching passages (English)
SELECT
  id,
  title,
  ts_headline(
    'english',
    content,
    plainto_tsquery('english', 'Flutter performance'),
    'StartSel=<mark>, StopSel=</mark>, MaxWords=30'
  ) AS excerpt
FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'Flutter performance');
Enter fullscreen mode Exit fullscreen mode

Flutter Implementation (via RPC)

Future<List<SearchResult>> searchArticles(String query) async {
  final results = await supabase.rpc('search_articles', params: {
    'p_query': query,
    'p_limit': 20,
  });
  return (results as List)
      .map((r) => SearchResult.fromJson(r as Map<String, dynamic>))
      .toList();
}
Enter fullscreen mode Exit fullscreen mode

Summary

English search      → tsvector GENERATED ALWAYS + GIN index
Multilingual        → pgroonga extension + N-gram tokenizer
Highlights          → ts_headline() to emphasize matches
Query types         → plainto_tsquery (natural language) / to_tsquery (with operators)
Enter fullscreen mode Exit fullscreen mode

Supabase full-text search delivers Elasticsearch-class capabilities with no extra service.

Top comments (0)