15.6. Расширяемость

15.6.1. Анализ текста

Класс Zend_Search_Lucene_Analysis_Analyzer используется индексатором для разбиения текстовых полей документа на лексемы.

Методы Zend_Search_Lucene_Analysis_Analyzer::getDefault() и Zend_Search_Lucene_Analysis_Analyzer::setDefault() используются для получения и установки анализатора по умолчанию.

Таким образом, вы можете устанавливать собственный анализатор текста или выбирать его из ряда предопределенных анализаторов: Zend_Search_Lucene_Analysis_Analyzer_Common_Text и Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive (по умолчанию). Оба интерпретируют лексему как последовательность букв. Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive приводит лексемы к нижнему регистру.

Переключение между анализаторами:

<?php

Zend_Search_Lucene_Analysis_Analyzer::setDefault(
    new Zend_Search_Lucene_Analysis_Analyzer_Common_Text());
...
$index->addDocument($doc);

?>

Zend_Search_Lucene_Analysis_Analyzer_Common создан для того, чтобы быть родительским классом для всех анализаторов, определяемых пользователем. Пользователь должен определить только метод tokenize(), который принимает входные данные в виде строки и возвращает массив лексем.

Метод tokenize() должен использовать метод normalize() для всех лексем. Он позволит использовать фильтры лексем с вашим анализатором.

Здесь приведен пример пользовательского анализатора, котрорый принимает слова с цифрами как элементы:

Пример 15.1. Собственный анализатор текста

<?php
/** Это созданный пользователем текстовый анализатор, который интерпретирует слова с цифрами как один элемент. */

/** Zend_Search_Lucene_Analysis_Analyzer_Common */
require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common.php';

class My_Analyzer extends Zend_Search_Lucene_Analysis_Analyzer_Common
{
    /**
     * Разбиение текста на лексемы
     * Returns array of Zend_Search_Lucene_Analysis_Token objects
     *
     * @param string $data
     * @return array
     */
    public function tokenize($data)
    {
        $tokenStream = array();

        $position = 0;
        while ($position < strlen($data)) {
            // пропуск пробелов
            while ($position < strlen($data) && !ctype_alpha($data{$position}) && !ctype_digit($data{$position})) {
                $position++;
            }

            $termStartPosition = $position;

            // чтение лексемы
            while ($position < strlen($data) && (ctype_alpha($data{$position}) || ctype_digit($data{$position}))) {
                $position++;
            }

            // Пустая лексема, конец потока.
            if ($position == $termStartPosition) {
                break;
            }

            $token = new Zend_Search_Lucene_Analysis_Token(substr($data,
                                             $termStartPosition,
                                             $position-$termStartPosition),
                                      $termStartPosition,
                                      $position);
            $tokenStream[] = $this->normalize($token);
        }

        return $tokenStream;
    }
}

Zend_Search_Lucene_Analysis_Analyzer::setDefault(
    new My_Analyzer());

?>

15.6.2. Алгоритмы ранжирования

Ранг q документа d определяется следующим образом:

score(q,d) = sum( tf(t in d) * idf(t) * getBoost(t.field in d) * lengthNorm(t.field in d) ) * coord(q,d) * queryNorm(q)

tf(t in d) - Zend_Search_Lucene_Search_Similarity::tf($freq) - коэффициент ранга, основанный на том, насколько часто встречается элемент или фраза в документе.

idf(t) - Zend_Search_Lucene_Search_SimilaritySimilarity::tf($term, $reader) - коэффициент ранга для простого элемента применительно к определенному индексу.

getBoost(t.field in d) - коэффициент усиления для поля элемента.

lengthNorm($term) - значение нормализации для поля, получаемое из общего количества элементов, содержащихся в поле. Это значение хранится внутри индекса. Эти значения вместе с коэффициентом усиления поля хранятся в индексе, результатом их умножения является ранг для каждого поля.

Совпадения в длинных полях менее точны, поэтому реализации этого метода обычно возвращают тем меньшие значения, чем больше число лексем, и тем большие значения, чем меньше число лексем.

сoord(q,d) - Zend_Search_Lucene_Search_Similarity::coord($overlap, $maxOverlap) - коэффициент ранга, основанный на относительной доле всех элементов запроса, найденных в документе.

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

queryNorm(q) - значение нормализации для запроса, получаемое из суммы возведенных в квадрат весов каждого из элементов запроса. Это значение затем умножается в вес каждого элемента запроса.

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

Алгоритм ранжирования может быть изменен через определение своего собственного класса. Для этого надо создать потомка класса Zend_Search_Lucene_Search_Similarity, как показано ниже, затем использовать метод Zend_Search_Lucene_Search_Similarity::setDefault($similarity); для установки объекта как используемого по умолчанию.

<?php

