<sect1 id="zend.search.lucene.query-api">
    <title>API для построения запросов<!--Query Construction API--></title>

    <para>
        В дополнение к автоматическому парсингу запросов есть возможность
        строить запросы через API.
<!--
        In addition to parsing query automatically it's also possible to constract them through API.
-->
    </para>

    <para>
        Пользовательские запросы могут быть объединены с запросами, созданными
        через API. Используйте парсер запросов для построения запроса из строки.
<!--
        User query can be combined with queries created through API. Use query parser to construct query from a string:
-->
        <programlisting language="php"><![CDATA[<?php
$query = Zend_Search_Lucene_Search_QueryParser::parse($queryString);]]></programlisting>
    </para>


    <sect2 id="zend.search.lucene.queries.exceptions">
        <title>Исключения, бросаемые парсером запросов<!--Query Parser Exceptions--></title>
        <para>
        Парсер запросов может генерировать два типа исключений:
<!--
        Query parser may generate two types of exceptions.
-->
        <itemizedlist>
            <listitem>
                <para>
                    <code>Zend_Search_Lucene_Exception</code> бросается, если
                    ошибка произошла в самом парсере запросов.
<!--
                    <code>Zend_Search_Lucene_Exception</code> is thrown if something wrong in a query parser itself.
-->
                </para>
            </listitem>
            <listitem>
                <para>
                    <code>Zend_Search_Lucene_Search_QueryParserException</code>
                    бросается в случае ошибки синтаксиса запроса.
<!--
                    <code>Zend_Search_Lucene_Search_QueryParserException</code> is thrown in tha case of
                    query syntax exception.
-->
                </para>
            </listitem>
        </itemizedlist>
        Будет хорошей идеей отлавливать запросы
        Zend_Search_Lucene_Search_QueryParserException и выводить
        соответствующее сообщение.
<!--
        Thus that's good idea to catch Zend_Search_Lucene_Search_QueryParserException and make appropriate message:
-->
        <programlisting language="php"><![CDATA[<?php
try {
    $query = Zend_Search_Lucene_Search_QueryParser::parse($queryString);
} catch (Zend_Search_Lucene_Search_QueryParserException $e) {
    echo "Query syntax error: " . $e->getMessage() . "\n";
}]]></programlisting>
        </para>

        <para>
            Тот же самый подход может (и должен) применяться для метода
            find() объекта Zend_Search_Lucene.
<!--
            The same technique may (and should) be used for find() method of Zend_Search_Lucene object.
-->
        </para>
    </sect2>

    <sect2 id="zend.search.lucene.queries.term-query">
            <title>Простой запрос (запрос по одному ключевому слову)<!--Term Query--></title>
        <para>
        Простые запросы предназначены для поиска по одному элементу.
<!--
        Term queries are intended for a searching for a single term.
-->
        </para>

        <para>
        Строка запроса:
<!--
        Query string:
-->
        </para>
        <programlisting language="querystring"><![CDATA[word1]]>
        </programlisting>

        <para>или</para>

        <para>
        Построение запроса через API:
<!--
        Query construction by API:
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
$term  = new Zend_Search_Lucene_Index_Term('word1', 'field1');
$query = new Zend_Search_Lucene_Search_Query_Term($term);
$hits  = $index->find($query);]]></programlisting>

        <para>
            Параметр, задающий поле для поиска ('field1'), является
            опциональным. Zend_Search_Lucene производит поиск по всем
            полям, если поля для поиска не заданы.
<!--
            Term field is optional. Zend_Search_Lucene searches through all fields if field is not specified:
-->
            <programlisting language="php"><![CDATA[<?php
$term  = new Zend_Search_Lucene_Index_Term('word1');  // Search 'word1' through all indexed fields
$query = new Zend_Search_Lucene_Search_Query_Term($term);
$hits  = $index->find($query);]]></programlisting>
        </para>

    </sect2>

    <sect2 id="zend.search.lucene.queries.multiterm-query">
        <title>Составной запрос (запрос по нескольким ключевым словам)<!--Multi-Term Query--></title>
        <para>
            Составные запросы предназначены для поиска по набору элементов.
<!--
            Multi term queries are intended for a searching for a set of terms.
-->
        </para>

        <para>
            Каждый элемент в наборе может быть определен как "обязательный",
            "необязательный" или "запрещенный".
<!--
            Each term in a set can be defined as required, prohibited or neither
            required, nor prohibited (optional).
