Sphinx, часть 1: Начало

Date December 11th, 2009 Author Vitaly Agapov

sphinxSphinx – это отличный полнотекстовый поисковый движок, который легко интегрируется в любое web-приложение и не требует особых усилий по установке и настройке. Распространяется он по лицензии GPL2 и в настоящее время поддерживает СУБД MySQL и PostgreSQL, а также работу с XML-файлами. Название Sphinx принято расшифровывать как SQL Phrase Index, а автором его является наш соотечественник Андрей Аксёнов.
Но хватит на этом теории. Посмторим, как приручить этого мифического зверя…

Чтобы дать наиболее адекватную и проверенную информацию, в качестве примера я опишу процесс разработки поиска для одного из своих проектов. Сразу оговорюсь, что в примере будет использоваться ОС Linux (в моём случае – Ubuntu, но это непринципиально), СУБД MySQL и web-приложение на Perl.

Установка
Скачать дистрибутив можно здесь: http://sphinxsearch.com/downloads.html.
Кроме исходников для компиляции в unix здесь же можно скачать и версию для Windows. На момент написания статьи последней стабильной версией является Sphinx 0.9.9 от 2 декабря 2009 года.
Качаем дистрибутив и делаем привычные вещи:

tar -xzvf sphinx-0.9.8.tar.gz
cd sphinx
./configure
make
make install

Перед тем, как начинать компиляцию, есть смысл обратить внимание на возможные параметры скрипта configure (полный список – ./configure –help). Во-первых, по умолчанию Sphinx установится в директорию /usr/local, что не очень удобно. Поэтому лучше указывать путь –prefix=/usr/local/sphinx, что поможет избежать путаницы в файлах.
Во-вторых, если планируется использовать драйверы PostgreSQL, то надо явно указывать необходимость поддержки этих драйверов ключом –with-pgsql (драйверы MySQL компилируются по умолчанию).

Если скрипт configure ругается на отсутствие заголовков mysql-devel, то их надо доустановить. В Ubuntu надо поставить следующие пакеты:

sudo apt-get install libmysqld-dev libmysqlclient15-dev libmysql++-dev

После установки в директории /usr/local/sphinx/bin (или /usr/local/bin, если не использовался ключ –prefix) появятся следующие бинарники:

  • indexer – утилита для создания индексов
  • indextool – утилита для получения сводной информации об индексе
  • search – тестовая утилита для проверки работы движка из командной строки
  • searchd – демон, предоставляющий API для доступа к индексу внешним приложениям
  • spelldump – утилита для кастомизации индекса с помощью словарей ispell или MySpell.

Установка завершена. Но до того, как можно будет пойти выпить пива, нужно еще выполнить несколько действий: сконфигурировать Sphinx, создать индекс, проверить его и, наконец, использовать Sphinx API в нашем web-приложении. Но прежде чем приступать к конфигурированию, посмотрим на нашу базу данных и определимся с тем, что мы хотим получить в качестве результата поиска.

База данных

В нашем примере у нас есть три таблицы:
book содержит информацию о книгах (название, аннотацию и год)
author содержит информацию об авторах (имя)
aob определяет их связь, то есть каждая её строка задаёт соответствие между первичными ключами из таблиц book и author

Нам требуется по введенному слову или словосочетанию находить как авторов, так и книги. При этом если в строке поиска ввести, например, фамилию автора и часть названия книги, то должна быть найдена соответствующая книга соответствующего автора и среди прочих найденных объектов должна находиться выше по релевантности.

Конфигурирование

Весь конфиг Sphinx находится в одном файле – sphinx.conf, располагающийся в /usr/local/sphinx/etc (или /usr/local/etc, опять же если не использовался ключ –prefix).
Вот конфиг для нашего случая:

source book
{
              # Параметры подключения к БД
              type                         = mysql
              sql_host                     = <host>
              sql_user                      = <mysql username>
              sql_pass                      = <mysql password>
              sql_db                        = <db name>
              sql_port                      = 3306

              sql_query_pre                  = SET NAMES utf8
              sql_query_pre                  = SET character_set_results=utf8

              sql_query                               = \
              SELECT book.id AS id, book.name AS name, book.year AS year, book.orig_name AS orig_name, \
              book.annotation AS annotation, GROUP_CONCAT(author.name) AS aname \
              FROM book LEFT JOIN aob ON book.id=aob.book_id \
              LEFT JOIN author ON aob.author_id=author.id GROUP BY book.id
              sql_attr_uint                 = year

              sql_ranged_throttle     = 0

              sql_query_info         = SELECT * FROM book WHERE id=$id
}

