Приветствую, Земляне!
В данной статье я расскажу, как настроить поиск 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 например.
Вот Вы написали: "а может вы хотите индексировать файловую систему".
Не подскажете как изменить 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 ) ) )
Какой командой вытащить атрибут? Или нужно не в атрибут, а еще в один элемент Путь засунуть?
Всё зависит от того какие данные Вы передаёте на вход Sphinx, есть лишь одно условие - они должны быть в формате XML, я бы наверно сделал xml поле в котором была бы необходимая для индекса информация (имя файла например). На самом деле упоминание файловой системы это отсылка к одному проекту в котором я участвовал, он как раз был про индексирование ФС, но не через Sphinx, а через C++ вызовы.
PS. Прошу прощения за долгий ответ, только что разобрал весь спам в коментариях и обнаружил Ваше сообщение.