-->

            <itemizedlist>
                <listitem>
                    <para>
                        "обязательный" элемент означает, что документы, которые не соответствуют
                        данному элементу, не соответствуют всему запросу;
<!--
                        required which means that documents which do not match this term
                        will not match the query;
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                        "запрещенный" элемент означает, что документы, соответствующие
                        данному элементу, не соответствуют всему запросу;
<!--
                        prohibited which means that documents which do match this term
                        will not match the query;
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                         в случае "необязательного" элемента
                         документы не обязательно должны соответствовать
                         (или не иметь соответствия) элементу. Тем не менее,
                         документ должен соответствовать хотя бы одному элементу, чтобы
                         соответствовать запросу.
<!--
                        neither, in which case matched documents are neither prohibited from
                        nor required to match the term. However, a document must match at least
                        1 term to match the query.
-->
                    </para>
                </listitem>
            </itemizedlist>
        </para>

        <para>
            Это значит, что если необязательные элементы добавлены в запрос с
            обязательными, то они будут иметь одинаковый набор
            результатов, но в случае второго запроса хиты, соответствующие
            необязательным элементам, будут перемещены в верх списка
            результатов запроса.
<!--
            It means, that if optional terms are added to a query with required terms,
            then they will have the same result set, but second query will have hits,
            which match optional terms, moved to the top of result set.
-->
        </para>

        <para>
            Для составных запросов могут использоваться оба метода поиска.

<!--
            Both of a search method can be used for multi term queries.
-->
        </para>

        <para>
            Запрос в виде строки:
<!--
            Query string:
-->
        </para>
        <programlisting language="querystring"><![CDATA[+word1 author:word2 -word3]]></programlisting>

        <itemizedlist>
            <listitem>
                <para>
                    '+' используется для определения обязательного элемента
                    <!--is used to define required term.-->
                </para>
            </listitem>
            <listitem>
                <para>
                    '-' используется для определения запрещенного элемента
                    <!--is used to define prohibited term.-->
                </para>
            </listitem>
            <listitem>
                <para>
                    Приставка 'field:' используется, чтобы указать поле документа
                    для поиска. Если она опущена, то используется поле 'contents'.
<!--
                    'field:' prefix is used to indicate document field for a search.
                    If it's omitted, then 'contents' is used.
-->
                </para>
            </listitem>
        </itemizedlist>

        <para>или</para>

        <para>
        Построение запроса через программный интерфейс:
<!--
        Query construction by API:
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_MultiTerm();

$query->addTerm(new Zend_Search_Lucene_Index_Term('word1'), true);
$query->addTerm(new Zend_Search_Lucene_Index_Term('word2'), null);
$query->addTerm(new Zend_Search_Lucene_Index_Term('word3'), false);

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

        <para>
            Массив <varname>$signs</varname> содержит информацию о типах элементов:
<!--
            <varname>$signs</varname> array contains an information about term type:
-->
            <itemizedlist>
                <listitem>
                    <para>
                        <constant>TRUE</constant> используется для определения обязательных элементов.
<!--
                        true is used to define required term.
-->
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <constant>FALSE</constant> используется для определения запрещенных элементов.
<!--
                        false is used to define prohibited term.
-->
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <constant>NULL</constant> используется для определения необязательных
                        элементов.
<!--
                        null is used to define neither required,
                        nor prohibited term.
-->
                    </para>
                </listitem>
            </itemizedlist>
        </para>
    </sect2>

    <sect2 id="zend.search.lucene.queries.phrase-query">
        <title>Фразовый запрос<!--Phrase Query--></title>
        <para>
            Фразовые запросы предназначены для поиска по фразам.
<!--
            Phrase Queries are intended for a searching for a phrases.
-->
        </para>

        <para>
            Фразовые запросы являются очень гибкими и позволяют искать как
            точные фразы, так и неточные.
<!--
            Phrase Queries are very flexible and allow to search exact phrases as well as sloppy phrases.
-->
        </para>

        <para>
            Фразы могут содержать пропуски или несколько
            слов в одной и той же позиции. (Это может быть сгенерировано анализатором
            для различных целей. Например, элементы могут повторяться для
            повышения "веса" или в одной позиции могут быть размещены несколько
            синонимов). В соответствии с этим фразовые запросы
            могут строиться только через программный интерфейс:
<!--
            Phrases can also contain gaps or terms in the same places.
            (It can be generated by Analyser for different
            purposes. Ex. term can be duplicated to increase term
            weight or several synonyms can be placed into one position).
            According to this phrase queries can be constructed only by API now:
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
$query1 = new Zend_Search_Lucene_Search_Query_Phrase();

