Chapter 14 · Full-Text Search, R-Tree, Virtual Tables, and Extensions

FTS5 Fundamentals: Full-Text Tables, MATCH, and Tokenization

Build a small FTS5 search index, learn token-, phrase-, prefix-, Boolean-, and NEAR-style queries, and understand why tokenization makes full-text search fundamentally different from LIKE scans.

Beginner115–135 minutesFTS5 MATCH + tokenizer labSQLite 3.53.4 baselineFTS5 requiredLast reviewed: August 2026

Learning outcomes

Full-text search is not “LIKE, but faster.” FTS5 tokenizes documents, builds an inverted index from tokens to matching rows, and evaluates a dedicated query language. The lesson starts with a small searchable FieldNotes document set so every MATCH result can be reasoned about by hand.

01

Explain why a leading-wildcard LIKE search and a token-based full-text index solve different problems.

02

Create and populate a basic self-contained FTS5 virtual table with stable rowid identities.

03

Use MATCH for terms, phrases, prefixes, Boolean expressions, and NEAR groups.

04

Explain the default unicode61 tokenizer and why tokenization is part of search semantics.

05

Distinguish default content storage, external-content, and contentless choices conceptually.

06

Build and verify a small note/article search feature without assuming language stemming that was not configured.

The application problem: searchable maintenance knowledge

FieldNotes has short maintenance notes and operating articles. A user wants “cooling pump vibration,” phrase searches such as “bearing inspection,” and prefixes such as vibrat*. A query like WHERE body LIKE '%vibration%' can find a substring, but a normal B-tree index generally cannot turn an arbitrary leading wildcard into a full-text inverted index. It also lacks FTS concepts such as token phrases, document relevance, NEAR groups, and tokenizer configuration.

QuestionLIKE patternFTS5 MATCH
Find a literal substringNatural fit, often scan-oriented for %term%Token search unless a specialized trigram design is chosen
Find a phrase of tokensString pattern can approximate charactersNative phrase semantics after tokenization
Find token prefixAwkward pattern logicNative prefix syntax such as vibrat*
Rank by relevanceNot built inbm25/rank in Lesson 3
Control word splitting/case/diacriticsCollation/pattern semanticsTokenizer is explicit search policy

Confirm FTS5 exists before creating a table

sql · FTS5 capability check
SELECT sqlite_version();SELECT name FROM pragma_module_list WHERE name='fts5';SELECT compile_optionsFROM pragma_compile_optionsWHERE compile_options LIKE '%FTS5%';

Modern source builds commonly include FTS5, but an embedded host can omit it. If the module is missing, treat the lab as conceptual until you have an approved SQLite build; do not substitute untrusted binaries.

Create a small self-contained FTS5 table

The default FTS5 mode stores the supplied content as well as the full-text index. For this first lesson that simplicity is useful. We explicitly assign rowids so the search row identity remains stable and can later correspond to an application document id.

sql · search corpus
DROP TABLE IF EXISTS search_fts;CREATE VIRTUAL TABLE search_fts USING fts5(  title,  body,  tokenize='unicode61');INSERT INTO search_fts(rowid,title,body) VALUES(101,'Cooling pump vibration','Pump 7 shows rising vibration near the drive-end bearing.'),(102,'Bearing inspection','Inspect the cooling-water pump bearing and record vibration trend.'),(103,'Fan inspection due','Exhaust fan 14 requires belt and bearing inspection.'),(104,'Sensor calibration','Vibration sensor 3 calibration passed; verify MQTT telemetry.'),(105,'Pump seal leak','Cooling pump seal shows a small leak but vibration remains normal.');SELECT rowid,title FROM search_fts ORDER BY rowid;

The ordinary-looking rows are backed by FTS5's structures. Do not create a separate B-tree index on body; the virtual-table module owns its search index.

MATCH searches tokens, not arbitrary character fragments

sql · term and phrase queries
-- A token query.SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'vibration'ORDER BY rowid;-- A phrase: adjacent tokens in this order.SELECT rowid,title FROM search_ftsWHERE search_fts MATCH '"bearing inspection"'ORDER BY rowid;-- A prefix token query.SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'vibrat*'ORDER BY rowid;

With the default tokenizer, punctuation and whitespace help define tokens. A phrase is about token adjacency after tokenization—not a byte-for-byte substring contract. If an application needs arbitrary substring search, investigate an intentionally configured trigram tokenizer or another search design rather than assuming the default FTS index does it.

Boolean expressions and NEAR groups express search intent

FTS5 supports explicit AND, OR, and NOT, plus documented NEAR groups. Keep the syntax readable and parameterize the query string in application code; a search expression is still user-controlled input even though it is not ordinary SQL syntax.

sql · Boolean and proximity queries
SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'pump AND vibration'ORDER BY rowid;SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'pump NOT leak'ORDER BY rowid;SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'NEAR(pump bearing, 8)'ORDER BY rowid;SELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'sensor OR fan'ORDER BY rowid;

