# Поиск по индексу

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

Производить поиск по индексу можно двумя способами. Первый способ использует парсер запросов для построения запросов из строки. Второй способ дает возможность создавать свои запросы через программный интерфейс 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 является более подходящим для сгенерированных компьютером или не разбитых на лексемы полей.

### Парсинг запроса

Метод `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_Lucene_Search_Query_Term($pathTerm);

    $query = new Zend_Search_Lucene_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()` возвращает используемую по умолчанию кодировку для строки запроса (пустая строка означает "текущая локаль").

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

Результат поиска является массивом объектов 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') также сохраняется в индексе и должен извлекаться из него.

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

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. За подробностями см. раздел Расширяемость. Алгоритмы ранжирования.

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

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

`$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);

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

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

Метод `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-коду, что и используемый класс.