TigerZF
🌐Español

61.7. Extensibilidad

61.7.1. Análisis de texto

La clase Zend_Search_Lucene_Analysis_Analyzer es utilizada por el indexador para tokenizar los campos de texto de los documentos.

Los métodos Zend_Search_Lucene_Analysis_Analyzer::getDefault() y Zend_Search_Lucene_Analysis_Analyzer::setDefault() se utilizan para obtener y establecer el analizador por defecto.

Puede asignar su propio analizador de texto o elegirlo del conjunto de analizadores predefinidos: Zend_Search_Lucene_Analysis_Analyzer_Common_Text y Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive (por defecto). Ambos interpretan los tokens como secuencias de letras. Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive convierte todos los tokens a minúsculas.

Para cambiar entre analizadores:

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

La clase Zend_Search_Lucene_Analysis_Analyzer_Common está diseñada para ser el antecesor de todos los analizadores definidos por el usuario. El usuario solo debe definir los métodos reset() y nextToken(), que toman su cadena del miembro $_input y devuelven los tokens uno a uno (un valor NULL indica el final del flujo).

El método nextToken() debe llamar al método normalize() en cada token. Esto le permitirá usar filtros de tokens con su analizador.

A continuación se muestra un ejemplo de un analizador personalizado, que acepta palabras con dígitos como términos:

Ejemplo 61.1. Analizador de texto personalizado

/**
 * Here is a custom text analyser, which treats words with digits as
 * one term
 */

class My_Analyzer extends Zend_Search_Lucene_Analysis_Analyzer_Common
{
    private $_position;

    /**
     * Reset token stream
     */
    public function reset()
    {
        $this->_position = 0;
    }

    /**
     * Tokenization stream API
     * Get next token
     * Returns null at the end of stream
     *
     * @return Zend_Search_Lucene_Analysis_Token|null
     */
    public function nextToken()
    {
        if ($this->_input === null) {
            return null;
        }

        while ($this->_position < strlen($this->_input)) {
            // skip white space
            while ($this->_position < strlen($this->_input) &&
                   !ctype_alnum( $this->_input[$this->_position] )) {
                $this->_position++;
            }

            $termStartPosition = $this->_position;

            // read token
            while ($this->_position < strlen($this->_input) &&
                   ctype_alnum( $this->_input[$this->_position] )) {
                $this->_position++;
            }

            // Empty token, end of stream.
            if ($this->_position == $termStartPosition) {
                return null;
            }

            $token = new Zend_Search_Lucene_Analysis_Token(
                                      substr($this->_input,
                                             $termStartPosition,
                                             $this->_position -
                                             $termStartPosition),
                                      $termStartPosition,
                                      $this->_position);
            $token = $this->normalize($token);
            if ($token !== null) {
                return $token;
            }
            // Continue if token is skipped
        }

        return null;
    }
}

Zend_Search_Lucene_Analysis_Analyzer::setDefault(
    new My_Analyzer());


61.7.2. Filtrado de tokens

El analizador Zend_Search_Lucene_Analysis_Analyzer_Common también ofrece un mecanismo de filtrado de tokens.

La clase Zend_Search_Lucene_Analysis_TokenFilter proporciona una interfaz abstracta para dichos filtros. Sus propios filtros deben extender esta clase, ya sea directa o indirectamente.

Cualquier filtro personalizado debe implementar el método normalize(), que puede transformar el token de entrada o indicar que el token actual debe omitirse.

Ya existen tres filtros definidos en el subpaquete de análisis:

  • Zend_Search_Lucene_Analysis_TokenFilter_LowerCase

  • Zend_Search_Lucene_Analysis_TokenFilter_ShortWords

  • Zend_Search_Lucene_Analysis_TokenFilter_StopWords

El filtro LowerCase ya se utiliza por defecto en el analizador Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive.

Los filtros ShortWords y StopWords pueden usarse con analizadores predefinidos o personalizados de la siguiente manera:

$stopWords = array('a', 'an', 'at', 'the', 'and', 'or', 'is', 'am');
$stopWordsFilter =
    new Zend_Search_Lucene_Analysis_TokenFilter_StopWords($stopWords);

$analyzer =
    new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
$analyzer->addFilter($stopWordsFilter);

Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);
$shortWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_ShortWords();

$analyzer =
    new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
$analyzer->addFilter($shortWordsFilter);

Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);

El constructor de Zend_Search_Lucene_Analysis_TokenFilter_StopWords recibe un array de palabras vacías como entrada. Pero las palabras vacías también pueden cargarse desde un archivo:

$stopWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_StopWords();
$stopWordsFilter->loadFromFile($my_stopwords_file);

$analyzer =
   new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
$analyzer->addFilter($stopWordsFilter);

Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);

Este archivo debe ser un archivo de texto común con una palabra en cada línea. El carácter '#' marca una línea como comentario.

El constructor de Zend_Search_Lucene_Analysis_TokenFilter_ShortWords tiene un argumento opcional. Este es el límite de longitud de palabra, establecido por defecto en 2.

61.7.3. Algoritmos de puntuación

La puntuación de un documento d para una consulta q se define como sigue:

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) - un factor de puntuación basado en la frecuencia de un término o frase en un documento.

idf(t) - Zend_Search_Lucene_Search_Similarity::idf($input, $reader) - un factor de puntuación para un término simple con el índice especificado.

getBoost(t.field in d) - el factor de refuerzo para el campo del término.

