文字セット

拡張性

テキスト解析

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 はトークンを小文字に変換します。

解析器を変更するには、以下のようにします。

  1. Zend_Search_Lucene_Analysis_Analyzer::setDefault(
  2.     new Zend_Search_Lucene_Analysis_Analyzer_Common_Text());
  3. ...
  4. $index->addDocument($doc);

ユーザ定義の解析器のための共通の親クラスとして設計されているのが Zend_Search_Lucene_Analysis_Analyzer_Common です。 ユーザが定義しなければならないのは reset() および nextToken() メソッドのみで、 これは文字列を $_input から受け取って順に返します (NULL が最後のデータを表します)。

nextToken() メソッドでは、各トークンに対して normalize() メソッドを適用しなければなりません。 これにより、作成した解析器をトークンフィルタとして使用できるようになります。

独自のテキスト解析器の例を示します。 これは、数字つきの単語をひとつの言葉として扱います。

Example #1 独自のテキスト解析器

  1. /**
  2. * これは独自のテキスト解析器で、数字つきの単語をひとつの言葉として
  3. * 扱います
  4. */
  5.  
  6. class My_Analyzer extends Zend_Search_Lucene_Analysis_Analyzer_Common
  7. {
  8.     private $_position;
  9.  
  10.     /**
  11.      * トークンストリームをリセットします
  12.      */
  13.     public function reset()
  14.     {
  15.         $this->_position = 0;
  16.     }
  17.  
  18.     /**
  19.      * トークンストリーム API
  20.      * 次のトークンを取得します。
  21.      * ストリームの最後に達すると null を返します。
  22.      *
  23.      * @return Zend_Search_Lucene_Analysis_Token|null
  24.      */
  25.     public function nextToken()
  26.     {
  27.         if ($this->_input === null) {
  28.             return null;
  29.         }
  30.  
  31.         while ($this->_position < strlen($this->_input)) {
  32.             // 空白を読み飛ばします
  33.             while ($this->_position < strlen($this->_input) &&
  34.                    !ctype_alnum( $this->_input[$this->_position] )) {
  35.                 $this->_position++;
  36.             }
  37.  
  38.             $termStartPosition = $this->_position;
  39.  
  40.             // トークンを読み込みます
  41.             while ($this->_position < strlen($this->_input) &&
  42.                    ctype_alnum( $this->_input[$this->_position] )) {
  43.                 $this->_position++;
  44.             }
  45.  
  46.             // 空のトークン、あるいはストリームが終了
  47.             if ($this->_position == $termStartPosition) {
  48.                 return null;
  49.             }
  50.  
  51.             $token = new Zend_Search_Lucene_Analysis_Token(
  52.                                       substr($this->_input,
  53.                                              $termStartPosition,
  54.                                              $this->_position -
  55.                                              $termStartPosition),
  56.                                       $termStartPosition,
  57.                                       $this->_position);
  58.             $token = $this->normalize($token);
  59.             if ($token !== null) {
  60.                 return $token;
  61.             }
  62.             // トークンがスキップされた場合は継続します
  63.         }
  64.  
  65.         return null;
  66.     }
  67. }
  68.  
  69. Zend_Search_Lucene_Analysis_Analyzer::setDefault(
  70.     new My_Analyzer());

トークンのフィルタリング

Zend_Search_Lucene_Analysis_Analyzer_Common 解析器には、トークンをフィルタリングする仕組みもあります。 mechanism.

Zend_Search_Lucene_Analysis_TokenFilter クラスは、このフィルタリングの仕組みを抽象化したものです。 自分でフィルタを作成する際には、これを継承します。

独自に作成するフィルタは、 normalize() メソッドを実装する必要があります。 このメソッドは、入力トークンを変換したり トークンを読み飛ばす指示を出したりします。

Analysis のサブパッケージとして、これらの三つのフィルタが定義されています。

  • Zend_Search_Lucene_Analysis_TokenFilter_LowerCase

  • Zend_Search_Lucene_Analysis_TokenFilter_ShortWords

  • Zend_Search_Lucene_Analysis_TokenFilter_StopWords

LowerCase フィルタは、既に Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive 解析器で使用されています。これはデフォルトの解析器です。

