27.3. Поиск по индексу

27.3.1. Построение запросов

Производить поиск по индексу можно двумя способами. Первый способ использует парсер запросов для построения запросов из строки. Второй способ дает возможность создавать свои запросы через программный интерфейс Zend_Search_Lucene.

В случае выбора парсера запросов учтите следующее:

  1. Если вы программно генерируете строку запроса и затем парсите ее с помощью парсера запросов, то вам следует серьезно подумать о построении запросов непосредственно через программный интерфейс. Другими словами, парсер запросов предназначен для текста, вводимого пользователем, а не генерируемого программным способом.

  2. Не разбитые на лексемы поля лучше добавлять непосредственно в запросы, а не через парсер запросов. Если значения полей программно генерируются приложением, то должны быть отдельные элементы запроса для этого поля. Анализатор, используемый парсером запросов, предназначен для преобразования введенного пользователем текста в элементы запроса. Программно генерируемые значения, такие, как даты, ключевые слова и т.д., должны генерироваться единообразно.

  3. В форме запроса поля с основным текстом должны использовать парсер запросов. Все остальные, такие, как периоды времени, ключевые слова и т.д, лучше добавлять непосредственно через программный интерфейс для запросов. Поля с ограниченным набором значений, которые могут отображаться в виде выпадающего списка, лучше не добавлять в строку запроса, которая парсится, а как элемент запроса.

  4. Булевы запросы позволяют объединять несколько запросов в один. Таким образом, это является наилучшим путем добавления дополнительных критериев пользовательского поиска, определяемых строкой запроса.

Оба способа используют один и тот же метод программного интерфейса для поиска в индексе:

<?php
require_once('Zend/Search/Lucene.php');

$index = Zend_Search_Lucene::open('/data/my_index');

$index->find($query);   

Метод Zend_Search_Lucene::find() автоматически определяет тип ввода и использует парсер запросов для построения соответствующего объекта Zend_Search_Lucene_Search_Query.

Важно отметить, что парсер запросов использует стандартный анализатор для разбиения на лексемы отдельных частей строки запроса. Таким образом, все преобразования, проделываемые с индексируемым текстом, также проделываются и с частями строки запроса.

Это могут быть приведение к нижнему регистру для того, чтобы сделать поиск нечувствительным к регистру, удаление запрещенных слов и т.д.

В противоположность парсеру запросов, методы API не преобразовывают или фильтруют входные элементы. Таким образом, API является более подходящим для сгенерированных компьютером или не разбитых на лексемы полей.

27.3.1.1. Парсинг запроса

Метод Zend_Search_Lucene_Search_QueryParser::parse() может использоваться для парсинга строки запроса и преобразования ее в объект запроса.

Этот объект может использоваться в методах программного интерфейса для объединения программно сгенерированных запросов с введенными пользователем.

Сейчас в некоторых случаях только таким способом можно будет производить поиск значений в полях, которые не были разбиты на лексемы.

<?php
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);

$pathTerm  = new Zend_Search_Lucene_Index_Term('/data/doc_dir/' . $filename, 'path');
$pathQuery = new Zend_Search_Query_Term($pathTerm);

$query = new Zend_Search_Query_Boolean();
$query->addSubquery($userQuery, true /* required */);
$query->addSubquery($pathQuery, true /* required */);

$hits = $index->find($query);

Метод Zend_Search_Lucene_Search_QueryParser::parse() также принимает необязательный параметр, через который указывается кодировка строки запроса.

<?php
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');

Если этот параметр опущен, то используется текущая локаль.

Можно также указать используемую по умолчанию кодировку для строки запроса через метод Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding():

<?php
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-5');
...
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);

Zend_Search_Lucene_Search_QueryParser::getDefaultEncoding() возвращает используемую по умолчанию кодировку для строки запроса (пустая строка означает "текущая локаль").

27.3.2. Результаты поиска

Результат поиска является массивом объектов Zend_Search_Lucene_Search_QueryHit. Все эти объекты имеют два свойства: $hit->document - номер документа в индексе, $hit->score - ранг "хита" в результате поиска. Результат упорядочен по рангу ("хиты" с наибольшим рангом идут первыми).

Объект Zend_Search_Lucene_Search_QueryHit также предоставляют все поля документа Zend_Search_Lucene_Document как свойства объекта. В данном примере возвращается "хит" и соответствующий ему документ имеет два поля: заголовок и автор.

