SphinxSearch и MongoDB на примере HaipIT News

Приветствую, Земляне!

В данной статье я расскажу, как настроить поиск Sphinx таким образом, чтобы он мог выполнять индексацию базы данных Mongo (про работу с индексом через PHP код в другой раз).

Наверно многие из вас в курсе, что последние пару неделю я работаю над очередным своим проектом под названием HaipIT News, суть данного проекта - агрегация новостей из некоторых любимых мною источников. Проект пока ещё находится в стадии разработки. но уже много чего сделано, например API которое работает напрямую с базой данных MongoDB и ряд скриптов для импорта новостей из источников и частично завершённый Web-интерфейс.

Изначально для поиска я планировал реализовать связку MongoDB и ElasticSearch, это у меня получилось при помощи специального плагина. Наигравшись с ElasticSearch пришёл к выводу, что это не самая подходящая для меня система. Да, у ElasticSearch очень круто настраиваются словари, очень легко реализовать интеграцию в код, из коробки поддерживаются кластеры Mongo, но со временем Elastic стала очень странно себя вести, ps aux и service status показывали что всё нормально, но любая попытка сделать запрос возвращала null. После нескольких попыток заставить Elastic работать (restart в crontab помогал некоторое время), я разочаровался в этой идее и принялся искать более надёжное решение. И им, как не сложно догадаться, оказался Sphinx, у меня уже был большой опыт работы с данных сервером раньше, но я никогда не настраивал индексацию MongoDB.

Как показала практика, сделать это очень просто:

Sphinx (установить и настроить)

Для счастливых обладателей Debian-like дистрибутивов всё должно быть очень просто:

sudo apt-get install sphinxsearch

Дальше создадим конфиг, который будет запускать генератор XML:

sudo mcedit /etc/sphinxsearch/sphinx.conf

Запишем в него следующее

source src_mongo {
type = xmlpipe2
xmlpipe_command = /usr/bin/php /home/king/sphinx-index.php
}

index mongo {
morphology      = stem_enru
charset_type    = utf-8
source          = src_mongo
path            = /var/lib/sphinxsearch/data/mongo
}

indexer
{
mem_limit               = 128M
}

searchd
{
listen                  = 9312
listen                  = 9306:mysql41
log                     = /var/log/sphinxsearch/searchd.log
query_log               = /var/log/sphinxsearch/query.log
read_timeout            = 5
max_children            = 30
pid_file                = /var/run/sphinxsearch/searchd.pid
seamless_rotate         = 1
preopen_indexes         = 1
unlink_old              = 1
workers                 = threads # for RT to work
binlog_path             = /var/lib/sphinxsearch/data
}

Жирным шрифтом я выделил пару моментов:

  • xmlpipe2 - тип данного источника, для всех современных версий Sphinx стоит выбирать именно его, так как первая версия просто не работает
  • /home/king/sphinx-index.php - название и путь к скрипту генератору, это может быть любая программа или скрипт, главное чтобы данные которые он возвращает в процессе работы были в формате XML, php показан только как пример
  • mongo - название нашего индекса, оно понадобится нам в дальнейшем

Скрипт, для генерации XML

Создадим файл /home/king/sphinx-index.php:

mcedit /home/king/sphinx-index.php

Со следующим содержимым:

<?php
$filter = [];
$options = [];
$query = new MongoDB\Driver\Query($filter, $options);
$rows = $mongo->executeQuery('myDB.myCollection', $query);

echo '<?xml version = "1.0" encoding = "utf-8"?>';
?>
<sphinx:docset>

<sphinx:schema>
<sphinx:attr name="_id" type="string"/>
<sphinx:field name="content"/>
</sphinx:schema>

<?php
$i = 1;
foreach ($rows as $document) {
?>
<sphinx:document id="<?php echo $i ?>">
<_id><?php echo $document->_id ?></_id>
<content><![CDATA[[<?php echo $document->content ?>]]></content>
</sphinx:document>
<?php
$i++;
}
?>

</sphinx:docset>

Выполнение данного скрипта должно отображать нам что-то вроде:

<?xml version = "1.0" encoding = "utf-8"?>
<sphinx:docset>

<sphinx:schema>
<sphinx:attr name="_id" type="string"/>
<sphinx:field name="content"/>
</sphinx:schema>

<sphinx:document id="1">
<_id>5907466c6b02a304e855f47f</_id>
<content><![CDATA[[some text with html tags]]></content>
</sphinx:document>

............lot of results.............

</sphinx:docset>

Если Вы видите что-то подобное, значит всё хорошо и данные из монги выгружаются как надо, на самом деле это может быть какая угодно база данных (а может вы хотите индексировать файловую систему), важно только чтобы данные были в формате XML.

Выполним индексацию

Тут всё просто, для того чтобы выполнить индексацию нужно выполнить следующую команду:

sudo indexer mongo

Где mongo это название индекса, который мы прописали в конфиге /etc/sphinxsearch/sphinx.conf. В дальнейшем мы будем только обновлять индекс, следующей командой:

sudo indexer mongo --rotate

Я добавил её в крон, чтобы индексация происходила автоматически, без моего участия.

Завершение

Как видно из статьи, реализовать индексацию данных, находящихся в Mongo очень просто, работать с индексом так же просто как настраивать его. А высокая скорость работы будет радовать Вас ещё очень долго, если время отклика важно для Вас. Например моя база (на момент выхода данной заметки), содержит в себе около 10000 документов, размер индекса составляет около 10Мбайт, а время генерации индекса почти одна секунда, неплохой результат как по мне.

А на этом всё, напомню что в Discord есть сообщество программистов, в котором я принимаю активное участие, поэтому если у Вас возникнут вопросы, обращайтесь, не стесняйтесь, ну или в Twitter @EvilFreelancer например.

2 thoughts on “SphinxSearch и MongoDB на примере HaipIT News

  1. Вот Вы написали: "а может вы хотите индексировать файловую систему".
    Не подскажете как изменить XML, чтобы путь к файлу с именем файла (полное имя в общем) заносился в индекс и выдавался оттуда по запросу?

    Я занес его в атрибут:

    testing
    Проиндексировал, но получить не могу:
    $result = $sphinx->query('testing ', '*');

    Array ( [error] => [warning] => [status] => 0 [fields] => Array ( [0] => content ) [attrs] => Array ( ) [matches] => Array ( [3] => Array ( [weight] => 1 [attrs] => Array ( ) ) ) [total] => 2 [total_found] => 2 [time] => 0.000 [words] => Array ( [test3] => Array ( [docs] => 2 [hits] => 2 ) ) )

    Какой командой вытащить атрибут? Или нужно не в атрибут, а еще в один элемент Путь засунуть?

    1. Всё зависит от того какие данные Вы передаёте на вход Sphinx, есть лишь одно условие - они должны быть в формате XML, я бы наверно сделал xml поле в котором была бы необходимая для индекса информация (имя файла например). На самом деле упоминание файловой системы это отсылка к одному проекту в котором я участвовал, он как раз был про индексирование ФС, но не через Sphinx, а через C++ вызовы.
      PS. Прошу прощения за долгий ответ, только что разобрал весь спам в коментариях и обнаружил Ваше сообщение.

Comments are closed.