ShortWords および StopWords は、定義済み解析器あるいは独自の解析器でこのように使用します。

  1. $stopWords = array('a', 'an', 'at', 'the', 'and', 'or', 'is', 'am');
  2. $stopWordsFilter =
  3.     new Zend_Search_Lucene_Analysis_TokenFilter_StopWords($stopWords);
  4.  
  5. $analyzer =
  6.     new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
  7. $analyzer->addFilter($stopWordsFilter);
  8.  
  9. Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);
  1. $shortWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_ShortWords();
  2.  
  3. $analyzer =
  4.     new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
  5. $analyzer->addFilter($shortWordsFilter);
  6.  
  7. Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);

Zend_Search_Lucene_Analysis_TokenFilter_StopWords のコンストラクタには、禁止単語の配列を入力として渡します。 この禁止単語はファイルから読み込ませることもできます。

  1. $stopWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_StopWords();
  2. $stopWordsFilter->loadFromFile($my_stopwords_file);
  3.  
  4. $analyzer =
  5.    new Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive();
  6. $analyzer->addFilter($stopWordsFilter);
  7.  
  8. Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);

ファイル形式は一般的なテキストファイルで、各文字列にひとつの単語が含まれるものとなります。 '#' を指定すると、その文字列はコメントであるとみなします。

Zend_Search_Lucene_Analysis_TokenFilter_ShortWords のコンストラクタには、オプションの引数をひとつ指定できます。 これは単語長の制限を表し、デフォルト値は 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_Similarity::idf($input, $reader) - 指定したインデックスに対する単純な単語の重み要素。

getBoost(t.field in d) - 単語のフィールドの重み。

lengthNorm($term) - フィールド内に含まれる単語の総数を正規化した値。 この値はインデックスに保存されます。 これらの値はフィールドの重みとともにインデックスに保存され、 検索コードによってヒットした各フィールドのスコアに掛けられます。

長いフィールドでマッチした場合は、あまり的確であるとはいえません。 そのため、このメソッドの実装は通常、 numTokens が大きいときにはより小さな値、 numTokens が小さいときにはより大きな値を返すようになっています。

coord(q,d) - Zend_Search_Lucene_Search_Similarity::coord($overlap, $maxOverlap) - ドキュメントに含まれる、検索対象の全単語の部分一致に基づく重み要素。

検索対象の単語のより多くの部分が存在しているほど、 検索結果としてよいものであるといえます。そのため、このメソッドの実装は通常、 これらのパラメータの割合が大きいときにはより大きな値、 割合が小さいときにはより小さな値を返すようになっています。

queryNorm(q) - 検索対象の各単語の重みの二乗の和で与えられる、クエリの正規化値。 この値は、検索対象の各単語の重みに掛けられます。

これは重み付けには影響しません。単に別のクエリの結果との差をなくすために使用されます。

重み付けのアルゴリズムを変更するには、独自の Similatity クラスを定義します。そのためには以下のように Zend_Search_Lucene_Search_Similarity クラスを継承し、 Zend_Search_Lucene_Search_Similarity::setDefault($similarity); メソッドでそれをデフォルトとして設定します。

  1. class MySimilarity extends Zend_Search_Lucene_Search_Similarity {
  2.     public function lengthNorm($fieldName, $numTerms) {
  3.         return 1.0/sqrt($numTerms);
  4.     }
  5.  
  6.     public function queryNorm($sumOfSquaredWeights) {
  7.         return 1.0/sqrt($sumOfSquaredWeights);
  8.     }
  9.  
  10.     public function tf($freq) {
  11.         return sqrt($freq);
  12.     }
  13.  
  14.     /**
  15.      * 現在は使用しません。曖昧検索の曖昧度を計算します。
  16.      */
  17.     public function sloppyFreq($distance) {
  18.         return 1.0;
  19.     }
  20.  
  21.     public function idfFreq($docFreq, $numDocs) {
  22.         return log($numDocs/(float)($docFreq+1)) + 1.0;
  23.     }
  24.  
  25.     public function coord($overlap, $maxOverlap) {
  26.         return $overlap/(float)$maxOverlap;
  27.     }
  28. }
  29.  
  30. $mySimilarity = new MySimilarity();
  31. Zend_Search_Lucene_Search_Similarity::setDefault($mySimilarity);

