Sphinx, часть 2: Perl API
Date December 31st, 2009 Author Vitaly Agapov
В прошлой статье «Sphinx: начало» я описал процедуру установки Sphinx и показал, как создавать полнотекстовые индексы на примере базы данных с книгами. Теперь нам надо понять, как использовать эти индексы в Perl-приложении. Это может быть любое Perl-приложение, но в основном Sphinx Perl API находит применение в Web CGI. Почему именно в Perl? Да потому что про PHP и так написано везде и помногу, и, кроме того, Perl – это наше всё.
В качестве API будем использовать перловый модуль Sphinx::Search, легко добывающийся с cpan.org:
cpan -i Sphinx::Search
Модуль обладает всей необходимой функциональностью, отлично работает и вообще всячески радует. На момент написания актуальной версией является 0.22, которую можно найти здесь – http://search.cpan.org/~jjschutz/Sphinx-Search-0.22/lib/Sphinx/Search.pm.
Итак, для нашего примера с индексом book_search и кодировкой utf-8 в базе и на сайте для вывода результатов поиска нужен примерно такой вот код:
01.
use
Sphinx::Search;
02.
my
$q
=
$input
{query};
# Получаем строку поиска
03.
my
$string
= decode(
'utf-8'
,
$q
);
# Преобразуем строку для поиска из utf-8 во внутренний формат Perl
04.
my
$sphinx
= Sphinx::Search->new();
# Вызываем конструктор
05.
my
%weights
=(name=>3, annotation=>1);
06.
my
$results
=
$sphinx
->SetMatchMode(SPH_MATCH_ANY)
# Режим поиска совпадений
07.
->SetSortMode(SPH_SORT_RELEVANCE)
# Режим сортировки
08.
->SetFieldWeights(\
%weights
)
# Вес полей
09.
->Query(
$string
,
'book_search'
);
# Поисковый запрос
10.
my
$found
=
$results
->{total_found};
# Получаем количество найденных совпадений
11.
my
$attrs
=
$results
->{words};
12.
for
(
my
$x
=0;
$x
<
$found
;
$x
++)
13.
{
14.
$doc
=
$results
->{matches}->[
$x
]->{doc};
15.
...
16.
# читаем из таблицы book строку с id = $doc и выводим нужные поля
17.
...
18.
}
Всё достаточно просто. Но для полноты картины расскажу о кастомизации и возможностях модуля.
Некоторые методы объекта Sphinx::Search
$error = $sphinx->GetLastError;
Получить строку с сообщением о последней ошибке
$warning = $sphinx->GetLastWarning;
Получить строку с сообщением о последнем предупреждении
$sphinx->SetServer($host, $port);
Установить хост и порт в случае, если searchd запущен на другом хосте
$sphinx->SetConnectTimeout($timeout)
Установить таймаут соединения в секундах
$sphinx->SetMaxQueryTime($millisec);
Установить ограничение на вермя запроса в миллисекундах. 0 – отсутствие ограничения.
$sphinx->SetMatchMode($mode);
Установить режим поиска совпадений. Возможные аргументы:
- SPH_MATCH_ALL – Совпадение по всем словам
- SPH_MATCH_ANY – Совпадение по любому из слов
- SPH_MATCH_PHRASE – Точное совпадение фразы
- SPH_MATCH_BOOLEAN – Запрос с использованием логических операторов AND (&), OR (|), NOT (!,-)
$sphinx->SetSortMode(SPH_SORT_RELEVANCE);
$sphinx->SetSortMode($mode, $sortby);
Установить режим сортировки результатов. Возможны аргументы:
- SPH_SORT_RELEVANCE – сортировка по релевантности
- SPH_SORT_ATTR_DESC, SPH_SORT_ATTR_ASC – сортировка по убыванию/возрастанию атрибута $sortby
- SPH_SORT_TIME_SEGMENTS – сортировка по временному атрибуту $sortby по убыванию
- SPH_SORT_EXTENDED – сортировка по аналогии с синтаксисом SQL, где $sortby определяет режим
$sphinx->SetFieldWeights(\%weights);
Установить вес для полей. Хэш определяет численные значения для соответствующих полей таблиц.
$sphinx->SetIndexWeights(\%weights);
Установить вес для индексов в том случае если для поиска используется более одного индекса.
$sphinx->SetIDRange($min, $max);
Установить диапазон значений id для записей.
$sphinx->SetFilter($attr, \@values);
$sphinx->SetFilter($attr, \@values, $exclude);
Установить фильтры по атрибутам. Если установлен $exclude, то фильтр будет удалять соответствующие записи.
$sphinx->SetRetries($count, $delay);
Установить количество повторных попыток и задержку между попытками.
$results = $sphinx->Query($query, $index);
Подключиться к searchd и выполнить поиск данного запроса в данном индексе. Если $index=”*”, то поиск ведётся по всем индексам. Возвращает хэш с такими ключами:
- matches – массив с хэшами о найденных записях (ключи “doc”, “weight”, “group”, “stamp” )
- total – количество совпадений
- total_found – количество найденных записей
- time – время поиска
- words – хэш с отдельными словами запроса
$results = $sphinx->AddQuery($query, $index);
Добавить запрос в последовательность запросов. Механизм позволяет оптимизировать сложны или многократные запросы, добавляя возможность поиска в найденных результатах.
$sphinx->RunQueries
Выполнить последовательность запросов.
Поисковые сниппеты
Sphinx::Search позволяет генерировать сниппеты с контекстом, в котором встретилось ключевое слово. В своей терминологии здесь используется понятие excerpts.
Для генерирования сниппета используется метод BuildExcerpts:
$excerpts = $spinx->BuildExcerpts($docs, $index, $words, $opts)
Здесь:
- $docs – ссылка на массив строк с содержимым документа
- $index – строка с именем индекса для стемминга
- $words – строка, содержащая ключевые слова для из выделения
- $opts – ссылка на хэш с полями, определяющими параметры выделения ключевых слов
- before_match – строка для вставки перед ключевым словом, по умолчанию “<b>”
- after_match – строка для вставки после ключевого слова, по умолчанию “</b>”
- chunk_separator – строка для вставки между кусками сниппета, по умолчанию ” … “
- limit – максимальное количество символов в сниппете, по умолчанию 256
- around – сколько слов выделять вокруг каждого совпадения, по умолчанию 5
- exact_phrase – выделять ли только конкретные совпадения, по умолчанию false
- single_passage – выделять ли только одно лучшее совпадение, по умолчанию false
- use_boundaries
- weight_order
Для пущей понятности принципа получения сниппетов приведу примерный кусок кода, как раз этим и занимающийся:
01.
use
Sphinx::Search;
02.
use
DBI;
03.
# Подключаемся к базе
04.
my
$dbh
= DBI->
connect
(
"DBI:mysql:database=db_name;host=localhost"
,
"user"
,
"pwd"
,{
'RaiseError'
=> 1});
05.
$dbh
->
do
(
"SET NAMES utf8"
);
06.
$dbh
->
do
(
"SET CHARACTER SET utf8"
);
07.
$dbh
->
do
(
"SET character_set_connection=utf8"
);
08.
$dbh
->
do
(
"SET character_set_client=utf8"
);
09.
$dbh
->
do
(
"SET character_set_server=utf8"
);
10.
$dbh
->
do
(
"SET character_set_results=utf8"
);
11.
# Здесь пропустим кусок, занимающийся вспомогательными делами
12.
my
$q
=
$input
{query};
# Получаем строку поиска
13.
my
$string
= decode(
'utf-8'
,
$q
);
# Преобразуем строку для поиска из utf-8 во внутренний формат Perl
14.
my
$sphinx
= Sphinx::Search->new();
# Вызываем конструктор
15.
my
$results
=
$sphinx
->SetMatchMode(SPH_MATCH_ANY)
# Режим поиска совпадений
16.
->SetSortMode(SPH_SORT_RELEVANCE)
# Режим сортировки
17.
->Query(
$string
,
'index_name'
);
# Поисковый запрос
18.
my
$found
=
$results
->{total_found};
# Получаем количество найденных совпадений
19.
my
$opts
= {
# Задаём опции для BuildExcerpts
20.
'after_match'
=>
'</strong>'
,
21.
'before_match'
=>
'<strong>'
22.
};
23.
for
(
my
$x
=0;
$x
<
$found
;
$x
++)
24.
{
25.
my
$doc
=
$results
->{matches}->[
$x
]->{doc};
# Получаем id найденного документа
26.
my
$sql_query
=
"SELECT text FROM table_name WHERE id="
.
$doc
;
# Получаем текст документа
27.
my
$sth
=
$dbh
->prepare(
$sql_query
);
28.
$sth
->execute;
29.
if
(
my
$ref
=
$sth
->fetchrow_hashref())
30.
{
31.
my
$tmp
= decode(
'utf-8'
,
$ref
->{text});
# Преобразуем текст во внутренний формат Perl
32.
my
$excerpts
=
$sphinx
->BuildExcerpts([
$tmp
],
'index_name'
,
$string
,
$opts
);
# Ищем совпадения
33.
$ref
->{text} = encode(
'utf-8'
,
$excerpts
->[0]);
# Преобразуем полученную строку обратно в UTF-8
34.
print
$ref
->{text};
# Выводим на экран сниппет с подсвеченными ключевыми словами
35.
}
36.
}
Tags: MySQL, Perl, Sphinx
Category:
MySQL, Perl, Web-dev |
4 Comments »
31 May 2012 - 11:04
Добрый день, очень интересно если у меня несколько индексов, то как предать их в BuildExcerpts? через запятую и пробел не получается возвращает ноль.
20 November 2012 - 7:21
Маленькое замечание: запрос к базе делать построчно в цикле – 20 результатов = 20 запросов, нерационально (стр. 26).
А статья очень пригодилась спасибо.
26 February 2015 - 22:46
Добрый день!
Имеем задачу сделать поиск по базе запчастей от поставщиков, она заключается в следующем:
Наша компания занимается ремонтом мобильной техники и нам необходимо когда звонит клиент
по виду услуги и виду модели делать поиск по базе поставщиков (которые уже залиты и
проиндексированы sphinx'ом)
Пример:
iPhone 5s Замена дисплея
Мы обращаемся в нашу базу тегов где прописаны на услугу Замена дисплея следующие теги:
(диспл, тачскрин, сборе, lcd, touch, digitizer и так далее скажем штук 10 тегов)
Дальше мы дробим название бренда и модели по пробелам на теги – то есть получается массив
тегов:
{iPhone, 5s, диспл, тачскрин, сборе, lcd, touch, digitizer}
Нам нужно, чтобы поиск был по тегам, и выводились результаты ТОП 100 подходящих по
релевантности.
Даже если скажем у нас в базе 300 строк, из них если искать по тегам чтобы все находились
теги в строках – даже если их будет 50 штук, то остальные 50 в любом случае должны быть
показаны (даже если не найден хотябы 1 тег) – то есть нужен вывод по релевантности. Если
например сделаем вывод 300 строк – то будут выведены все строки с БД, но показаны будут
по релевантности (количеству вхождений тегов, то есть Строка с наличием 10 тегов будет на
1 месте, с 9 на 2 и так далее.)
Никак не можем понять как настроить конфиг.
Очень просим помочь, заранее больше спасибо!
22 September 2015 - 16:34
[…] Sphinx, часть 2: Perl API […]