<sect1 id="zend.search.lucene.searching">
    <title>Поиск по индексу<!--Searching an Index--></title>

    <sect2 id="zend.search.lucene.searching.query_building">
        <title>Построение запросов<!--Building Queries--></title>

        <para>
            Производить поиск по индексу можно двумя способами. Первый способ
            использует парсер запросов для построения запросов из строки.
            Второй способ дает возможность создавать свои запросы
            через программный интерфейс Zend_Search_Lucene.
<!--
            There are two ways to search the index. The first method uses
            Query Parser to construct query from a string. The second provides
            the ability to create your own queries through the Zend_Search_Lucene API.
-->
        </para>

        <para>
        В случае выбора парсера запросов учтите следующее:
<!--
        Before choosing to use the provided Query Parser, please consider
        the following:
-->

            <orderedlist>
                <listitem>
                    <para>
                        Если вы программно генерируете  строку запроса и затем парсите
                        ее с помощью парсера запросов, то вам следует серьезно подумать
                        о построении запросов непосредственно через программный интерфейс.
                        Другими словами, парсер запросов предназначен для текста, вводимого
                        пользователем, а не генерируемого программным способом.
<!--
                        If you are programmatically generating a query string and then parsing
                        it with the query parser then you should seriously consider building
                        your queries directly with the query API. In other words, the query
                        parser is designed for human-entered text, not for program-generated text.
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Не разбитые на лексемы поля лучше добавлять непосредственно
                        в запросы, а не через парсер запросов. Если значения полей программно
                        генерируются приложением, то должны быть отдельные элементы
                        запроса для этого поля. Анализатор, используемый
                        парсером запросов, предназначен для преобразования
                        введенного пользователем текста в элементы запроса.
                        Программно генерируемые значения, такие, как даты, ключевые слова
                        и т.д., должны генерироваться единообразно.
<!--
                        Untokenized fields are best added directly to queries, and not through
                        the query parser. If a field's values are generated programmatically
                        by the application, then so should query clauses for this field.
                        An analyzer, which the query parser uses, is designed to convert
                        human-entered text to terms. Program-generated values, like dates,
                        keywords, etc., should be consistently program-generated.
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                        В форме запроса поля с основным текстом должны использовать
                        парсер запросов. Все остальные, такие, как периоды времени,
                        ключевые слова и т.д, лучше добавлять непосредственно
                        через программный интерфейс для запросов. Поля с ограниченным
                        набором значений, которые могут отображаться в виде выпадающего
                        списка, лучше не добавлять в строку запроса, которая
                        парсится, а как элемент запроса.
<!--
                        In a query form, fields which are general text should use the query parser.
                        All others, such as date ranges, keywords, etc. are better added directly
                        through the query API. A field with a limit set of values, that can be
                        specified with a pull-down menu should not be added to a query string
                        which is subsequently parsed, but rather added as a TermQuery clause.
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Булевы запросы позволяют объединять несколько запросов в
                        один. Таким образом, это является наилучшим путем
                        добавления дополнительных критериев пользовательского
                        поиска, определяемых строкой запроса.
<!--
                        Boolean queries allow to mix several other queries into new one.
                        Thus it's the best way to add some additional criteria to user search, defined by
                        a query string.
-->
                    </para>
                </listitem>
            </orderedlist>

        </para>

        <para>
            Оба способа используют один и тот же метод программного интерфейса
            для поиска в индексе:
<!--
            Both ways use the same API method to search through the index:
-->
        </para>

        <programlisting role="php"><![CDATA[<?php
require_once('Zend/Search/Lucene.php');

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

$index->find($query);]]>   </programlisting>

        <para>
            Метод <code>Zend_Search_Lucene::find()</code> автоматически определяет
            тип ввода и использует парсер запросов для построения соответствующего
            объекта Zend_Search_Lucene_Search_Query.
<!--
            The <code>Zend_Search_Lucene::find()</code> method determines input type automatically and
            uses query parser to construct appropriate Zend_Search_Lucene_Search_Query object
            from a string.
-->
        </para>

        <para>
            Важно отметить, что парсер запросов использует стандартный
            анализатор для разбиения на лексемы отдельных частей строки запроса.
            Таким образом, все преобразования, проделываемые с индексируемым
            текстом, также проделываются и с частями строки запроса.
