четверг, 12 ноября 2009 г.

Поисковая экспедиция

Всем привет. Для нового апдейта моего проекта TrentaQuattro был необходим поиск. Дело крайне сложное и весьма мной нелюбимое — в принципе не люблю работать со строками, а особенно с регулярными выражениями, обязательно где-то облажаюсь. А если не облажаюсь, то всё равно облажаюсь.

Задача: организовать полнотекстовый поиск в MySQL в двух таблицах, в каждой из которых ищем только в одной строке. Иначе говоря — поиск среди постов.

Для начала я решил пройтись по гуглю — во-первых, как там это устроено (так сказать, взглянуть глазами девелопера, а не юзера), а во-вторых — поискать разных поисковых движков. Sphinx был слишком суров: мне не нужен атомный ледокол, если я ищу рыбацкую лодку. Яндекс-Сервер — уже не ледокол, это корабль Энтерпрайз из Стар Трека, что мне уж точно не нужно. Мои самые худшие опасения подтвердились, прийдется делать всё самому. Так, сперва нарисуем схему работы. Пользователь вводит поисковый запрос, он аяксом передается на сервер, там каленым железом и святой водичкой из запроса вытравливаются все кул-хацерские символы, потом поиск, отправка результатов браузеру в виде JSON. Вроде просто. Как мне тогда казалось.

Что из себя представляет JSON-ответ? Код ответа (нашлось, не нашлось, мало символов), количество найденного, количество показанного и сами результаты. Очень хотелось сделать поиск в один MySQL запрос, но выяснилось, что ничего не получится — нужно минимум два запроса: один поисковый, а второй похожим запросом считает общее количество найденного. Ну это-то просто, а вот как быть с самим поиском? Делать всё через like? Отчего же, давайте попробуем... Первый образец уже работал, когда я наткнулся на спецификацию родного полнотекстового поиска в MySQL — через match-against. Выглядело очень вкусно — сортировка по релевантности, низкое время выполнения запроса. На всякий случай забэкапив старый запрос, я быстренько составил новый. Потестировал в PHPMyAdmin — не работает. Почему? Ах, да. Таблицы в InnoDB, а он не поддерживает полнотекстовый поиск. Быстренько смотался в гугл, начал набирать "InnoDB..." и тут оно мне выдало возможные запросы, первым из которых — "InnoDB vs. MyISAM". В этот момент я выпал — именно это мне и было нужно, узнать о подводных камнях MyISAM, может, ну его нафиг. Но откуда гугл узнал, что я собираюсь искать именно это?.. Чудеса. Выяснив, что MyISAM ничего чудовищного в себе не содержит, я забэкапил базу данных вне очереди (обычно я делаю это по утрам) и перевел нужные таблицы на новый движок. Отлично, запрос работает... Но ничего не возвращает. Тут я запаниковал — вдруг этот поиск понимает только английские символы, а русские нет? Но пронесло. Выяснилось, что я выставил кодировку utf8_bin в полях body, где хранились тексты постов. А это значит, что регистр имеет значение ©. Следующий запрос уже выдал мне искомый результат.

Но тем не менее, после я понял, что родной полнотекстовый поиск меня совершенно не устраивает — порой не ищет самые обычные слова (ну скажите на милось, почему нельзя найти слово "хуй"?), зато чудесно ищет союзы и предлоги (хотя заявлено, что не ищет ничего, что короче трех символов). К тому же, отказывается искать то, что есть во многих постах. Короче говоря, пришлось мне вернуться к конструкции like.

Интересная эта штука, полнотекстовый поиск.