lengthNorm($term) - el valor de normalización para un campo dado el número total de términos contenidos en dicho campo. Este valor se almacena dentro del índice. Estos valores, junto con los refuerzos de campo, se almacenan en un índice y se multiplican en las puntuaciones de las coincidencias de cada campo mediante el código de búsqueda.

Las coincidencias en campos más largos son menos precisas, por lo que las implementaciones de este método suelen devolver valores más pequeños cuando numTokens es grande, y valores más grandes cuando numTokens es pequeño.

coord(q,d) - Zend_Search_Lucene_Search_Similarity::coord($overlap, $maxOverlap) - un factor de puntuación basado en la fracción de todos los términos de la consulta que contiene un documento.

La presencia de una gran parte de los términos de la consulta indica una mejor coincidencia con la consulta, por lo que las implementaciones de este método suelen devolver valores mayores cuando la proporción entre estos parámetros es grande y valores menores cuando la proporción entre ellos es pequeña.

queryNorm(q) - el valor de normalización para una consulta dada la suma de los pesos al cuadrado de cada uno de los términos de la consulta. Este valor se multiplica luego por el peso de cada término de la consulta.

Esto no afecta a la clasificación, sino que simplemente intenta hacer que las puntuaciones de distintas consultas sean comparables.

El algoritmo de puntuación puede personalizarse definiendo su propia clase Similarity. Para ello extienda la clase Zend_Search_Lucene_Search_Similarity como se define a continuación, y luego use el método Zend_Search_Lucene_Search_Similarity::setDefault($similarity); para establecerla como predeterminada.

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);
    }

    /**
     * It's not used now. Computes the amount of a sloppy phrase match,
     * based on an edit distance.
     */
    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);

61.7.4. Contenedores de almacenamiento

La clase abstracta Zend_Search_Lucene_Storage_Directory define la funcionalidad de directorio.

El constructor de Zend_Search_Lucene utiliza como entrada una cadena o un objeto Zend_Search_Lucene_Storage_Directory.

La clase Zend_Search_Lucene_Storage_Directory_Filesystem implementa la funcionalidad de directorio para un sistema de archivos.

Si se usa una cadena como entrada para el constructor de Zend_Search_Lucene, el lector de índices (objeto Zend_Search_Lucene) la trata como una ruta del sistema de archivos e instancia el objeto Zend_Search_Lucene_Storage_Directory_Filesystem.

Puede definir su propia implementación de directorio extendiendo la clase Zend_Search_Lucene_Storage_Directory.

Métodos de Zend_Search_Lucene_Storage_Directory:

abstract class Zend_Search_Lucene_Storage_Directory {
/**
 * Closes the store.
 *
 * @return void
 */
abstract function close();

/**
 * Creates a new, empty file in the directory with the given $filename.
 *
 * @param string $name
 * @return void
 */
abstract function createFile($filename);

/**
 * Removes an existing $filename in the directory.
 *
 * @param string $filename
 * @return void
 */
abstract function deleteFile($filename);

/**
 * Returns true if a file with the given $filename exists.
 *
 * @param string $filename
 * @return boolean
 */
abstract function fileExists($filename);

/**
 * Returns the length of a $filename in the directory.
 *
 * @param string $filename
 * @return integer
 */
abstract function fileLength($filename);

/**
 * Returns the UNIX timestamp $filename was last modified.
 *
 * @param string $filename
 * @return integer
 */
abstract function fileModified($filename);

/**
 * Renames an existing file in the directory.
 *
 * @param string $from
 * @param string $to
 * @return void
 */
abstract function renameFile($from, $to);

/**
 * Sets the modified time of $filename to now.
 *
 * @param string $filename
 * @return void
 */
abstract function touchFile($filename);

/**
 * Returns a Zend_Search_Lucene_Storage_File object for a given
 * $filename in the directory.
 *
 * @param string $filename
 * @return Zend_Search_Lucene_Storage_File
 */
abstract function getFileObject($filename);

}

El método getFileObject($filename) de una instancia de Zend_Search_Lucene_Storage_Directory devuelve un objeto Zend_Search_Lucene_Storage_File.

La clase abstracta Zend_Search_Lucene_Storage_File implementa la abstracción de archivos y las primitivas de lectura de archivos de índice.

También debe extender Zend_Search_Lucene_Storage_File para su implementación de directorio.

Solo dos métodos de Zend_Search_Lucene_Storage_File deben sobrescribirse en su implementación:

class MyFile extends Zend_Search_Lucene_Storage_File {
    /**
     * Sets the file position indicator and advances the file pointer.
     * The new position, measured in bytes from the beginning of the file,
     * is obtained by adding offset to the position specified by whence,
     * whose values are defined as follows:
     * SEEK_SET - Set position equal to offset bytes.
     * SEEK_CUR - Set position to current location plus offset.
     * SEEK_END - Set position to end-of-file plus offset. (To move to
     * a position before the end-of-file, you need to pass a negative value
     * in offset.)
     * Upon success, returns 0; otherwise, returns -1
     *
     * @param integer $offset
     * @param integer $whence
     * @return integer
     */
    public function seek($offset, $whence=SEEK_SET) {
        ...
    }

    /**
     * Read a $length bytes from the file and advance the file pointer.
     *
     * @param integer $length
     * @return string
     */
    protected function _fread($length=1) {
        ...
    }
}