<!--
            It is important to note that query parser uses standard analyzer to tokenize separate parts of query string.
            Thus all transformations, which are done on indexed text are also done on query string entries.
-->
        </para>
        <para>
            Это могут быть приведение к нижнему регистру для того, чтобы сделать
            поиск нечувствительным к регистру, удаление запрещенных слов
            и т.д.
<!--
            It may be transforming to lower case to make search case-insensitive, removing stop-words, stamming and
            mauch more other things.
-->
        </para>
        <para>
            В противоположность парсеру запросов, методы API не преобразовывают
            или фильтруют входные элементы. Таким образом, API является более
            подходящим для сгенерированных компьютером или не разбитых на
            лексемы полей.
<!--
            As opposed to it, API method doesn't transform or filter input terms. Thus it's more suitable for
            computer generated or untokenized fields.
-->
        </para>

        <sect3 id="zend.search.lucene.searching.query_building.parsing">
            <title>Парсинг запроса<!--Query parsing--></title>
            <para>
                Метод <code>Zend_Search_Lucene_Search_QueryParser::parse()</code>
                может использоваться для парсинга строки запроса и
                преобразования ее в объект запроса.
<!--
                <code>Zend_Search_Lucene_Search_QueryParser::parse()</code> method may be used to parse query string
                into query object.
-->
            </para>

            <para>
                Этот объект может использоваться в методах программного
                интерфейса для объединения программно сгенерированных
                запросов с введенными пользователем.
<!--
                This object may be used in query construction API methods to combine user entered queries with
                machine generated queries.
-->
            </para>

            <para>
                Сейчас в некоторых случаях только таким способом можно будет
                производить поиск значений в полях, которые не были
                разбиты на лексемы.
<!--
                Actually, in some cases it's only way to search for values within untokenized fields:
-->

                <programlisting role="php"><![CDATA[<?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);]]></programlisting>
            </para>

            <para>
                Метод <code>Zend_Search_Lucene_Search_QueryParser::parse()</code>
                также принимает необязательный параметр, через который
                указывается кодировка строки запроса.
<!--
                <code>Zend_Search_Lucene_Search_QueryParser::parse()</code> method also takes optional encoding parameter,
                which can specify query string encoding:
-->
            <programlisting role="php"><![CDATA[<?php
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');]]></programlisting>
            </para>

            <para>
                Если этот параметр опущен, то используется текущая локаль.
<!--
                If encoding is omitted, then current locale is used.
-->
            </para>

            <para>
                Можно также указать используемую по умолчанию кодировку для
                строки запроса через метод <code>Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding()</code>:
<!--
                It's also possible to specify default query string encoding with
                <code>Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding()</code> method:
-->
            <programlisting role="php"><![CDATA[<?php
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-5');
...
$userQuery = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);]]></programlisting>
            </para>

            <para>
                <code>Zend_Search_Lucene_Search_QueryParser::getDefaultEncoding()</code>
                возвращает используемую по умолчанию кодировку для строки
                запроса (пустая строка означает "текущая локаль").
<!--
                <code>Zend_Search_Lucene_Search_QueryParser::getDefaultEncoding()</code> returns current default query
                string encoding (empty string means "current locale").
-->
            </para>
        </sect3>
    </sect2>

    <sect2 id="zend.search.lucene.searching.results">
        <title>Результаты поиска<!--Search Results--></title>
        <para>
            Результат поиска является массивом объектов
            Zend_Search_Lucene_Search_QueryHit. Все эти объекты имеют два
            свойства: <code>$hit->document</code> - номер документа в индексе,
            <code>$hit->score</code> - ранг "хита" в результате поиска.
            Результат упорядочен по рангу ("хиты" с наибольшим рангом идут
            первыми).

<!--
            The search result is an array of Zend_Search_Lucene_Search_QueryHit objects.  Each of these has
            two properties: <code>$hit->document</code> is a document number within
            the index and <code>$hit->score</code> is a score of the hit in
            a search result. Result is ordered by score (top scores come first).
-->
        </para>

        <para>
            Объект Zend_Search_Lucene_Search_QueryHit также предоставляют все
            поля документа Zend_Search_Lucene_Document как свойства объекта. В
            данном примере возвращается "хит" и соответствующий ему документ
            имеет два поля: заголовок и автор.
<!--
            The Zend_Search_Lucene_Search_QueryHit object also exposes each field of the Zend_Search_Lucene_Document found by
            the hit as a property of the hit.  In this example, a hit is returned and
            the corresponding document has two fields: title and author.
-->
        </para>
        <programlisting role="php"><![CDATA[<?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;
}
?>]]></programlisting>

        <para>
            Сохраненные в индексе поля всегда возвращаются в кодировке UTF-8.
