12.6. Extensibilité

12.6.1. Analyse textuelle

La classe Zend_Search_Lucene_Analysis_Analyzer est utilisée par l'indexeur pour tokenizer les champs texte du document.

Les méthodes Zend_Search_Lucene_Analysis_Analyzer::getDefault() et Zend_Search_Lucene_Analysis_Analyzer::setDefault() sont utilisée pour obtenir (get) et définir (set) l'analyseur par défaut.

Ainsi, vous pouvez assigner votre propre analyseur de texte, ou alors en choisir un dans le jeu des analyseurs prédéfinis : Zend_Search_Lucene_Analysis_Analyzer_Common_Text et Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive (par défaut). Les deux interpètrent le token comme une séquence de lettres. Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive convertit les tokens en minuscule.

Pour passer d'un analyseur à un autre, utilisez le code suivant :

<?php

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

?>

Zend_Search_Lucene_Analysis_Analyzer_Common a été créée pour être un parent de tous les analyseurs définis pas l'utilisateur. L'utilisateur ne devrait définir que la méthode tokenize(), qui prend en entrée une chaîne de caractères, et retourne un tableau de tokens.

La méthode tokenize() devrait appliquer la méthode normalize() à tous les jetons. Cela permettra d'utiliser des filtres sur les tokens, avec votre analyseur.

Voici un exemple d'Analyser personnalisé, qui prend pour termes des mots de 2 chiffres :

Exemple 12.1. Analyseur de text personnalisé

<?php
/** Voici un analyseur de texte personnalisé, qui traite les mots contenant des chiffres comme un seul terme */

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

class Mon_Analyseur extends Zend_Search_Lucene_Analysis_Analyzer_Common
{
    /**
     * Tokenisation du text en termes
     * Retourne un tableau d'objets Zend_Search_Lucene_Analysis_Token
     *
     * @param string $data
     * @return array
     */
    public function tokenize($data)
    {
        $tokenStream = array();

        $position = 0;
        while ($position < strlen($data)) {
            // skip white space
            while ($position < strlen($data) && !ctype_alpha($data{$position}) && !ctype_digit($data{$position})) {
                $position++;
            }

            $termStartPosition = $position;

            // lecture du token 
            while ($position < strlen($data) && (ctype_alpha($data{$position}) || ctype_digit($data{$position}))) {
                $position++;
            }

            // token vide, fin du flux
            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 Mon_Analyseur());

?>

12.6.2. Algorithme de scoring

Le score d'une requête q pour un document d est définis comme ceci :

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 facteur de score basé sur la fréquence des termes ou des phrases dans le document.

idf(t) - Zend_Search_Lucene_Search_SimilaritySimilarity::tf($term, $reader) - un facteur de score pour un terme simple, pour l'index specifié.

getBoost(t.field in d) - facteur de boost pour le champ du terme.

lengthNorm($term) - La valeur de normalisation pour un champ, à partir du nombre total de termes contenus dans ce champ. Cette valeur est stockée dans l'index. Ces valeurs, avec fieldBoost, sont stockées dans un index et multipliés dans les scores de hits, sur chaque champ, par le code de recherche.

Matches in longer fields are less precise, so implementations of this method usually return smaller values when numTokens is large, and larger values when numTokens is small.

coord(q,d) - Zend_Search_Lucene_Search_Similarity::coord($overlap, $maxOverlap) - un facteur de score basé sur la fraction de tous les termes de requête qu'un document contient.

La présence d'une large portion de termes de requête indique une meilleure correspondance avec la requête, ainsi les implémentations de cette méthode retournent des plus grandes valeurs quand le ratio entre ces paramètres est élevé, et des plus petites valeurs quand le ratio entre eux est faible.

queryNorm(q) - la valeur de normalisation pour une requête, à partir de la somme des carrés des poids de chacun des termes de la requête. Cette valeur est ensuite multipliée par le poid de chaque terme de requête.

Cela n'affecte pas le ranking, mais tente plutôt de rendre comparable des scores provenant de requêtes différentes.

L'algorithme de scoring peut-être personnalisé en définissant votre propre classe Similarity. Pour ce faire étendez la classe Zend_Search_Lucene_Search_Similarity comme montré ci-dessous, et utilisez ensuite la méthode Zend_Search_Lucene_Search_Similarity::setDefault($similarity); pour la définir par défaut.

<?php

class MaSimilarite 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 MaSimilarite();
Zend_Search_Lucene_Search_Similarity::setDefault($mySimilarity);

?>

12.6.3. API de Stockage

Une classe abstraite Zend_Search_Lucene_Storage_Directory définit les fonctionnalités relatives au répertoires.

Le constructeur de Zend_Search_Lucene utilise soit une chaine soit un objet Zend_Search_Lucene_Storage_Directory comme entrée.

La classe Zend_Search_Lucene_Storage_Directory_Filesystem implémente les fonctionnalités de répertoires pour le système de fichier.

Si une chaîne est utilisée comme entrée dans le constructeur de Zend_Search_Lucene, alors le lecteur d'index (objet Zend_Search_Lucene) le traite comme un chemin du système de fichier, et instantie un objet Zend_Search_Lucene_Storage_Directory_Filesystem de lui-même.

Vous pouvez faire votre propre implementation des répertoires en étendant la classe Zend_Search_Lucene_Storage_Directory.

Méthodes de Zend_Search_Lucene_Storage_Directory :

<?php

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

}

?>

La méthode getFileObject($filename) de la classe Zend_Search_Lucene_Storage_Directory retourne un objet Zend_Search_Lucene_Storage_File.

La classe abstraite Zend_Search_Lucene_Storage_File implemente l'abstraction des fichiers et de la lecture des fichiers d'index.

Vous devez bien sur étendre la classe Zend_Search_Lucene_Storage_File pour votre implémentation de Directory.

Seules deux méthodes de la classe Zend_Search_Lucene_Storage_File doit être surchargé dans votre implementation:

<?php

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) {
        ...
    }
}

?>