1 <?xml version="1.0" encoding="UTF-8"?>
3 <!-- EN-Revision: 20854 -->
4 <sect1 id="zend.search.lucene.best-practice">
5 <title>ベストプラクティス</title>
7 <sect2 id="zend.search.lucene.best-practice.field-names">
11 <classname>Zend_Search_Lucene</classname> では、フィールド名に関する制限は特にありません。
15 しかし、できれば '<emphasis>id</emphasis>'
16 および '<emphasis>score</emphasis>' という名前は使用を控えるようにしましょう。
17 これらを使用すると、<code>QueryHit</code>
22 <classname>Zend_Search_Lucene_Search_QueryHit</classname> のプロパティ
23 <code>id</code> と <code>score</code> はそれぞれ、Lucene
24 ドキュメントが内部で使用する ID、検索結果の
25 <link linkend="zend.search.lucene.searching.results-scoring">スコア</link>
26 を表します。もしドキュメントでこれらと同じ名前のフィールドを使っているのなら、
27 そのフィールドにアクセスするには <methodname>getDocument()</methodname>
30 <programlisting language="php"><![CDATA[
31 $hits = $index->find($query);
33 foreach ($hits as $hit) {
34 // 'title' フィールドを取得します
37 // 'contents' フィールドを取得します
38 $contents = $hit->contents;
40 // Lucene ドキュメントの内部 ID を取得します
47 $docId = $hit->getDocument()->id;
49 // 'score' フィールドを取得します
50 $docId = $hit->getDocument()->score;
52 // 'title' フィールドもこの方法で取得できます
53 $title = $hit->getDocument()->title;
59 <sect2 id="zend.search.lucene.best-practice.indexing-performance">
60 <title>インデックス作成のパフォーマンス</title>
65 そしてインデックスの品質との兼ね合いで決まります。
69 インデックスの品質とは、要するにインデックスセグメントの数のことです。
73 各インデックスセグメントはデータ部とは独立しています。
74 つまり、インデックスに含まれるセグメントが多くなればなるほど
80 複数のセグメントをまとめて新しいひとつのセグメントを作成します。
81 完全に最適化されたインデックスは、セグメントひとつだけで構成されます。
85 インデックスの最適化を行うには <methodname>optimize()</methodname> メソッドを使用します。
86 <programlisting language="php"><![CDATA[
87 $index = Zend_Search_Lucene::open($indexPath);
94 インデックスの最適化はデータストリーム上で行われるので、
100 Lucene のインデックスセグメントは、その性質上
102 セグメントファイルを新たに作りなおす必要があります)。
103 したがって、新しいドキュメントがインデックスに追加されるたびに
104 新しいセグメントが作成されることになります。
105 その結果、インデックスの品質は下がっていきます。
109 セグメントが作成されるたびにインデックスの自動最適化が行われ、
110 一部のセグメントは自動的にマージされます。
114 自動最適化の設定は、次の 3 つのオプションで変更できます
115 (<link linkend="zend.search.lucene.index-creation.optimization">インデックスの最適化</link>
119 <para><emphasis>MaxBufferedDocs</emphasis>
120 は、メモリ内のバッファに保持されるドキュメントの最大数です。
121 この数を超えると、新しいセグメントを作成して
122 ハードディスクに書き込みます。</para>
125 <para><emphasis>MaxMergeDocs</emphasis>
126 は、自動最適化によって新しいセグメントへのマージを行う基準となる
130 <para><emphasis>MergeFactor</emphasis>
131 は、自動最適化を行う頻度を指定します。</para>
136 これらのオプションはすべて <classname>Zend_Search_Lucene</classname>
137 オブジェクトのプロパティであり、インデックスのプロパティではありません。
139 <classname>Zend_Search_Lucene</classname> オブジェクトに対してのみ働くようになり、
146 <emphasis>MaxBufferedDocs</emphasis> は、
147 スクリプトを一回実行するたびにひとつのドキュメントしか扱わない場合は
149 逆に、バッチ処理の場合にはこの設定が非常に重要になります。
150 値を大きくするとインデックス作成の速度が上がりますが、
151 同時に大量のメモリを消費するようになります。
155 <emphasis>MaxBufferedDocs</emphasis>
156 パラメータの値として最適なものを計算する公式はありません。
157 これはドキュメントのサイズや解析器、使用できるメモリ量などに依存するからです。
162 扱うであろうドキュメントの中で最もサイズが大きいものを用いて
166 <methodname>memory_get_usage()</methodname>
167 や <methodname>memory_get_peak_usage()</methodname>
171 使用可能なメモリのうち半分を超えない程度のメモリ消費量に抑えておくことをお勧めします。
175 <emphasis>MaxMergeDocs</emphasis> はセグメントの大きさ
176 (これはドキュメントの大きさによって決まります) を制限します。
177 これにより、自動最適化の時間を短縮できます。
178 つまり、<methodname>addDocument()</methodname> メソッドが
180 これは、対話的なアプリケーションで重要になります。
184 <emphasis>MaxMergeDocs</emphasis> の設定値を小さくすると、
186 インデックスの自動最適化は対話的な処理であり、
188 小さなセグメントたちがひとつの大きなセグメントにまとめられ、
189 さらにまたそれが他のセグメントとまとまってより大きなセグメントになり、
190 といった具合です。インデックスの最適化を完全に行うと、
195 セグメントのサイズを小さくするとインデックスの品質が下がり、
196 大量のセグメントができあがってしまいます。場合によっては、OS
197 の制限に引っかかって "オープンしているファイルが多すぎる"
201 <classname>Zend_Search_Lucene</classname> は、セグメントファイルをずっとオープンしたままにしておきます。
208 したがって、バックグラウンドでのインデックスの最適化は対話モードで行い、
209 バッチ処理用の <emphasis>MaxMergeDocs</emphasis>
210 はあまり小さくしすぎないようにしなければなりません。
214 <emphasis>MergeFactor</emphasis> は自動最適化の頻度に影響を及ぼします。
215 値を小さくすると、最適化されていないインデックスの品質が上がります。
216 値を大きくするとインデックス作成の策度が上がりますが、
217 セグメントの数も増えます。何度も言いますが、これは
218 "オープンしているファイルが多すぎる" エラーの原因となりえます。
222 <emphasis>MergeFactor</emphasis> は、以下の条件を満たす大きさで
223 インデックスセグメントをグループ化します。
225 <listitem><para><emphasis>MaxBufferedDocs</emphasis> 以下</para></listitem>
226 <listitem><para><emphasis>MaxBufferedDocs</emphasis> より大きいが
227 <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis> を超えない</para></listitem>
228 <listitem><para><emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis> より大きいが
229 <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis>*<emphasis>MergeFactor</emphasis> を超えない</para></listitem>
230 <listitem><para>...</para></listitem>
235 Zend_Search_Lucene は、<methodname>addDocument()</methodname>
236 をコールするたびにセグメントの状況を調べ、
237 いくつかのセグメントをまとめて次のグループの新しいセグメントに移動できるかどうかを確認します。
242 つまり、N 個のグループからなるインデックスには <emphasis>MaxBufferedDocs</emphasis> + (N-1)*<emphasis>MergeFactor</emphasis>
244 <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis><superscript>(N-1)</superscript>
249 この式で、インデックス内のセグメントの概数を求めることができます。
252 <emphasis>NumberOfSegments</emphasis> <= <emphasis>MaxBufferedDocs</emphasis> + <emphasis>MergeFactor</emphasis>*log
253 <subscript><emphasis>MergeFactor</emphasis></subscript> (<emphasis>NumberOfDocuments</emphasis>/<emphasis>MaxBufferedDocs</emphasis>)
257 <emphasis>MaxBufferedDocs</emphasis> は、使用できるメモリ量によって決まります。
258 MergeFactor を適切に設定することで、セグメントの数を調整できます。
262 バッチ処理においては、<emphasis>MergeFactor</emphasis>
263 パラメータを調整するほうが <emphasis>MaxMergeDocs</emphasis>
264 を調整するよりも効率的です。しかし、微調整はできず大雑把なものとなります。
265 そこで、まず上の公式をもとに <emphasis>MergeFactor</emphasis> を調整し、
266 それから <emphasis>MaxMergeDocs</emphasis> を微調整してパフォーマンスを最適化しましょう。
270 <sect2 id="zend.search.lucene.best-practice.shutting-down">
271 <title>インデックスの終了時処理</title>
274 <classname>Zend_Search_Lucene</classname> オブジェクトは、
276 これは、インデックスにドキュメントが追加されたけれども
277 新しいセグメントに書き込まれていないという場合に行われます。
281 また、場合によっては自動最適化も行います。
285 インデックスオブジェクトは、自分自身および QueryHit
286 オブジェクトがすべてスコープ外に出た時点で自動的に終了処理を行います。
290 インデックスオブジェクトがグローバル変数に格納されている場合は、
294 インデックスや QueryHit オブジェクトが複合データ型から参照されている場合にもこれは起こりえます。
295 たとえば、循環参照を含むオブジェクトはスクリプトの終了時まで破棄されません。
301 <acronym>PHP</acronym> の例外処理もここで終了します。
305 これは通常のインデックス終了処理を妨げることはありませんが、
306 何かエラーが発生した際に正しいエラー情報を取得できなくなる可能性があります。
314 まずは、強制的にスコープ外に出す方法です。
315 <programlisting language="php"><![CDATA[
316 $index = Zend_Search_Lucene::open($indexPath);
320 unset($index);]]></programlisting>
324 そしてもうひとつは、スクリプトの終了前にコミット操作を行うことです。
325 <programlisting language="php"><![CDATA[
326 $index = Zend_Search_Lucene::open($indexPath);
331 "<link linkend="zend.search.lucene.advanced.static">応用: 静的プロパティとしてのインデックスの使用</link>"
336 <sect2 id="zend.search.lucene.best-practice.unique-id">
337 <title>一意な ID によるドキュメントの取得</title>
340 ドキュメントの一意な ID、たとえば URL やパス、データベース上の ID
341 などをインデックスに保存しておくとよいでしょう。
345 <classname>Zend_Search_Lucene</classname> には <methodname>termDocs()</methodname>
346 というメソッドがあり、指定した単語を含むドキュメントを取得できます。
350 これは <methodname>find()</methodname> メソッドよりも効率的です。
351 <programlisting language="php"><![CDATA[
352 // find() メソッドでクエリ文字列を指定することによるドキュメントの取得
353 $query = $idFieldName . ':' . $docId;
354 $hits = $index->find($query);
355 foreach ($hits as $hit) {
356 $title = $hit->title;
357 $contents = $hit->contents;
362 // find() メソッドでクエリ API を使用することによるドキュメントの取得
363 $term = new Zend_Search_Lucene_Index_Term($docId, $idFieldName);
364 $query = new Zend_Search_Lucene_Search_Query_Term($term);
365 $hits = $index->find($query);
366 foreach ($hits as $hit) {
367 $title = $hit->title;
368 $contents = $hit->contents;
374 // termDocs() メソッドによるドキュメントの取得
375 $term = new Zend_Search_Lucene_Index_Term($docId, $idFieldName);
376 $docIds = $index->termDocs($term);
377 foreach ($docIds as $id) {
378 $doc = $index->getDocument($id);
379 $title = $doc->title;
380 $contents = $doc->contents;
387 <sect2 id="zend.search.lucene.best-practice.memory-usage">
388 <title>メモリの使用法</title>
391 <classname>Zend_Search_Lucene</classname> は、比較的メモリを消費するモジュールです。
395 各種の情報をキャッシュしたり、検索やインデックス作成の速度を上げたりするために、メモリを使用しています。
399 メモリに関する挙動は、モードによって異なります。
403 単語辞書のインデックスは、検索時にメモリに読み込まれます。
404 これは、実際の辞書に登録されている単語が 128件
406 Lucene のファイルフォーマットでは、この件数を変更することもできます。しかし
407 <classname>Zend_Search_Lucene</classname> の <acronym>API</acronym> ではそれをサポートしていません。
408 別の Lucene 実装を使用してインデックスをサポートすれば、
415 したがって、単語の数が増えれば増えるほどメモリの消費量も増加します。
416 トークン化していないフレーズをフィールドの値として使用したり、
417 テキスト以外の情報を大量にインデックスとして使用したりすると、
422 最適化されていないインデックスは、複数のセグメントで構成されます。
423 これも、メモリ消費量の増加の要因となります。
424 各セグメントは独立しているので、それぞれ独自に単語辞書と辞書インデックスを持っています。
425 ひとつのインデックスの中に <emphasis>N</emphasis> 個のセグメントがあったとすると、
426 メモリの消費量は最悪で <emphasis>N</emphasis> 倍になってしまいます。
427 インデックスの最適化を行ない、セグメントをひとつにまとめましょう。
431 インデックスは、検索処理とドキュメントのバッファリングに同じメモリを使用します。
432 このメモリの使用量は、パラメータ <emphasis>MaxBufferedDocs</emphasis>
437 インデックスの最適化 (完全最適化、部分最適化の両方)
438 はストリーム上で行なわれるので、あまりメモリを消費しません。
442 <sect2 id="zend.search.lucene.best-practice.encoding">
443 <title>エンコーディング</title>
446 <classname>Zend_Search_Lucene</classname> は、内部で UTF-8 文字列を使用しています。
447 したがって、Zend_Search_Lucene が返す文字列は、すべて UTF-8
452 単なる <acronym>ASCII</acronym> データのみを扱うのであればエンコーディングを気にする必要はありません。
458 エンコーディングの変換時にエラーが発生したりデータを失ってしまったりする可能性があります。
462 <classname>Zend_Search_Lucene</classname> は、ドキュメントやクエリのエンコーディングとしてさまざまなものに対応しています。
466 フィールドを作成するメソッドで、エンコーディングをオプションのパラメータによって指定できます。
467 <programlisting language="php"><![CDATA[
468 $doc = new Zend_Search_Lucene_Document();
469 $doc->addField(Zend_Search_Lucene_Field::Text('title',
472 $doc->addField(Zend_Search_Lucene_Field::UnStored('contents',
476 エンコーディングの指定をはっきりさせるという意味で、これが最も良い方法です。
480 このエンコーディング指定を省略すると、現在のロケールをもとに判断を行ないます。
481 ロケールの指定時に、言語だけでなく文字セットも指定できます。
482 <programlisting language="php"><![CDATA[
483 setlocale(LC_ALL, 'fr_FR');
486 setlocale(LC_ALL, 'de_DE.iso-8859-1');
489 setlocale(LC_ALL, 'ja_JP.UTF-8');
495 クエリ文字列のエンコーディングも、同じ方式で指定します。
499 エンコーディングを何らかの方法で指定しなかった場合は、
500 現在のロケールにもとづいて判断を行ないます。
505 エンコーディングはオプションのパラメータとして指定できます。
506 <programlisting language="php"><![CDATA[
508 Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');
509 $hits = $index->find($query);
515 デフォルトのエンコーディングを指定するには <methodname>setDefaultEncoding()</methodname>
517 <programlisting language="php"><![CDATA[
518 Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-1');
519 $hits = $index->find($queryStr);
522 空の文字列は、'現在のロケール' を意味します。
526 正しいエンコーディングを指定すれば、解析器はそれを正しく処理できます。
527 実際の挙動は、使用する解析器によって異なります。詳細は
528 <link linkend="zend.search.lucene.charset">文字セット</link>
533 <sect2 id="zend.search.lucene.best-practice.maintenance">
534 <title>インデックスの保守</title>
537 まずはっきりさせておくべきなのは、<classname>Zend_Search_Lucene</classname> やその他の
538 Lucene 実装は決して "データベース" ではないということです。
542 つまり、データを保存するものとして使用してはいけません。
543 通常のデータベース管理システムのように、バックアップ/リストア
544 やジャーナル処理、ログの記録、トランザクションといった機能は持っていません。
548 しかし、<classname>Zend_Search_Lucene</classname> はインデックスの一貫性を保持するための機能は持っています。
552 インデックスのバックアップ/リストアは、オフラインでインデックスフォルダをコピーすることで行ないます。
556 何らかの理由でインデックスが壊れてしまった場合は、
557 インデックスをリストアするか再構築しなければなりません。
561 そこで、大きなインデックスは、どこかに手動でバックアップしておき、
562 何かあったときに手動で復元できるようにしておきましょう。
563 そうすれば、障害からの復旧にかかる時間が短縮できます。