LIKE '%слово%' не використовує індекс і читає всю таблицю. FULLTEXT-індекс розбиває текст на слова й будує інвертований індекс «слово → рядки».
ALTER TABLE posts ADD FULLTEXT INDEX ft_posts (title, body);
SELECT id, title, MATCH(title, body) AGAINST ('черги laravel') AS score
FROM posts
WHERE MATCH(title, body) AGAINST ('черги laravel')
ORDER BY score DESC;
Колонки в MATCH мають точно збігатися з колонками індексу.
Режими пошуку:
| Режим | Що робить |
|---|---|
IN NATURAL LANGUAGE MODE (за замовчуванням) |
ранжує рядки за релевантністю |
IN BOOLEAN MODE |
оператори: +обов'язкове -виключене "точна фраза" префікс* |
WITH QUERY EXPANSION |
другий прохід зі словами з найкращих результатів, часто шумний |
WHERE MATCH(title, body) AGAINST ('+laravel -symfony черг*' IN BOOLEAN MODE)
Чому щось «не знаходиться»:
- мінімальна довжина слова: для InnoDB
innodb_ft_min_token_size= 3 - слова з двох літер («ІТ», «UI») не індексуються. Зміна потребує перезапуску сервера й перебудови індексів; - стоп-слова: InnoDB має короткий вбудований англійський список; власний задають через
innodb_ft_server_stopword_table; - поріг 50% (слово в половині рядків вважається шумом) стосується лише MyISAM у природному режимі - через нього на маленьких тестових таблицях MyISAM пошук «нічого не знаходить».
Кирилиця й морфологія. Стандартний парсер розбиває текст за пробілами й розділовими знаками, тож кирилиця індексується, але без морфології: «черга», «черги», «чергу» - різні слова. Допомагає пошук за префіксом у булевому режимі (черг*), але відмінки з чергуванням літер він не покриває.
Парсер ngram (WITH PARSER ngram) розбиває текст на послідовності з N символів (ngram_token_size, за замовчуванням 2). Створений для китайської, японської й корейської, де немає пробілів; для української дає частковий збіг, але індекс великий і результати менш релевантні.
У Laravel:
$table->fullText(['title', 'body']); // міграція
Post::whereFullText(['title', 'body'], 'черги laravel')->get();
Post::whereFullText(['title', 'body'], '+laravel черг*', ['mode' => 'boolean'])->get();
Коли FULLTEXT достатньо: простий пошук по сайту, адмінка, невеликі обсяги. Коли потрібні морфологія, стійкість до опечаток, фасети й підсвічування - окремий рушій (Meilisearch, Typesense, Elasticsearch) через Laravel Scout.