Задача
FULLTEXT поиск по коротким словам не возвращает записей.
Решение
Измените значение параметра минимальной длины слова для механизма индексирования.
Обсуждение
В тексте, подобном KJV, некоторые слова имеют особое значение, например «Бог» или «грех». Но если вы, работая с сервером MySQL 3.23, выполните FULLTEXT поиск этих слов в таблице kjv, то обнаружите любопытный результат – ни того, ни другого слова как будто никогда и не было в тексте:
SELECT COUNT(*) FROM kjv WHERE MATCH(vtext) AGAINST('God');
+------------+
| COUNT(*) |
+------------+
| 0 |
+------------+
mysql> SELECT COUNT(*) FROM kjv WHERE MATCH(vtext) AGAINST('sin');
+------------+
| COUNT(*) |
+------------+
| 0 |
+------------+
Одно из свойств индексатора – игнорирование «слишком общих» слов (то есть слов, присутствующих более чем в половине записей). Так, из индекса удаляются слова типа «the» и «and», но в данном случае мы имеем дело с чемто иным. Давайте сосчитаем общее количество записей и (при помощи шаблонов SQL) количество записей, содержащих каждое из слов:1
SELECT COUNT(*) AS 'total verses',
> COUNT(IF(vtext LIKE '%God%',1,NULL)) AS 'verses containing "God"',
> COUNT(IF(vtext LIKE '%sin%',1,NULL)) AS 'verses containing "sin"'
> FROM kjv;
+--------------+-----------------------------+----------------------------+
| total verses | verses containing "God" | verses containing "sin" |
+--------------+-----------------------------+----------------------------+
| 31102 | 4118 | 1292 |
+--------------+-----------------------------+----------------------------+
Ни одно из слов не присутствует более чем в половине стихов, так что полно текстовый поиск не удался не изза частого употребления слов. Причина в том, что по умолчанию в индексы не включаются слова, длина которых меньше четырех символов. Если вы работаете с сервером MySQL 3.23, то вам ничего не удастся с этим поделать (по крайней мере, ничего более простого, чем обращение к исходным текстам MySQL с их повторной компиляцией).
Но начиная с версии MySQL 4.0 минимальная длина слова является настраиваемым параметром, который можно изменить, задав переменную сервера ft_min_word_len. Например, чтобы включать в индекс слова, содержащие три и более символов, добавьте строку setvariable в группу
файла /etc/my.cnf (или другого файла, в котором вы храните настройки сервера):
[mysqld]
setvariable = ft_min_word_len=3
Сохраните изменения, перезапустите сервер и пересоздайте индекс FULLTEXT, чтобы новое значение вступило в силу:
[SQL]ALTER TABLE kjv DROP INDEX vtext;
ALTER TABLE kjv ADD FULLTEXT (vtext);
Давайте посмотрим, включает ли новый индекс короткие слова:
SELECT COUNT(*) FROM kjv WHERE MATCH(vtext) AGAINST('God');
+------------+
| COUNT(*) |
+------------+
| 3878 |
+------------+
mysql> SELECT COUNT(*) FROM kjv WHERE MATCH(vtext) AGAINST('sin');
+------------+
| COUNT(*) |
+------------+
| 389 |
+------------+
Так то лучше!
Но почему запрос с MATCH() находит 3878 и 389 записей, в то время как при веденный ранее запрос с LIKE нашел 4118 и 1292 записей? Поиск по образцу с помощью LIKE ищет соответствующие подстроки, а поиск FULLTEXT, осуществляемый MATCH(), ищет только целые слова.