<!--
            Stored fields are always returned in UTF-8 encoding.
-->
        </para>

        <para>
            Исходный объект документа Zend_Search_Lucene_Document может также
            быть получен из Zend_Search_Lucene_Search_QueryHit. Вы можете
            извлечь сохраненные в индексе части документа, используя метод
            <code>getDocument()</code> объекта индекса и затем получая из через
            метод <code>getFieldValue()</code>:
<!--
            Optionally, the original Zend_Search_Lucene_Document object can be returned from the
            Zend_Search_Lucene_Search_QueryHit.

            You can retrieve stored parts of the document by using the <code>getDocument()</code>
            method of the index object and then get them by
            <code>getFieldValue()</code> method:
-->
        </para>

        <programlisting role="php"><![CDATA[<?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;
}
?>]]></programlisting>

        <para>
        Поля, доступные через объект Zend_Search_Lucene_Document, определяются
        во время индексирования. Поля документа либо только индексируются, либо
        индексируются и сохраняются в индексе индесирующим приложением
        (например, LuceneIndexCreation.jar).
<!--
        The fields available from the Zend_Search_Lucene_Document object are determined at
        the time of indexing.  The document fields are either indexed, or
        index and stored, in the document by the indexing application
        (e.g. LuceneIndexCreation.jar).
-->
        </para>

        <para>
        Обратите внимание, что идентификатор документа (в нашем примере — 'path')
        также сохраняется в индексе и должен извлекаться из него.
<!--
        Pay attention, that document identity ('path' in our example) is also stored
        in the index and must be retrieved from them.
-->
        </para>

    </sect2>


    <sect2 id="zend.search.lucene.searching.results-scoring">
        <title>Ранжирование результата<!--Results Scoring--></title>
        <para>
            Zend_Search_Lucene использует тот же самый алгоритм ранжирования,
            что и Java Lucene. Результаты поиска по умолчанию сортируются по
            рангу (релевантности). "Хиты" с наибольшим рангом идут первыми, и
            документы, имеющие больший ранг, болльше соответствуют запросу, чем
            документы с меньшим рангом.
<!--
            Zend_Search_Lucene uses the same scoring algorithms as Java Lucene.
            Hits in search result are ordered by score by default. Hits with greater score come first, and
            documents having higher scores match the query more than documents having lower scores.
-->
        </para>

        <para>
            Приблизительно говоря, документы, в которых искомый элемент или фраза
            встречаются чаще, будут иметь более высокий ранг.
<!--
            Roughly speaking, search hits that contain the searched term or phrase more frequently
            will have a higher score.
-->
        </para>

        <para>
            Число, соответствующее рангу, может быть получено через
            свойство <code>score</code>:
<!--
            Score can be retrived by <code>score</code> property of hit:
-->
        </para>

        <programlisting role="php"><![CDATA[<?php
$hits = $index->find($query);

foreach ($hits as $hit) {
    echo $hit->id;
    echo $hit->score;
}]]>   </programlisting>

        <para>
            Для вычисления ранга используется класс Zend_Search_Lucene_Search_Similarity.
            За подробностями см. раздел
            <link linkend="zend.search.lucene.extending.scoring">Расширяемость. Алгоритмы ранжирования</link>.
<!--
            Zend_Search_Lucene_Search_Similarity class is used to calculate score.
            See <link linkend="zend.search.lucene.extending.scoring">Extensibility. Scoring Algorithms</link> section for details.
-->
        </para>

    </sect2>

    <sect2 id="zend.search.lucene.searching.sorting">
        <title>Сортировка результатов поиска<!--Search Result Sorting--></title>
        <para>
            По умолчанию результаты поиска сортируются по рангу. Вы можете
            изменить это поведение установкой поля (полей) для сортировки, типа
            сортировки и порядка сортировки.
