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 в базе и на сайте для вывода результатов поиска нужен примерно такой вот код:
use Sphinx::Search; my $q = $input{query}; # Получаем строку поиска my $string = decode('utf-8', $q); # Преобразуем строку для поиска из utf-8 во внутренний формат Perl my $sphinx = Sphinx::Search->new(); # Вызываем конструктор my %weights=(name=>3, annotation=>1); my $results = $sphinx->SetMatchMode(SPH_MATCH_ANY) # Режим поиска совпадений ->SetSortMode(SPH_SORT_RELEVANCE) # Режим сортировки ->SetFieldWeights(\%weights) # Вес полей ->Query($string, 'book_search'); # Поисковый запрос my $found=$results->{total_found}; # Получаем количество найденных совпадений my $attrs=$results->{words}; for (my $x=0; $x<$found; $x++) { $doc=$results->{matches}->[$x]->{doc}; ... # читаем из таблицы book строку с id = $doc и выводим нужные поля ... }
Всё достаточно просто. Но для полноты картины расскажу о кастомизации и возможностях модуля.
Некоторые методы объекта 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
Для пущей понятности принципа получения сниппетов приведу примерный кусок кода, как раз этим и занимающийся:
use Sphinx::Search; use DBI; # Подключаемся к базе my $dbh = DBI->connect("DBI:mysql:database=db_name;host=localhost","user","pwd",{'RaiseError' => 1}); $dbh->do("SET NAMES utf8"); $dbh->do("SET CHARACTER SET utf8"); $dbh->do("SET character_set_connection=utf8"); $dbh->do("SET character_set_client=utf8"); $dbh->do("SET character_set_server=utf8"); $dbh->do("SET character_set_results=utf8"); # Здесь пропустим кусок, занимающийся вспомогательными делами my $q = $input{query}; # Получаем строку поиска my $string = decode('utf-8', $q); # Преобразуем строку для поиска из utf-8 во внутренний формат Perl my $sphinx = Sphinx::Search->new(); # Вызываем конструктор my $results = $sphinx->SetMatchMode(SPH_MATCH_ANY) # Режим поиска совпадений ->SetSortMode(SPH_SORT_RELEVANCE) # Режим сортировки ->Query($string, 'index_name'); # Поисковый запрос my $found=$results->{total_found}; # Получаем количество найденных совпадений my $opts = { # Задаём опции для BuildExcerpts 'after_match' => '</strong>', 'before_match' => '<strong>' }; for (my $x=0; $x<$found; $x++) { my $doc = $results->{matches}->[$x]->{doc}; # Получаем id найденного документа my $sql_query="SELECT text FROM table_name WHERE id=".$doc; # Получаем текст документа my $sth = $dbh->prepare($sql_query); $sth->execute; if (my $ref = $sth->fetchrow_hashref()) { my $tmp = decode('utf-8', $ref->{text}); # Преобразуем текст во внутренний формат Perl my $excerpts = $sphinx->BuildExcerpts([$tmp], 'index_name', $string, $opts); # Ищем совпадения $ref->{text} = encode('utf-8', $excerpts->[0]); # Преобразуем полученную строку обратно в UTF-8 print $ref->{text}; # Выводим на экран сниппет с подсвеченными ключевыми словами } }
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 […]