Скачиваем сфинкс (берем версию с MySQL и со стеммингом на 15 языков Win32 binaries w/MySQL+PgSQL+libstemmer+id64 support соответствующую битности твоей ОС), распаковываем например в d:\temp\s\
На этом установка sphinx завершена. В дебиане просто делаем sudo apt-get install sphinxsearch
.
Создаем таблицы:
CREATE TABLE news
(id INT(10) AUTO_INCREMENT PRIMARY KEY, topic INT(10) NOT NULL, header VARCHAR(200) NOT NULL,
body TEXT, added TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
Вбиваем данные:
INSERT INTO news (topic, header, body) VALUES
(1, 'Биржевой курс евро приблизился к 46 рублям',
'Курс евро во второй половине торгов на Московской бирже 21 января приблизился к 46 рублям. К 17:00 по московскому времени европейская валюта подорожала почти на 18 копеек до 45,9795 рубля. Курс доллара к тому же времени вырос на 21 копейку до 33,98 рубля.'),
(1, 'Найденный в ЮАР алмаз оценили в 15 миллионов долларов',
'В Южной Африке нашли голубой алмаз, который предварительно оценен в 15-20 миллионов долларов. За эту сумму, по мнению экспертов, камень может быть продан на аукционе, сообщает Reuters.'),
(2, 'В Петербурге исчез музей-квартира Ленина',
'Партия «Коммунисты России» потребовала, чтобы власти Санкт-Петербурга восстановили музей, действовавший в последней конспиративной квартире Владимира Ленина. Об этом 21 января сообщается на сайте партии.'),
(1, 'Hello world', 'Test news for search');
Пишем конфиг для индексера например в d:\temp\s\sphinx.conf на основе sphinx-min.conf.in, мануала и интуиции (файл sphinx.conf
, приложен ниже).
В конфиге упомняуты папки log и data, так что созадем их в d:\temp\s\
Аттрибуты (added
и topic
) — это числа, которые можно сохранить в поисковый индекс и потом использовать их в условии поиска (для поиска в определенной теме или сортировки по дате).
Уф. Тяжело потрудились. Попробуем теперь запустить индексер, открываем консоль (гайд по использованию консоли https://gist.github.com/codedokode/586dabb540415e0cc3d3 ), переходим в папку сфинкса и печатаем:
.\bin\indexer --config sphinx.conf --all
(под линуксом печатаем просто indexer так как там он прописан в системную папку). Если все ОК, выведется текст, и там мы увидим:
indexing index 'index_news'...
WARNING: collect_hits: mem_limit=20480 kb too low, increasing to 25856 kb
Мало памяти, надо минимум 25 Мб (и зачем ему столько?)
total 4 docs, 1418 bytes
total 0.030 sec, 45854 bytes/sec, 129.34 docs/sec
4 документа из базы проиндексировались
Ок, индекс создан в папочке data, проверим, работает ли поиск? Набираем
.\bin\search --config sphinx.conf "курс"
Вот так печалька, ничего не найдено. Еще бы, виндовая консоль не умеет в utf-8 и коверкает наши буковки. Попробуем английский:
.\bin\search --config sphinx.conf "hello"
Сфинкс вернул нам id новости, отлично. Теперь запустим поисковый демон и перейдем к PHP (и к поддержке utf-8). Запускаем демон:
.\bin\searchd --config sphinx.conf --console
(--console
чтобы он не пытался уйти в фоновый режим и его можно было остановить через Ctrl + C
). Появятся надписи что все хорошо. Из PHP к сфинксу можно подсоединиться 2 способами:
- через mysql-совместимый протокол подсоединиться к демону как к БД и искать с помощью SQL-запросов
- использовать sphinx.api.php и бинарный протокол
Начнем с варианта 1. Пишем test-sql.php
(код в приложении).
Запускаем его, из консоли или через браузер и сервер (демон поиска должен быть запущен естественно). Если все верно, в ответ вернется массив вроде такого, с id документа и его аттрибутами:
array(1) {
[0] =>
array(6) {
'id' =>
string(1) "1"
[0] =>
string(1) "1"
'topic' =>
string(1) "1"
[1] =>
string(1) "1"
'added' =>
string(10) "1390318498"
[2] =>
string(10) "1390318498"
}
}
Перейдем к sphinx.api.php (мануал по апи: http://sphinxsearch.com/docs/manual-2.2.1.html#api-reference ). Берем пример кода из папки api и пишем на его основе код api-test.php
(приложен ниже).
Запускаем, тоже видим массив с результатами. Хорошо, когда все работает.
Если мы сейчас попробуем поискать слово с учетом склонения, например «курсом» то ничего не найдем, так как в тексте нет таких слов. Нехорошо. Давай исправлять это при помощи встроенных возможностей сфинкса.
В сфинксе есть такие возможности преобразования слов при индексировании: lemmatizer (приводит слово в нормальную форму: running -> run), stemmer (отрезает окончания слов, не особо заботясь о логике, business -> busi но зато простой и не требует словаря), фонетические алгоритмы (soundex, metaphone заменяют похожие звуки в слове для поиска с неправильным написанием слова но только в английских словах). Мануал: http://sphinxsearch.com/docs/manual-2.2.1.html#conf-morphology
Подключим русский и англ. стеммер, прописав в конфиге в секции index_news:
morphology = stem_ru, stem_en
Индекс сам себя не обновит, так что останавливаем демон (Ctrl + C), запускаем переиндексацию и перезапускаем демон (когда демон работает в фоновом режиме, можно дописать ключ --rotate
и индексер сам попросит демона перезагрузить индексы, при этом работа поиска не прервется):
.\bin\indexer --config sphinx.conf --all
Теперь попробуем поискать по слову «курса» через PHP-скрипт. Все должно работать.
Лемматайзер конечно работает качественнее, так что лучше скачать нужные словари и подключить его.
Сфинкс умеет еще вырезать HTML-теги и декодировать HTML-сущности (параметр html_strip
), задавать диапазон символов (charset_table
), исключения и синонимы (wordforms
и exceptions
).
Можно задать, какие символы являются и не являются частью слова (например чтобы слова разделенные подчеркиванием считались как разные).
Также, сфинкс может строить сниппеты в результатах поиска (вырезать куски текста рядом с подсвеченным ключевым словом), с помощью функции BuildExcerpts()
в API.
Sphinx можно использовать для реализации автокомплита в поиске. т.е. дополнения по началу слова а также исправления опечаток (мануал http://sphinxsearch.com/blog/2013/05/21/simple-autocomplete-and-correction-suggestion/ ).
Ок, у нас есть индекс для поиска. Но как обновлять его при добавлении новых данных или удалении старых? Ну с удалением просто, если sphinx вернул нам id несуществующей в таблице записи — значит, она удалена. А как добавлять данные в индекс?
Для этого используются realtime-индексы. Rt индексы можно обновлять с задержкой в несколько миллисекунд (по крайней мере так говорят авторы), но они не очень эффективно хранят данные и много данных в них лучше не помещать. То, есть мы делаем так:
- основной индекс, содержит все записи (или все записи старше определенной даты), полностью перестраивается раз в час/в сутки в заивисимости от объема данных
- RT-индекс, который хранит только новые данные, которых нет в основном индексе, который обновляется в реальном времени и который очищается при переиндексации
Добавим RT-индекс в конфиг:
index rt_news
{
type = rt
path = d:/temp/s/data/rt_news
# Надо описывать все возможные поля для индексирования
rt_field = header
rt_field = body
rt_attr_uint = added
rt_attr_uint = topic
# Сколько памяти отведем под индекс (что не поместится, будет храниться на диске)
rt_mem_limit = 25M
}
Ок, теперь перезапускаем индексер и демон. Добавление в Rt-индекс делается SQL-запросами, так что возьмем sql-test.php и чуть-чуть переделаем:
$st = $pdo->prepare("INSERT INTO rt_news (id, topic, header, body, added) VALUES (?, ?, ?, ?, ?)");
$st->execute(array(100, 1, 'Проверим realtime index', 'Это текст проверки rt индекса', time()));
Запустим наш файл. Теперь поиском убедимся, что поиск по слову «проверим» и «realtime» выдает нам результаты. Если ты ищешь через SQL, а не через api, не забудь указать в запросе оба индекса (SELECT ( FROM index_news, rt_news ...
)).
Индекс можно обновить через вставку новой записи с таким же id помощью REPLACE, проверим:
$st = $pdo->prepare("REPLACE INTO rt_news (id, topic, header, body, added) VALUES (?, ?, ?, ?, ?)");
$st->execute(array(100, 1, 'Черный кот в черном доме', 'здесь ничего нет', time()));
Убедимся, что теперь ищется слово кот, а по слову realtime резальтотов нет.
Ну и можно еще удалять записи из RT-индекса, с помощью DELETE FROM rt_news WHERE id IN(1, 2, 3)
Sphinx понимает хитрый синтаксис в поисковых запросах ( http://sphinxsearch.com/docs/manual-2.2.1.html#searching ):
- оператор ИЛИ:
слово1 | слово2
- оператор НЕ:
слово1 -слово2
- скобки для группировки
- и мнгого других опций, которые есть в мануале
Можно сортировать и группировать результаты по разным аттрибутам.
Вот неплохая статья на русском про то, как sphinx обрабатывает слова в тексте и некоторые настройки для этого: http://chakrygin.ru/2013/07/sphinx-search.html