class MySimilarity extends Zend_Search_Lucene_Search_Similarity {
    public function lengthNorm($fieldName, $numTerms) {
        return 1.0/sqrt($numTerms);
    }

    public function queryNorm($sumOfSquaredWeights) {
        return 1.0/sqrt($sumOfSquaredWeights);
    }

    public function tf($freq) {
        return sqrt($freq);
    }

    /**
     * Сейчас не используется. Подсчитывает сумму соответствий неточной фразе,
     * основанную на изменяемом расстоянии.
     */
    public function sloppyFreq($distance) {
        return 1.0;
    }

    public function idfFreq($docFreq, $numDocs) {
        return log($numDocs/(float)($docFreq+1)) + 1.0;
    }

    public function coord($overlap, $maxOverlap) {
        return $overlap/(float)$maxOverlap;
    }
}

$mySimilarity = new MySimilarity();
Zend_Search_Lucene_Search_Similarity::setDefault($mySimilarity);

?>  

15.6.3. Контейнеры хранения

Абстрактный класс Zend_Search_Lucene_Storage_Directory определяет функционал директории.

Конструктор Zend_Search_Lucene использует строку или объект Zend_Search_Lucene_Storage_Directory как входные данные.

Zend_Search_Lucene_Storage_Directory_Filesystem реализует функционал директории для файловой системы.

Если для конструктора Zend_Search_Lucene в качестве входных данных испольуется строка, то считыватель индекса (объект Zend_Search_Lucene) рассматривает ее как путь в файловой системе и сама инстанцирует объекты Zend_Search_Lucene_Storage_Directory_Filesystem.

Вы можете определить собственную реализацию директории, создав потомка класса Zend_Search_Lucene_Storage_Directory.

Методы Zend_Search_Lucene_Storage_Directory:

<?php

abstract class Zend_Search_Lucene_Storage_Directory {
/**
 * Закрывает средство хранения.
 *
 * @return void
 */
abstract function close();


/**
 * Создает новый пустой файл с данным именем в директории.
 *
 * @param string $name
 * @return void
 */
abstract function createFile($filename);


/**
 * Удаляет существующий файл в директории.
 *
 * @param string $filename
 * @return void
 */
abstract function deleteFile($filename);


/**
 * Возвращает true, если файл с данным именем существует.
 *
 * @param string $filename
 * @return boolean
 */
abstract function fileExists($filename);


/**
 * Возвращает длину файла в директории.
 *
 * @param string $filename
 * @return integer
 */
abstract function fileLength($filename);


/**
 * Возвращает время последнего изменения файла в формате UNIX.
 *
 * @param string $filename
 * @return integer
 */
abstract function fileModified($filename);


/**
 * Переименовывает существующий файл в директории.
 *
 * @param string $from
 * @param string $to
 * @return void
 */
abstract function renameFile($from, $to);


/**
 * Устанавливает время изменения файла в текущее.
 *
 * @param string $filename
 * @return void
 */
abstract function touchFile($filename);


/**
 * Возвращает объект Zend_Search_Lucene_Storage_File для данного файла в директории.
 *
 * @param string $filename
 * @return Zend_Search_Lucene_Storage_File
 */
abstract function getFileObject($filename);

}

?>

Метод getFileObject($filename) класса Zend_Search_Lucene_Storage_Directory возвращает объект Zend_Search_Lucene_Storage_File.

Абстрактный класс Zend_Search_Lucene_Storage_File реализует абстракцию файла и примитивы чтения файла индекса.

Вы должны создать класс, наследующий от Zend_Search_Lucene_Storage_File для своей реализации директории.

Только два метода класса Zend_Search_Lucene_Storage_File должны быть перегружены в вашей реализации:

<?php

class MyFile extends Zend_Search_Lucene_Storage_File {
    /**
     * Устанавливает индикатор позиции и перемещает указатель файла.
     * Новая позиция, измеряемая в байтах от начала файла,
     * получается добавлением смещения к позиции, определяемой аргументом $whence,
     * который может принимать следующие значения:
     * SEEK_SET - Устанавливает позицию равной смещению в байтах.
     * SEEK_CUR - Устанавливает позицию равной текущей позиции плюс смещение.
     * SEEK_END - Устанавливает позицию равной концу файла плюс смещение.
     * (Для перемещения позиции относительно конца файла вы должны передать отрицательное значение смещения)
     * В случае успеха возвращает 0; иначе -1
     *
     * @param integer $offset
     * @param integer $whence
     * @return integer
     */
    public function seek($offset, $whence=SEEK_SET) {
        ...
    }

    /**
     * Считывает $length байт из файла и перемещает указатель файла.
     *
     * @param integer $length
     * @return string
     */
    protected function _fread($length=1) {
        ...
    }
}

?>