Full-Text Search
Turso Extension: Full-text search (FTS) in Turso is powered by Tantivy, not SQLite’s FTS3/FTS4/FTS5 modules. The syntax and functions differ from SQLite FTS.
fts_match for filtering, fts_score for relevance ranking, and fts_highlight for displaying results with matched terms highlighted.
Creating FTS Indexes
FTS indexes are created with theUSING fts clause on CREATE INDEX. Each indexed column participates in the full-text search.
Tokenizer Configuration
Each column can use a different tokenizer via theWITH clause.
Available Tokenizers
| Tokenizer | Description | Use case |
|---|---|---|
default | Unicode-aware tokenizer with lowercasing and punctuation splitting (40-char limit) | General-purpose text search |
raw | No tokenization — matches the entire field value as a single token | IDs, UUIDs, tags, exact-match fields |
simple | Splits on whitespace and punctuation without lowercasing | Simple text without case-insensitive needs |
whitespace | Splits on whitespace only | Whitespace-delimited tokens |
ngram | Generates 2-3 character n-grams from the text | Autocomplete, substring matching |
| Input | default | raw | ngram |
|---|---|---|---|
Hello World | hello, world | Hello World | He, Hel, el, ell, ll, llo, … |
user-123 | user, 123 | user-123 | us, use, se, ser, … |
Field Weights
You can assign relative weights to indexed columns to influence the BM25 relevance score.| Parameter | Default | Description |
|---|---|---|
weights | 1.0 for all fields | Comma-separated column=weight pairs. Higher weights increase score contribution. |
Functions
fts_match
Returns 1 if the row matches the full-text query, or 0 otherwise. Used in theWHERE clause to filter rows.
| Parameter | Type | Description |
|---|---|---|
column1, column2, ... | TEXT | One or more columns covered by an FTS index |
query | TEXT | The search query string (see Query Syntax below) |
fts_match must correspond to columns in an existing FTS index. When Turso’s query planner detects fts_match in a WHERE clause, it routes the query through the FTS index for efficient lookup.
fts_score
Computes the BM25 relevance score for each matching row. Lower scores indicate higher relevance.| Parameter | Type | Description |
|---|---|---|
column1, column2, ... | TEXT | One or more columns covered by an FTS index |
query | TEXT | The search query string |
ORDER BY score ASC or ORDER BY score DESC depending on your preference for result ordering. When used in SELECT, the FTS index automatically provides scored results.
fts_highlight
Returns text with matching query terms wrapped in custom tags. Useful for displaying search results with visual emphasis on matched terms.| Parameter | Type | Description |
|---|---|---|
column1, column2, ... | TEXT | One or more text columns to highlight |
open_tag | TEXT | The tag to insert before each matching term (e.g. '<b>') |
close_tag | TEXT | The tag to insert after each matching term (e.g. '</b>') |
query | TEXT | The search query string |
Query Syntax
The query string passed tofts_match and fts_score supports Tantivy’s query parser syntax.
Basic queries
| Syntax | Example | Description |
|---|---|---|
| Single term | 'database' | Match rows containing “database” |
| Multiple terms (OR) | 'database search' | Match rows containing “database” OR “search” |
| Boolean AND | 'database AND search' | Match rows containing both terms |
| Boolean NOT | 'database NOT nosql' | Match “database” but exclude “nosql” |
Advanced queries
| Syntax | Example | Description |
|---|---|---|
| Phrase search | '"exact phrase"' | Match the exact phrase |
| Prefix search | 'data*' | Match terms starting with “data” |
| Column-specific | 'title:database' | Match “database” only in the title field |
| Boosting | 'title:database^2' | Boost matches in title by a factor of 2 |
Examples
Complete Example
This example walks through creating a table, adding an FTS index, inserting data, and running queries with scoring and highlighting.Index Maintenance
OPTIMIZE INDEX
Merges all Tantivy segments into a single optimized segment. This improves query performance and reduces storage overhead, particularly after bulk inserts.- After bulk data imports with many INSERT statements
- When query performance degrades over time
- During scheduled maintenance windows
- Flushes any pending documents to disk
- Merges all segments into a single segment
- Removes deleted document tombstones
- Invalidates internal caches for fresh reads
DML and Index Updates
FTS indexes are updated automatically when you modify the underlying table.| Operation | FTS behavior |
|---|---|
INSERT | New rows are indexed immediately (batched commits every 1000 documents) |
UPDATE | Implemented as DELETE + INSERT internally |
DELETE | Marks documents as deleted via tombstones, cleaned up on OPTIMIZE |
Comparison with SQLite FTS5
| Feature | SQLite FTS5 | Turso FTS |
|---|---|---|
| Filtering | WHERE t MATCH 'query' | WHERE fts_match(cols, 'query') |
| Ranking | bm25(t), rank column | fts_score(cols, 'query') |
| Highlighting | highlight(t, col_idx, open, close) | fts_highlight(cols, open, close, query) |
| Snippets | snippet(t, ...) | Not supported |
| Boolean operators | AND, OR, NOT | AND, OR, NOT |
| Phrase search | "exact phrase" | "exact phrase" |
| Prefix search | word* | word* |
| Column filter | col:term | col:term |
| Tokenizers | unicode61, ascii, porter | default, raw, simple, whitespace, ngram |
| Segment management | Automatic | Manual via OPTIMIZE INDEX |
| Transaction visibility | Immediate | After COMMIT |
Current Limitations
- No snippet function: Use
fts_highlightfor term emphasis; context snippets are not yet available. - No automatic segment merging: Use
OPTIMIZE INDEXperiodically after bulk writes. - No read-your-writes in a transaction: FTS changes within a transaction are not visible to queries until the transaction is committed. ROLLBACK correctly discards both table and FTS changes.
- No MATCH operator syntax: Use
fts_match()function calls instead ofWHERE table MATCH 'query'.
See Also
- CREATE INDEX for the full
CREATE INDEX ... USING ftssyntax - Vector Functions for similarity search with embeddings