Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як працює повнотекстовий пошук у MySQL і чому він погано шукає короткі слова й кирилицю?

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.

Докладніше в документації: MySQL: повнотекстовий пошук

Схожі питання