Использование системы полнотекстового поиска Sphinx с поддержкой морфологии позволяет добиться высокой скорости индексации и поиска, а также снижения потребления ресурсов сервера.
Sphinx доступен на большинстве платформ, см. Getting started.
Таблицы в Sphinx 3 создаются динамически, ручное указание rt-индексов в конфигурационном файле не требуется. В секции searchd настройте опцию listen, которая задается в формате listen = ( address ":" port | port | path ) [ ":" protocol ]
searchd
{
listen = localhost:9312:mysql41
...
}
Далее перезагрузите searchd и проверьте наличие соединения:
/etc/init.d/searchd restart
Проверьте корректность соединения с Sphinx:
mysql -h 127.0.0.1 -P 9312
В конфигурационном файле модуля поиска в секции default укажите использования драйвера sphinx и задайте параметры:
<?php
return array (
'default' => array(
'driver' => 'sphinx3',
'database' => 'sphinx',
),
'modules' => array(
// ...
)
);
В конфигурационном файле баз данных добавьте параметры нового соединения sphinx, указанного выше в опции database:
<?php
return array(
'default' => array(
// ...
),
'sphinx' => array(
'driver' => 'pdo',
'host' => '127.0.0.1:9312',
'database' => NULL
)
);
Sphinx доступен на большинстве платформ, исчерпывающая информация доступна на сайте производителя - установка на Debian и Ubuntu, установка на CentOS и RedHat, установка на Windows.
В конфигурационный файл sphinx.conf внесите новый индекс, в данном примере он называется hostcms:
index hostcms {
type = rt
path = /var/lib/sphinx/hostcms
rt_mem_limit = 64M
morphology = stem_enru, soundex
rt_field = title
rt_field = text
rt_attr_string = title
rt_attr_timestamp = datetime
rt_attr_string = url
rt_attr_uint = size
rt_attr_uint = inner
rt_attr_multi = siteuser_group_id
rt_attr_uint = module
rt_attr_uint = module_id
rt_attr_uint = module_value_type
rt_attr_uint = module_value_id
rt_attr_uint = site_id
}
Путь размещения индекса path может варьироваться в зависимости от используемой ОС. Не забудьте проверить соответствие владельцев директории, указанной в пути, и пользователя, от которого работает searchd.
В секции searchd настройте опцию listen, которая задается в формате listen = ( address ":" port | port | path ) [ ":" protocol ]
searchd
{
listen = localhost:9312:mysql41
...
}
Далее перезагрузите searchd и проверьте наличие соединения:
/etc/init.d/searchd restart
Проверьте корректность соединения с Sphinx:
mysql -h 127.0.0.1 -P 9312
В конфигурационном файле модуля поиска в секции default укажите использования драйвера sphinx и задайте параметры:
<?php
return array (
'default' => array(
'driver' => 'sphinx',
'database' => 'sphinx',
'index' => 'hostcms'
),
'modules' => array(
// ...
)
);
В конфигурационном файле баз данных добавьте параметры нового соединения sphinx, указанного выше в опции database:
<?php
return array(
'default' => array(
// ...
),
'sphinx' => array(
'driver' => 'pdo',
'host' => '127.0.0.1:9312',
'database' => NULL
)
);
В случае изменения конфигурационного файла sphinx.conf мы рекомендуем остановить searchd, удалить файлы индекса (в данном примере файлы hostcms* в /var/lib/sphinx) и запустить searchd.
Морфологические словари позволяют улучшить качество поиска, т.к. вместо нахождения основы слова используется нормальная словарная форма.
| Слово | Стеммер | Словарь |
|---|---|---|
| Диплом | Дипл | Диплом |
| Диплома | Диплом | Диплом |
| Груша | Груш | Груша |
Создайте директорию /etc/sphinx/dic/ и загрузите в нее требуемые словари.
В конфигурационный файл sphinx.conf внесите следующие изменения:
index hostcms {
...
#morphology = stem_enru, soundex
morphology = lemmatize_ru_all, lemmatize_en_all
...
}
indexer
{
...
lemmatizer_cache = 64M
}
common
{
lemmatizer_base = /etc/sphinx/dic/
}
Перезагрузите searchd и проверьте работу:
/etc/init.d/searchd restart
В случае необходимости вносить изменения в результаты поиска на основе собственных весов, например, разместить товары, которых нет в наличии, в самом конце поисковой выдачи, вы можете использовать собственное поле, в которое будете вносить вес, а затем задать формулу расчета веса при поиске.
Внесем в конфигурационный файл после после rt_attr_uint = site_id добавим новое поле rt_attr_float = myweight (также см. выше "Удаление индекса при внесении изменений в sphinx.conf").
В приведенном ниже примере для товаров, которые отсутствуют на складе, устанавливается myweight в 0, а для товаров в наличии 1. Затем используется формула расчета веса, в которой к рассчитанном Sphinx-ом весу прибавляется myweight*10.
class Sphinx_Observer
{
static public function onAfterPrepareQueryBuilder($object, $args)
{
$queryBuilder = $args[0];
$queryBuilder->columns('myweight');
}
static public function onAfterPrepareValues($object, $args)
{
$aValues = $args[0];
$oPage = $args[1];
// Товар магазина
if ($oPage->module == 3 && $oPage->module_value_type == 2)
{
$oShop_Item = Core_Entity::factory('Shop_Item', $oPage->module_value_id);
$myweight = $oShop_Item->getRest() <= 0
? 0
: 1;
}
else
{
$myweight = 1;
}
$aValues[] = $myweight;
return $aValues;
}
static public function onBeforeExecuteFind($object, $args)
{
$oSearch_Sphinx_QueryBuilder_Select = $args[1];
// http://sphinxsearch.com/docs/current/sphinxql-select.html
// http://sphinxsearch.com/docs/current/formulas-for-builtin-rankers.html
/*
SPH_RANK_PROXIMITY_BM25 = sum(lcs*user_weight)*1000+bm25
SPH_RANK_BM25 = bm25
SPH_RANK_NONE = 1
SPH_RANK_WORDCOUNT = sum(hit_count*user_weight)
SPH_RANK_PROXIMITY = sum(lcs*user_weight)
SPH_RANK_MATCHANY = sum((word_count+(lcs-1)*max_lcs)*user_weight)
SPH_RANK_FIELDMASK = field_mask
SPH_RANK_SPH04 = sum((4*lcs+2*(min_hit_pos==1)+exact_hit)*user_weight)*1000+bm25
*/
//$oSearch_Sphinx_QueryBuilder_Select->option('ranker', "expr('sum(lcs*user_weight)*1000+bm25')")
//$oSearch_Sphinx_QueryBuilder_Select->option('ranker', "expr('sum((word_count+(lcs-1)*max_lcs)*user_weight)+myweight*2')");
$oSearch_Sphinx_QueryBuilder_Select->option('field_weights', "(title=2)");
$oSearch_Sphinx_QueryBuilder_Select->option('ranker', "expr('sum(lcs*user_weight) + myweight*10')");
}
}
Core_Event::attach('Search_Controller_Sphinx.onAfterPrepareQueryBuilder', array('Sphinx_Observer', 'onAfterPrepareQueryBuilder'));
Core_Event::attach('Search_Controller_Sphinx.onAfterPrepareValues', array('Sphinx_Observer', 'onAfterPrepareValues'));
Core_Event::attach('Search_Controller_Sphinx.onBeforeExecuteFind', array('Sphinx_Observer', 'onBeforeExecuteFind'));