Это поисковая система, предназначенная для содержательных сайтов с упрощенной, но функциональной поддержкой английской и русской морфологии. Он индексирует ваш контент и обеспечивает полнотекстовый поиск.
База данных | Тесты |
---|---|
MySQL 5.6 или новее и MariaDB 10.2 или новее. | |
PostgreSQL (проверено на версиях 10...16) | |
SQLite (проверено на версии 3.37.2) |
composer require s2/rose
Индекс может храниться в базе данных или в файле. Хранилище служит уровнем абстракции, скрывающим детали реализации. В большинстве случаев вам понадобится хранилище базы данных PdoStorage
.
Хранилище требуется как для индексации, так и для поиска.
$ pdo = new PDO ( ' mysql:host=127.0.0.1;dbname=s2_rose_test;charset=utf8 ' , ' username ' , ' passwd ' );
$ pdo -> setAttribute ( PDO :: ATTR_ERRMODE , PDO :: ERRMODE_EXCEPTION );
use S2 Rose Storage Database PdoStorage ;
$ storage = new PdoStorage ( $ pdo , ' table_prefix_ ' );
Если вы хотите перестроить индекс, вы вызываете метод PdoStorage::erase()
:
$ storage -> erase ();
Он удаляет индексные таблицы (если они существуют) и создает новые с нуля. Этого метода достаточно для обновления до новой версии Rose, которая может быть несовместима с существующим индексом.
Для обработки естественного языка Роуз использует стеммеры. Стеммер усекает измененную часть слов, а Роуз обрабатывает полученные основы. Rose не имеет встроенных словарей, но включает эвристические стеммеры, разработанные Портером. Вы можете интегрировать любой другой алгоритм, реализовав StemmerInterface.
use S2 Rose Stemmer PorterStemmerEnglish ;
use S2 Rose Stemmer PorterStemmerRussian ;
// For optimization primary language goes first (in this case Russian)
$ stemmer = new PorterStemmerRussian ( new PorterStemmerEnglish ());
Indexer
создает поисковый индекс. Это зависит от стеммера и хранилища.
use S2 Rose Indexer ;
$ indexer = new Indexer ( $ storage , $ stemmer );
Индексатор принимает ваши данные в специальном формате. Данные должны быть обернуты в класс Indexable
:
use S2 Rose Entity Indexable ;
// Main parameters
$ indexable = new Indexable (
' id_1 ' , // External ID - an identifier in your system
' Test page title ' , // Title
' This is the first page to be indexed. I have to make up a content. ' ,
1 // Instance ID - an optional ID of your subsystem
);
// Other optional parameters
$ indexable
-> setKeywords ( ' singlekeyword, multiple keywords ' ) // The same as Meta Keywords
-> setDescription ( ' Description can be used in snippets ' ) // The same as Meta Description
-> setDate ( new DateTime ( ' 2016-08-24 00:00:00 ' ))
-> setUrl ( ' url1 ' )
-> setRelevanceRatio ( 3.14 ) // Multiplier for important pages
;
$ indexer -> index ( $ indexable );
$ indexable = new Indexable (
' id_2 ' ,
' Test page title 2 ' ,
' This is the second page to be indexed. Let ' s compose something new. '
);
$ indexable -> setKeywords ( ' content, page ' );
$ indexer -> index ( $ indexable );
Конструктор Indexable
имеет 4 аргумента:
Дополнительные параметры, которые вы можете указать, включают: ключевые слова, описание, дату, коэффициент релевантности и URL-адрес. Ключевые слова индексируются и ищутся с более высокой релевантностью. Описание можно использовать для построения сниппета (см. ниже). Для этой цели предлагается использовать содержимое метатегов «ключевое слово» и «описание», если таковые имеются. URL-адрес может быть произвольной строкой.
Метод Indexer::index()
используется как для добавления, так и для обновления индекса. Если содержимое не изменилось, этот метод пропускает операцию. В противном случае контент будет удален и снова проиндексирован.
При удалении страницы с сайта просто позвоните
$ indexer -> removeById ( $ externalId , $ instanceId );
Результаты полнотекстового поиска можно получить через класс Finder
. $resultSet->getItems()
возвращает всю информацию об элементах контента и их релевантности.
use S2 Rose Finder ;
use S2 Rose Entity Query ;
$ finder = new Finder ( $ storage , $ stemmer );
$ resultSet = $ finder -> find ( new Query ( ' content ' ));
foreach ( $ resultSet -> getItems () as $ item ) {
// first iteration: second iteration:
$ item -> getId (); // 'id_2' 'id_1'
$ item -> getInstanceId (); // null 1
$ item -> getTitle (); // 'Test page title 2' 'Test page title'
$ item -> getUrl (); // '' 'url1'
$ item -> getDescription (); // '' 'Description can be used in snippets'
$ item -> getDate (); // null new DateTime('2016-08-24 00:00:00')
$ item -> getRelevance (); // 4.1610856664112195 0.26907154598642522
$ item -> getSnippet (); // 'This is the second page...' 'I have to make up a <i>content</i>.'
}
Измените объект Query
, чтобы использовать нумерацию страниц:
$ query = new Query ( ' content ' );
$ query
-> setLimit ( 10 ) // 10 results per page
-> setOffset ( 20 ) // third page
;
$ resultSet = $ finder -> find ( $ query );
$ resultSet -> getTotalCount (); // Returns total amount of found items (for pagination links)
Укажите идентификатор экземпляра, чтобы ограничить область поиска подсистемой:
$ resultSet = $ finder -> find (( new Query ( ' content ' ))-> setInstanceId ( 1 ));
foreach ( $ resultSet -> getItems () as $ item ) {
// first iteration only:
$ item -> getId (); // 'id_1'
$ item -> getInstanceId (); // 1
}
Распространенной практикой является выделение найденных слов в результатах поиска. Вы можете получить выделенный заголовок:
$ resultSet = $ finder -> find ( new Query ( ' title ' ));
$ resultSet -> getItems ()[ 0 ]-> getHighlightedTitle ( $ stemmer ); // 'Test page <i>title</i>'
Для этого метода необходим стеммер, поскольку он учитывает морфологию и выделяет все формы слов. По умолчанию слова выделяются курсивом. Вы можете изменить шаблон выделения, вызвав $finder->setHighlightTemplate('<b>%s</b>')
.
Сниппеты — это небольшие фрагменты текста, содержащие найденные слова, которые отображаются на странице результатов поиска. Роуз обрабатывает проиндексированный контент и выбирает наиболее подходящие предложения.
use S2 Rose Entity ExternalContent ;
use S2 Rose Snippet SnippetBuilder ;
$ finder -> setSnippetLineSeparator ( ' · ' ); // Set snippet line separator. Default is '... '.
$ resultSet -> getItems ()[ 0 ]-> getSnippet ();
// 'I have to make up a <i>content</i>. · I have changed the <i>content</i>.'
Слова в сниппетах выделяются так же, как и в заголовках.
Если создание фрагментов занимает много времени, попробуйте использовать нумерацию страниц, чтобы уменьшить количество обрабатываемых фрагментов.
Экземпляры могут быть полезны для ограничения области поиска.
Например, вы можете попробовать проиндексировать сообщения в блоге с instance_id = 1
и комментарии с instance_id = 2
. Затем вы можете запускать запросы с различными ограничениями:
(new Query('content'))->setInstanceId(1)
выполняет поиск по сообщениям в блоге,(new Query('content'))->setInstanceId(2)
выполняет поиск по комментариям,(new Query('content'))
ищет везде. Если при индексировании вы опустите instance_id или укажете instance_id === null
, внутри будет использоваться значение 0
. Такое содержимое может соответствовать только запросам без ограничений instance_id.
Rose предназначен для веб-сайтов и веб-приложений. По умолчанию он поддерживает формат HTML контента. Однако можно расширить код для поддержки других форматов (например, обычного текста, уценки). Это можно сделать, создав собственный экстрактор:
use S2 Rose Extractor ExtractorInterface ;
use S2 Rose Indexer ;
class CustomExtractor implements ExtractorInterface
{
// ...
// Please refer to the source code
// to figure out how to create an extractor.
}
$ indexer = new Indexer ( $ storage , $ stemmer , new CustomExtractor (), new Logger ());
PdoStorage имеет возможность идентифицировать похожие элементы во всем наборе индексированных элементов.
Рассмотрим сценарий, в котором у вас есть блог, и его сообщения индексируются с помощью Rose. Эта конкретная функция позволяет вам выбирать набор других сообщений для каждого отдельного сообщения, позволяя посетителям исследовать соответствующий контент.
Структура данных в полнотекстовом индексе хорошо подходит для задачи отбора похожих публикаций. Проще говоря, обычный поиск предполагает выбор релевантных сообщений на основе слов из поискового запроса, тогда как рекомендации по публикациям включают выбор других сообщений на основе слов, присутствующих в данном сообщении.
Вы можете получить рекомендации, вызвав следующий метод:
$ similarItems = $ readStorage -> getSimilar ( new ExternalId ( ' id_2 ' ));
// The result contains the following data:
// $similarItems[0] = [
// 'tocWithMetadata' => new TocEntryWithMetadata(...),
// 'external_id' => 'id_1',
// 'instance_id' => '1',
// 'title' => 'Test page title',
// 'snippet' => 'This is the first page to be indexed.',
// 'snippet2' => 'I have to make up a content.',
// ],
Примечание
Рекомендации поддерживаются в базах данных MySQL и PostgreSQL. Они не реализованы в SQLite из-за ограниченной поддержки SQL.