NEAR operates on token positions. It is useful when proximity carries meaning, but it is not a substitute for a domain-specific parser or semantic search model.

The tokenizer is part of your schema contract

FTS5's default unicode61 tokenizer follows Unicode 6.1 character categories. It performs case folding according to those rules and, by default, removes diacritics from Latin-script characters in the documented way. That is useful general-purpose behavior, but it is not a promise of language-aware stemming.

TokenizerWhat it doesProduction caution
unicode61Default Unicode 6.1 tokenization/case handling; configurable separators/categories/diacriticsTest real languages and punctuation used by your corpus.
asciiASCII-oriented case folding/token rulesNon-ASCII handling differs from unicode61.
porterWraps another tokenizer and applies Porter stemmingDesigned for English; do not advertise it as a multilingual stemmer.
trigramIndexes contiguous three-character sequences for substring-style searchDifferent index size/query behavior; test before choosing it.

Prefix indexes are optional acceleration, not the meaning of *

FTS5 can be configured with prefix indexes such as prefix='2 3' to accelerate some prefix queries. The * query operator still defines prefix semantics even without that configuration; the extra prefix indexes trade database size/write work for potentially faster prefix searches.

sql · optional prefix-index table
CREATE VIRTUAL TABLE search_prefix USING fts5(  title,  body,  tokenize='unicode61',  prefix='2 3');

Do not add prefix indexes “just in case.” Measure your actual query mix, corpus size, ingestion rate, and database growth.

Content-storage choices exist because indexing and content are separate concerns

The default FTS5 table stores content and index data together. An external-content FTS5 table stores the full-text index while fetching displayed columns from another table; keeping the two synchronized becomes your responsibility. A contentless design stores no retrievable content columns in the FTS table. Newer SQLite versions also support contentless-delete, which improves update/delete ergonomics for that specialized mode. Lesson 3 develops these tradeoffs only after basic MATCH behavior is solid.

Compare evidence: ordinary substring scan versus FTS MATCH

sql · plan comparison
CREATE TABLE ordinary_docs(  doc_id INTEGER PRIMARY KEY,  title TEXT NOT NULL,  body TEXT NOT NULL);INSERT INTO ordinary_docsSELECT rowid,title,body FROM search_fts;EXPLAIN QUERY PLANSELECT doc_id,title FROM ordinary_docsWHERE body LIKE '%vibration%';EXPLAIN QUERY PLANSELECT rowid,title FROM search_ftsWHERE search_fts MATCH 'vibration';

On a tiny five-row dataset timing is meaningless; the useful observation is that the first query is an ordinary-table pattern scan while the second delegates a MATCH constraint to the FTS5 virtual table. Scale tests belong on realistic corpora.

Lab: build a search-query matrix

Predict results before executing. Record which rows match each query and explain the result in terms of tokens, phrase order, prefix semantics, or Boolean logic.

sql · query-debugging matrix
SELECT 'term' AS kind,rowid,title FROM search_fts WHERE search_fts MATCH 'bearing';SELECT 'phrase',rowid,title FROM search_fts WHERE search_fts MATCH '"cooling pump"';SELECT 'prefix',rowid,title FROM search_fts WHERE search_fts MATCH 'calibrat*';SELECT 'boolean',rowid,title FROM search_fts WHERE search_fts MATCH 'pump AND normal';SELECT 'near',rowid,title FROM search_fts WHERE search_fts MATCH 'NEAR(sensor telemetry, 5)';

Then add one row containing punctuation, mixed case, and a Latin diacritic relevant to your application and document how your chosen tokenizer treats it. Search behavior should be tested as deliberately as schema constraints.

Verification checkpoint

FTS5 fundamentals checkpoint

Focus on the distinction between text matching and indexed token search.

  1. Why is LIKE %term% not equivalent to an FTS5 index?
  2. What does the FTS5 rowid represent in the examples?
  3. What is the difference between a phrase and a prefix query?
  4. What does unicode61 provide, and what does it not promise?
  5. Why might a prefix index be omitted even if prefix queries are supported?
  6. What new responsibility appears with external-content FTS5 tables?
Review the answers

LIKE can search arbitrary character patterns but does not create FTS token/relevance structures. rowid is the stable FTS document identity used here. Phrases require adjacent token sequences; prefix syntax matches token prefixes. unicode61 defines tokenization/case/diacritic behavior but is not a universal language stemmer. Prefix indexes cost storage/write work and should be measured. External-content tables make the application/schema responsible for keeping the FTS index synchronized with the content table.

Production judgment and bridge

FTS5 makes search semantics explicit: tokens, phrases, prefixes, and tokenizer policy. Lesson 3 adds the application-facing pieces—ranking, excerpts, external-content lifecycle, integrity checks, and index maintenance.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.