Найти тему

Как установить и настроить сфинкс


Скачиваем сфинкс (берем версию с 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 bytestotal 0.030 sec, 45854 bytes/sec, 129.34 docs/sec4 документа из базы проиндексировалисьОк, индекс создан в папочке 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.Autocomplete
Sphinx можно использовать для реализации автокомплита в поиске. т.е. дополнения по началу слова а также исправления опечаток (мануал
http://sphinxsearch.com/blog/2013/05/21/simple-autocomplete-and-correction-suggestion/ ).Realtime-индексы
Ок, у нас есть индекс для поиска. Но как обновлять его при добавлении новых данных или удалении старых? Ну с удалением просто, если 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

Raw

api-test.php

<?php error_reporting(-1);require __DIR__.'/api/sphinxapi.php'; $client = new SphinxClient(); // данные берем из конфига$client->SetServer('localhost', 9312);$client->SetConnectTimeout(1);$client->SetArrayResult(true); $result = $client->Query("черный");var_dump($result);

Raw

sphinx.conf

# Источник данных — MySQL# В конфиге источники могут наследоваться. создадим базовый конфиг для любых MySQL-источников# чтобы не копипастить данные для подключенияsource base{ type = mysql sql_host = localhost sql_user = tester sql_pass = password sql_db = tester sql_port = 3306 # Ставим кодировку при соединении sql_query_pre = SET NAMES utf8} # Теперь настроим источник данных для выборки новостей наследующийся от базовогоsource src_news: base{ # Включаем range (выборку больших таблиц по частям) sql_range_step = 1000 # запрос на выборку диапазона id sql_query_range = SELECT MIN(id), MAX(id) FROM news # запрос на выборку самих новостей для индексации # сфинкс понимает даты только в виде числа, так что преобразуем дату в timestamp sql_query = \ SELECT id, topic, header, body, UNIX_TIMESTAMP(added) AS added \ FROM news WHERE id BETWEEN $start AND $end # Сохраняем для каждой новости ее topic и дату в аттрибуты sql_attr_uint = topic sql_attr_timestamp = added} # Теперь создаем индекс из данных взятых из источникаindex index_news{ source = src_news # где хранить данные # не знаю, как писать относительный путь, потому пишу абсолютный path = d:/temp/s/data/news # где хранить аттрибуты — в индексе (inline) или отдельном файле (extern) docinfo = extern # Либо sbcs (1-байтовая кодировка) либо utf-8 charset_type = utf-8} # Говорим сколько памяти можно использовать при индексации (если недодать то будет ошибка)# объем памяти зависит от размера таблицы и опредеояется опытным путемindexer{ mem_limit = 20M} # настройки поискового демонаsearchd{ # на каких портах слушать с бинарным проткоолом listen = 9312 # и с mysql-протоколом listen = 9306:mysql41 # Куда класть логи log = d:/temp/s/log/searchd.log query_log = d:/temp/s/log/query.log read_timeout = 5 max_children = 30 pid_file = d:/temp/s/log/searchd.pid max_matches = 1000 seamless_rotate = 1 preopen_indexes = 1 unlink_old = 1 workers = threads # for RT to work binlog_path = d:/temp/s/data}

Raw

sql-test.php

<?phperror_reporting(-1);// номер порта берется из конфига сфикса а не наугад// Если вместо 127.0.0.1 написать localhost, то под линуксом PDO может приконнектиться к MySQL// вместо сфинкса через юникс-сокет, игнорируя указанный номер порта (MySQL использует // порт 3306 в отличие от сфинкса)// Мануал: http://php.net/manual/ru/ref.pdo-mysql.connection.php#refsect1-ref.pdo-mysql.connection-notes$pdo = new PDO('mysql:host=127.0.0.1;port=9306');$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Ищем слово «курс» в индексе новостей// мануал: http://sphinxsearch.com/docs/manual-2.2.1.html#sphinxql-select$stmt = $pdo->query("SELECT * FROM index_news WHERE MATCH('курс')");$results = $stmt->fetchAll();var_dump($results);