<?php
require_once('Zend/Search/Lucene.php');

$index = Zend_Search_Lucene::open('/data/my_index');

$hits = $index->find($query);

foreach ($hits as $hit) {
    echo $hit->score;
    echo $hit->title;
    echo $hit->author;
}
?>

Сохраненные в индексе поля всегда возвращаются в кодировке UTF-8.

Исходный объект документа Zend_Search_Lucene_Document может также быть получен из Zend_Search_Lucene_Search_QueryHit. Вы можете извлечь сохраненные в индексе части документа, используя метод getDocument() объекта индекса и затем получая из через метод getFieldValue():

<?php
require_once('Zend/Search/Lucene.php');

$index = Zend_Search_Lucene::open('/data/my_index');

$hits = $index->find($query);
foreach ($hits as $hit) {
    // возвращает объект для этого "хита"
    echo $document = $hit->getDocument();

    // возвращает объект Zend_Search_Lucene_Field
    // из Zend_Search_Lucene_Document
    echo $document->getField('title');

    // возвращает строковое значение объекта Zend_Search_Lucene_Field
    echo $document->getFieldValue('title');

    // делает то же самое, что и getFieldValue()
    echo $document->title;
}
?>

Поля, доступные через объект Zend_Search_Lucene_Document, определяются во время индексирования. Поля документа либо только индексируются, либо индексируются и сохраняются в индексе индесирующим приложением (например, LuceneIndexCreation.jar).

Обратите внимание, что идентификатор документа (в нашем примере — 'path') также сохраняется в индексе и должен извлекаться из него.

27.3.3. Ранжирование результата

Zend_Search_Lucene использует тот же самый алгоритм ранжирования, что и Java Lucene. Результаты поиска по умолчанию сортируются по рангу (релевантности). "Хиты" с наибольшим рангом идут первыми, и документы, имеющие больший ранг, болльше соответствуют запросу, чем документы с меньшим рангом.

Приблизительно говоря, документы, в которых искомый элемент или фраза встречаются чаще, будут иметь более высокий ранг.

Число, соответствующее рангу, может быть получено через свойство score:

<?php
$hits = $index->find($query);

foreach ($hits as $hit) {
    echo $hit->id;
    echo $hit->score;
}   

Для вычисления ранга используется класс Zend_Search_Lucene_Search_Similarity. За подробностями см. раздел Расширяемость. Алгоритмы ранжирования.

27.3.4. Сортировка результатов поиска

По умолчанию результаты поиска сортируются по рангу. Вы можете изменить это поведение установкой поля (полей) для сортировки, типа сортировки и порядка сортировки.

$index->find() может принимать несколько необязательных параметров:

<?php
$index->find($query [, $sortField [, $sortType [, $sortOrder]]] [, $sortField2 [, $sortType [, $sortOrder]]] ...);

$sortField является именем сохраненного в индексе поля для сортировки результата.

$sortType может быть опущен или принимать значения SORT_REGULAR (сравнивать элементы как обычно, значение по умолчанию), SORT_NUMERIC (сравнивать элементы как числа), SORT_STRING (сравнивать элементы как строки).

$sortOrder может быть опущен или принимать значения SORT_ASC (сортировать в порядке возрастания, значение по умолчанию), SORT_DESC (сортировать в порядке убывания).

<?php
$index->find($query, 'quantity', SORT_NUMERIC, SORT_DESC);

<?php
$index->find($query, 'fname', SORT_STRING, 'lname', SORT_STRING);

<?php
$index->find($query, 'name', SORT_STRING, 'quantity', SORT_NUMERIC, SORT_DESC);

Будьте осторожны, когда используете сортировку, отличную от принятой по умолчанию. Для этого нужно полное извлечение документов из индекса, что может привести к резкому снижению производительности.

27.3.5. Подсветка результатов поиска

Метод Zend_Search_Lucene_Search_Query::highlightMatches() позволяет подсвечивать в HTML-документе элементы, присутствующие в контексте поискового запроса:

<?php
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
$hits = $index->find($query);
...
$highlightedHTML = $query->highlightMatches($sourceHTML);

Метод highlightMatches() для обработки HTML использует класс Zend_Search_Lucene_Document_Html (см. раздел "HTML-документы"). Поэтому этот метод предъявляет те же требования к HTML-коду, что и используемый класс.