保存先

抽象クラス 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 のメソッドは以下のとおりです。

  1. abstract class Zend_Search_Lucene_Storage_Directory {
  2. /**
  3. * 保存先を閉じます
  4. *
  5. * @return void
  6. */
  7. abstract function close();
  8.  
  9. /**
  10. * $filename という名前の新しい空のファイルを、ディレクトリ内に作成します
  11. *
  12. * @param string $name
  13. * @return void
  14. */
  15. abstract function createFile($filename);
  16.  
  17. /**
  18. * 既存の $filename をディレクトリから削除します
  19. *
  20. * @param string $filename
  21. * @return void
  22. */
  23. abstract function deleteFile($filename);
  24.  
  25. /**
  26. * $filename で指定したファイルが存在する場合に true を返します
  27. *
  28. * @param string $filename
  29. * @return boolean
  30. */
  31. abstract function fileExists($filename);
  32.  
  33. /**
  34. * ディレクトリ内の $filename の長さを返します
  35. *
  36. * @param string $filename
  37. * @return integer
  38. */
  39. abstract function fileLength($filename);
  40.  
  41. /**
  42. * $filename の最終更新日時を UNIX タイムスタンプで返します
  43. *
  44. * @param string $filename
  45. * @return integer
  46. */
  47. abstract function fileModified($filename);
  48.  
  49. /**
  50. * ディレクトリ内の既存のファイルの名前を変更します
  51. *
  52. * @param string $from
  53. * @param string $to
  54. * @return void
  55. */
  56. abstract function renameFile($from, $to);
  57.  
  58. /**
  59. * $filename の更新時刻を現在の時刻にします
  60. *
  61. * @param string $filename
  62. * @return void
  63. */
  64. abstract function touchFile($filename);
  65.  
  66. /**
  67. * ディレクトリ内の $filename についての
  68. * Zend_Search_Lucene_Storage_File オブジェクトを返します
  69. *
  70. * @param string $filename
  71. * @return Zend_Search_Lucene_Storage_File
  72. */
  73. abstract function getFileObject($filename);
  74.  
  75. }

Zend_Search_Lucene_Storage_Directory クラスの getFileObject($filename) メソッドは、 Zend_Search_Lucene_Storage_File オブジェクトを返します。

抽象クラス Zend_Search_Lucene_Storage_File では、 ファイルの抽象化およびインデックスファイルの基本的な読み込み機能を実装しています。

ディレクトリ機能を実装するには Zend_Search_Lucene_Storage_File クラスを継承しなければなりません。

Zend_Search_Lucene_Storage_File クラスを実装する際に オーバーロードしなければならないメソッドは 2 つだけです。

  1. class MyFile extends Zend_Search_Lucene_Storage_File {
  2.     /**
  3.      * ファイル上の位置を指定し、そこにファイルポインタを進めます。
  4.      * 新しい位置は、whence で指定した場所からオフセットのバイト数だけ
  5.      * 進めた位置になります。whence に指定できる値は以下のいずれかです。
  6.      * SEEK_SET - 先頭からオフセット分進めた位置に移動します。
  7.      * SEEK_CUR - 現在位置からオフセット分だけ進めた位置に移動します。
  8.      * SEEK_END - ファイルの終端からオフセット分だけ進めた位置に移動します。
  9.      * (ファイルの終端から戻った位置を指定するには、オフセットに負の値を
  10.      * 指定する必要があります)
  11.      * 成功した場合に 0、それ以外の場合に -1 を返します。
  12.      *
  13.      * @param integer $offset
  14.      * @param integer $whence
  15.      * @return integer
  16.      */
  17.     public function seek($offset, $whence=SEEK_SET) {
  18.         ...
  19.     }
  20.  
  21.     /**
  22.      * ファイルから $length バイトを読み込み、ファイルポインタを進めます。
  23.      *
  24.      * @param integer $length
  25.      * @return string
  26.      */
  27.     protected function _fread($length=1) {
  28.         ...
  29.     }
  30. }

文字セット