// Добавление 'word1' в относительную позицию 0.
$query1->addTerm(new Zend_Search_Lucene_Index_Term('word1'));

// Добавление 'word2' в относительную позицию 1.
$query1->addTerm(new Zend_Search_Lucene_Index_Term('word2'));

// Добавление 'word3' в относительную позицию 3.
$query1->addTerm(new Zend_Search_Lucene_Index_Term('word3'), 3);

...

$query2 = new Zend_Search_Lucene_Search_Query_Phrase(
                array('word1', 'word2', 'word3'), array(0,1,3));

...

// Запрос без промежутков.
$query3 = new Zend_Search_Lucene_Search_Query_Phrase(
                array('word1', 'word2', 'word3'));

...

$query4 = new Zend_Search_Lucene_Search_Query_Phrase(
                array('word1', 'word2'), array(0,1), 'annotation');]]></programlisting>

        <para>
            Фразовый запрос может строиться сразу с помощью конструктора класса
            или пошагово с помощью метода
            <code>Zend_Search_Lucene_Search_Query_Phrase::addTerm()</code>.
<!--
            Phrase query can be constructed by one step with a class constructor or step by step with a
            <code>Zend_Search_Lucene_Search_Query_Phrase::addTerm()</code> method.
-->
        </para>

        <para>
            Конструктор класса <code>Zend_Search_Lucene_Search_Query_Phrase</code>
            принимает три необязательных параметра:
<!--
            Zend_Search_Lucene_Search_Query_Phrase class constructor takes
            three optional arguments:
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
Zend_Search_Lucene_Search_Query_Phrase([array $terms[, array $offsets[, string $field]]]);]]></programlisting>

        <para>
            <varname>$terms</varname> является массивом строк, который содержит набор
            элементов фразы. Если он опущен или равен <constant>NULL</constant>,
            то строится пустой запрос.
<!--
            <varname>$terms</varname> is an array of strings, which contains a set of phrase terms.
            If it's omitted or equal to null, then empty query is constructed.
-->
        </para>

        <para>
            <varname>$offsets</varname> является массивом целочисленных значений,
            который содержит смещения элементов во фразе. Если он опущен или равен
            <constant>NULL</constant>, то позиции элементов предполагаются как
            <code>array(0, 1, 2, 3, ...)</code>.
<!--
            <varname>$offsets</varname> is an array of integers, which contains offsets of terms in a phrase.
            If it's omitted or equal to null, then terms positions are supposed as <code>array(0, 1, 2, 3, ...)</code>.
-->
        </para>

        <para>
            <varname>$field</varname> является строкой, которая указывает поле
            документа, в котором производится поиск. Если он опущен или равен
            <constant>NULL</constant>, то поиск производится в поле по умолчанию.
            Данная версия Zend_Search_Lucene трактует поле 'contents'
            как поле, выбираемое по умолчанию, но в следующих версиях
            планируется добавить возможность указания любого поля,
            как выбираемого по умолчанию.
<!--
            <varname>$field</varname> is a string, which indicates searched document field.
            If it's omitted or equal to null, then default field is searched.
            This version of Zend_Search_Lucene treates 'contents' field as a default, but it's planned to change this
            behavior to "any field" in next versions.
-->
        </para>

        <para>
            Таким образом:
<!--
            Thus:
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase(array('zend', 'framework'));]]></programlisting>
        <para>
            будет искать фразу 'zend framework'.
<!--
            will search for 'zend framework' phrase.
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase(array('zend', 'download'), array(0, 2));]]></programlisting>
        <para>
            будет искать фразу 'zend ????? download', такой запрос соответствует фразам
            'zend platform download', 'zend studio download',
            'zend core download', 'zend framework download' и т.д.
<!--
            will search for 'zend ????? download' phrase and match 'zend platform download', 'zend studio download',
            'zend core download', 'zend framework download' and so on.
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase(array('zend', 'framework'), null, 'title');]]></programlisting>

        <para>
            будет искать фразу 'zend framework' в поле 'title'.
<!--
            will search for 'zend framework' phrase in a 'title' field.
-->
        </para>

        <para>
            Метод <code>Zend_Search_Lucene_Search_Query_Phrase::addTerm()</code>
            принимает два аргумента — объект элемента
            <code>Zend_Search_Lucene_Index_Term</code> в качестве обязательного
            параметра и позицию элемента в качестве необязательного:
