Full-text search
FULLTEXT indexes, MATCH … AGAINST in natural-language and boolean modes, relevance and limits.
Searching text with LIKE '%word%' works on small tables, but it scans every row, can't rank results by relevance and can't handle "this word but not that one". A FULLTEXT index splits text into words and indexes each one, so word searches stay fast on millions of rows and results come back ranked by relevance.
Creating a FULLTEXT index#
You can also add one later: ALTER TABLE articles ADD FULLTEXT INDEX ft_body (body);. FULLTEXT works on CHAR, VARCHAR and TEXT columns, in InnoDB and MyISAM.
Natural-language search#
MATCH(columns)must list exactly the columns of a FULLTEXT index, in any order.AGAINST('words')with no modifier is natural-language mode. Rows that contain any of the words match, and rarer words and more occurrences give higher scores.- In the
WHEREclause, results are automatically sorted by relevance. Selecting theMATCHexpression shows the score, and MySQL computes it only once.
A multi-word query matches rows with any of the words:
Boolean mode: precise control#
IN BOOLEAN MODE adds operators:
Boolean mode is what you usually want behind a search box. Translate user input into +word* terms so every word is required and partial words still match.
Query expansion#
WITH QUERY EXPANSION runs the search twice: first normally, then again adding words from the top results. It can find related documents that don't contain the original word, but it often adds noise. Use it for short queries, and test it.
What gets indexed: tokens, stopwords and length#
The full-text parser splits text on spaces and punctuation. Two rules surprise people:
- Minimum word length. InnoDB ignores words shorter than
innodb_ft_min_token_size(default 3), sogo,aianddbaren't indexed. MyISAM's default (ft_min_word_len) is 4. - Stopwords. Very common words (
the,and,it,about, …) are skipped. See the default list withSELECT * FROM information_schema.INNODB_FT_DEFAULT_STOPWORD;.
Changing innodb_ft_min_token_size requires a server restart and rebuilding the FULLTEXT indexes. You can also supply a custom stopword table (innodb_ft_server_stopword_table). Searches are case-insensitive with the usual _ci collations.
CJK and other languages
The default parser assumes spaces between words. For Chinese, Japanese and Korean, use the built-in ngram parser:
Combining full-text with normal filters#
MATCH combines freely with other conditions, joins and pagination:
Limitations, and when to use a search engine#
MySQL full-text search is great for "search the blog / help centre / product names" features without extra infrastructure. It doesn't offer:
- typo tolerance or fuzzy matching
- stemming (
runvsrunning) or synonyms, beyond what prefix search gives you - facets, highlighting, or advanced relevance tuning
When you need those, or are searching across many millions of large documents, use a dedicated engine such as OpenSearch/Elasticsearch, Meilisearch or Typesense, fed from MySQL. Even then, MySQL full-text search is often the right first version.
Common mistakes#
MATCH(title)when the index is on(title, body)fails with ERROR 1191: Can't find FULLTEXT index matching the column list. Create an index for each column combination you search.- Expecting results for short words or stopwords.
- Bulk-loading large tables with the FULLTEXT index in place. Loading first and then adding the index is much faster.
- Deleted rows linger in the index until
OPTIMIZE TABLE(withinnodb_optimize_fulltext_only=ON) cleans them up, which matters for tables with heavy churn.
What's next#
Finally, you'll learn how MySQL scales and stays available: replication and the core performance settings every MySQL admin should know.
Check your understanding
Quick quiz
1.Why is
WHERE body LIKE '%replication%'slow on a big table?2.In BOOLEAN MODE, what does
+mysql -oraclemean?3.Why might a search for the word 'it' return nothing even though many rows contain it?
Finished reading?
Mark this lesson complete to track your progress.