source author
{
              type                                    = mysql
              sql_host                                = localhost
              sql_user                                = booklist
              sql_pass                                = Grecto84
              sql_db                                  = booklist
              sql_port                                = 3306  # optional, default is 3306

              sql_query_pre                  = SET NAMES utf8
              sql_query_pre                  = SET character_set_results=utf8

              sql_query                               = \
              SELECT id, name, orig_name \
              FROM author

              sql_ranged_throttle     = 0
              sql_query_info         = SELECT * FROM author WHERE id=$id
}
index book_search
{
                # использовать блок source book
              source                  = book
                # путь, куда сохранять файл индекса
              path                    = /var/data/sphinx/book
              docinfo                 = extern
              mlock                   = 0

              morphology              = stem_enru
              min_word_len            = 1

              charset_type            = utf-8
                # маппинг символов, например U+401 (Ё) и U+451 (ё) приравниваются к U+435 (е)
              charset_table           = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F, U+401->U+435, U+451->U+435
              html_strip               = 0

index author_search
{
              source                  = author
              path                    = /var/data/sphinx/author
              docinfo                 = extern
              mlock                   = 0

              morphology              = stem_enru
              min_word_len            = 2

              charset_type            = utf-8
              charset_table           = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
              html_strip             = 0
}

indexer
{
              mem_limit                       = 32M
}
searchd
{
              listen                        = 3312
              log                           = /var/log/searchd.log
              query_log                     = /var/log/query.log

              read_timeout            = 5
              client_timeout          = 300
              max_children            = 30
              pid_file                  = /var/log/searchd.pid

              max_matches             = 1000
              seamless_rotate         = 1
              preopen_indexes         = 0
              unlink_old              = 1
              mva_updates_pool        = 1M
              max_packet_size         = 8M
              max_filters             = 256
              max_filter_values       = 4096
}

А теперь посмотрим в конфиг более пристально. Он состоит из нескольких блоков:

  • source определяет параметры источника данных, то есть из каких таблиц и какие поля мы должны выбрать в индекс.
  • index определяет параметры индексирования.
  • indexer определяет глобальные параметры для работы утилиты indexer.
  • searchd определяет параметры демона searchd

Блоков source и index может быть любое количество – в зависимости от наших потребностей. У нас этих блоков по паре: для индексации таблицы book и для индексации таблицы author.
Рассмотрим некоторые строки конфига подробнее.

С параметрами подключения к БД всё ясно. Дальше идут строки

              sql_query_pre                  = SET NAMES utf8
              sql_query_pre                  = SET character_set_results=utf8

Они определют запросы, которые будут выполнены сразу после подключения к БД перед началом индексации. В данном случае мы устанавливаем кодировку UTF8 для работы с базой данных.

Дальше идёт

        sql_query                               = \
                SELECT book.id AS id, book.name AS name, book.year AS year, book.orig_name AS orig_name, \
                        book.annotation AS annotation, GROUP_CONCAT(author.name) AS aname \
                FROM book LEFT JOIN aob ON book.id=aob.book_id \
                        LEFT JOIN author ON aob.author_id=author.id GROUP BY book.id

Это запрос для выборки данных для последующей индексации. Все поля, перечисленные в селекте, будут участвовать в полнотекстовом поиске.

Еще одно поле:

sql_attr_uint                 = year

Задаёт нетекстовое поле для индексации. В данном случае это целочисленное значение. По нему мы впоследствии сможем добавлять условия для поиска. Sphinx также может работать и с другими типами данных атрибутов (опции sql_attr_bool, sql_attr_bigint, sql_attr_timestamp, sql_attr_str2ordinal, sql_attr_float)

sql_ranged_throttle     = 0

Это поле задаёт задержку в миллисекундах между индексируемыми порциями данных. В данном случае указан 0, что означает отсутствие задержки. Однако при больших размерах базы данных и нежелании подвешивать её на время индексации, есть смысл загружать и индексировать данные порциями. Для этого потребуется в sql_query добавить условие WHERE id>=$start AND id<=$end, а также прописать опцию sql_range_step = 1000, в которой укажем размер порции, загружаемой за один раз.

Дальше:

        sql_query_info         = SELECT * FROM book WHERE id=$id

Это запрос, используемый утилитой search при вызове из командной строки. Утилита, находя в индексе подходящие строки, возвращает первичный ключ этой строки, а затем пользуется указанным здесь запросом для получения всех остальных полей строки. Вместо $id подставляется как раз найденный в индексе первичный ключ.

Индексация

Для того, чтобы проиндексировать базу данных, используем команду indexer:

indexer --config /usr/local/etc/sphinx.conf --all

Можно вместо –all указать конкретный индекс. А если запущен searchd и на время индексации останавливать его нежелательно, то нужно добавить ключ –rotate:

indexer --config /usr/local/etc/sphinx.conf -- rotate --all

Если ошибки не вылезли, то переходим к следующему шагу…

Тестирование индекса

Для поиска из командной строки используется утилита search:

search --config /usr/local/etc/sphinx.conf <search_text>

Если выводимые результаты похожи на правду, то запускаем поисковый демон…

Запуск сервера searchd

Запускаем сервер командой

searchd --config /usr/local/etc/sphinx.conf

Опять же, если нет ошибок, а ps waux | grep searchd показывает то, что надо, то радуемся – осалось совсем чуть-чуть.

Sphinx API для Perl

Об этом – статья «Sphinx, часть 2: Perl API»

Tags: , , ,
Category: Linux, MySQL, Web-dev | 2 Comments »

Comments

2 комментариев на “Sphinx, часть 1: Начало”

  1. agapoff.name | IT блог » Blog Archive » Sphinx, часть 2: Perl API

    […] прошлой статье «Sphinx: начало» я описал процедуру установки Sphinx и показал, как […]

  2. agapoff.name | IT блог » Blog Archive » Sphinx, часть 3. Real-Time индексы

    […] Sphinx, часть 1: Начало […]

Leave a comment

 Comment Form