<!--
            Search result is sorted by score by default. You change this by setting a sort field (or fields), sort type
            and sort order parameters.
-->
        </para>

        <para>
            <code>$index->find()</code> может принимать несколько необязательных
            параметров:
<!--
            <code>$index->find()</code> call may take several optional parameters:
-->
            <programlisting role="php"><![CDATA[<?php
$index->find($query [, $sortField [, $sortType [, $sortOrder]]] [, $sortField2 [, $sortType [, $sortOrder]]] ...);]]></programlisting>
        </para>

        <para>
            <code>$sortField</code> является именем сохраненного в индексе поля
            для сортировки результата.
<!--
            <code>$sortField</code> is a name of stored field to sort result.
-->
        </para>

        <para>
            <code>$sortType</code> может быть опущен или принимать значения
            <code>SORT_REGULAR</code> (сравнивать элементы как обычно, значение по умолчанию),
            <code>SORT_NUMERIC</code> (сравнивать элементы как числа),
            <code>SORT_STRING</code> (сравнивать элементы как строки).
<!--
            <code>$sortType</code> may be omitted or take values
            <code>SORT_REGULAR</code> (compare items normally, default value),
            <code>SORT_NUMERIC</code> (compare items numerically),
            <code>SORT_STRING</code> (compare items as strings).
-->
        </para>

        <para>
            <code>$sortOrder</code> может быть опущен или принимать значения
            <code>SORT_ASC</code> (сортировать в порядке возрастания, значение по умолчанию),
            <code>SORT_DESC</code> (сортировать в порядке убывания).
<!--
            <code>$sortOrder</code> may be omitted or take values
            <code>SORT_ASC</code> (sort in ascending order, default value),
            <code>SORT_DESC</code> (sort in descending order).
-->
        </para>

        <para>
<!--
            Examples:
-->
            <programlisting role="php"><![CDATA[<?php
$index->find($query, 'quantity', SORT_NUMERIC, SORT_DESC);]]></programlisting>
            <programlisting role="php"><![CDATA[<?php
$index->find($query, 'fname', SORT_STRING, 'lname', SORT_STRING);]]></programlisting>
            <programlisting role="php"><![CDATA[<?php
$index->find($query, 'name', SORT_STRING, 'quantity', SORT_NUMERIC, SORT_DESC);]]></programlisting>
        </para>

        <para>
            Будьте осторожны, когда используете сортировку, отличную от
            принятой по умолчанию. Для этого нужно полное извлечение документов
            из индекса, что может привести к резкому снижению
            производительности.
<!--
            Please be careful when using non-default search order.
            It needs to retrive documents completely from an index and may drammatically slow down search performance.
-->
        </para>
    </sect2>

    <sect2 id="zend.search.lucene.searching.highlighting">
        <title>Подсветка результатов поиска<!--Search Results Highlighting--></title>
        <para>
            Метод
            <code>Zend_Search_Lucene_Search_Query::highlightMatches()</code>
            позволяет подсвечивать в HTML-документе элементы, присутствующие в
            контексте поискового запроса:
<!--
            <code>Zend_Search_Lucene_Search_Query::highlightMatches()</code>
            method allows to highlight HTML document terms
            in context of search query:
-->
            <programlisting role="php"><![CDATA[<?php
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryStr);
$hits = $index->find($query);
...
$highlightedHTML = $query->highlightMatches($sourceHTML);]]></programlisting>
        </para>

        <para>
            Метод <code>highlightMatches()</code> для обработки HTML использует
            класс <code>Zend_Search_Lucene_Document_Html</code> (см.
            <link linkend="zend.search.lucene.index-creation.html-documents">раздел
            "HTML-документы"</link>). Поэтому этот метод предъявляет те же
            требования к HTML-коду, что и используемый класс.
<!--
            <code>highlightMatches()</code> method utilizes
            <code>Zend_Search_Lucene_Document_Html</code> class
            (see <link linkend="zend.search.lucene.index-creation.html-documents">HTML
            documents section</link> for details)
            for HTML processing. So it has the same requirements for HTML source.
-->
        </para>
    </sect2>

</sect1>

<!--
vim:se ts=4 sw=4 et:
-->