<!--
            <code>Zend_Search_Lucene_Search_Query_Phrase::addTerm()</code> method
            takes two arguments.
            Required <code>Zend_Search_Lucene_Index_Term</code> object and optional position:
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
Zend_Search_Lucene_Search_Query_Phrase::addTerm(Zend_Search_Lucene_Index_Term $term[, integer $position]);]]></programlisting>

        <para>
            <varname>$term</varname> описывает следующий элемент во фразе. Он должен
            указывать на то же самое поле, что и предыдущие элементы,
            иначе будет сгенерировано исключение.
<!--
            <varname>$term</varname> describes next term in a phrase. It must indicate
            the same field as previous terms.
            Otherwise an exception will be thrown.
-->
        </para>

        <para>
            <varname>$position</varname> указывает позицию элемента.
<!--
            <varname>$position</varname> indicates term position.
-->
        </para>

        <para>
<!--
            Thus:
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase();
$query->addTerm(new Zend_Search_Lucene_Index_Term('zend'));
$query->addTerm(new Zend_Search_Lucene_Index_Term('framework'));]]></programlisting>
        <para>
            будет искать фразу 'zend framework'.
<!--
            will search for 'zend framework' phrase.
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase();
$query->addTerm(new Zend_Search_Lucene_Index_Term('zend'), 0);
$query->addTerm(new Zend_Search_Lucene_Index_Term('framework'), 2);]]></programlisting>
        <para>
            будет искать фразу 'zend ????? download', такой запрос соответствует фразам
            'zend platform download', 'zend studio download',
            'zend core download', 'zend framework download' и т.д.
<!--
            will search for 'zend ????? download' phrase and match 'zend platform download', 'zend studio download',
            'zend core download', 'zend framework download' and so on.
-->
        </para>
        <programlisting language="php"><![CDATA[<?php
$query = new Zend_Search_Lucene_Search_Query_Phrase();
$query->addTerm(new Zend_Search_Lucene_Index_Term('zend', 'title'));
$query->addTerm(new Zend_Search_Lucene_Index_Term('framework', 'title'));]]></programlisting>
        <para>
            будет искать фразу 'zend framework' в поле 'title'.
<!--
            will search for 'zend framework' phrase in a 'title' field.
-->
        </para>

        <para>
            Коэффициент расстояния устанавливает допустимое количество других слов,
            находящихся между словами во фразе запроса. Если он равен нулю, то
            ищется точная фраза. При бОльших значениях это будет работать как
            оператор WITHIN или NEAR
<!--
            Sloop factor sets the number of other words permitted between words in query phrase. If zero,
            then this is an exact phrase search.  For larger values this works like a WITHIN or NEAR operator.
-->
        </para>

        <para>
            По сути, это управляемое расстояние, где единицы соответствуют
            перемене мест элементов относительно их позиции во фразе поиска.
            Например, для переключения порядка двух слов требуются две перемены мест
            (первое перемещение помещает слова друг над другом). Поэтому
            для того, чтобы разрешить изменение порядка фраз, коэффициент
            расстояния должен быть равен, как минимум, двум.
<!--
            The slop is in fact an edit-distance, where the units correspond to
            moves of terms in the query phrase
            out of position.  For example, to switch the order of two words
            requires two moves (the first move places
            the words atop one another), so to permit re-orderings of phrases,
            the slop must be at least two.
-->
        </para>

        <para>
            Более точные соответствия имеют бОльшую релевантность, чем менее точные,
            таким образом, результаты сортируются по точности. По умолчанию
            коэффициент расстояния равен нулю, что означает точное соответствие.
<!--
            More exact matches are scored higher than sloppier matches, thus search
            results are sorted by exactness.
            The slop is zero by default, requiring exact matches.
-->
        </para>

        <para>
            Коэффициент расстояния может быть указан после создания запроса:
<!--
            Sloop factor can be assigned after query creation:
-->
        </para>

        <programlisting language="php"><![CDATA[<?php
// Запрос без пропусков.
$query = new Zend_Search_Lucene_Search_Query_Phrase(array('word1', 'word2'));

// Поиск 'word1 word2', 'word1 ... word2'
$query->setSlop(1);
$hits1 = $index->find($query);

// Поиск 'word1 word2', 'word1 ... word2',
// 'word1 ... ... word2', 'word2 word1'
$query->setSlop(2);
$hits2 = $index->find($query);]]></programlisting>

    </sect2>
</sect1>

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