14.2. インデックスの構築

14.2.1. 新しいインデックスの作成

インデックスの作成機能および更新機能は、 Zend_Search_Lucene モジュールと Java Lucene で実装されています。 これらの両方の機能を使用することができます。

以下の PHP コードでは、Zend_Search_Lucene のインデックス作成 API を用いてファイルをインデックス化する例を示します。

<?php

// 2 番目の引数を TRUE にすると、新しいインデックスを作成します。
$index = new Zend_Search_Lucene('/data/my-index', true);

$doc = new Zend_Search_Lucene_Document();

// ドキュメントの URL を、検索結果の ID として保存します。
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));

// ドキュメントの内容をインデックス化します。
$doc->addField(Zend_Search_Lucene_Field::UnStored('contents', $docContent));

// ドキュメントをインデックスに追加します。
$index->addDocument($doc);

// 変更内容をインデックスに書き込みます。
$index->commit();
?>

新しく追加されたドキュメントをコミットすると、 インデックスから取得できるようになります。

Zend_Search_Lucene::commit() は、 スクリプトの実行終了時およびすべての検索リクエストの前に 自動的にコールされます。

commit() をコールするたびに、新しいインデックスセグメントが作成されます。 [6] そのため、コール回数は必要最小限にしなければなりません。 ただ、大量のドキュメントを一度にコミットすると、 メモリの消費量が多くなるという一面もあります。

Zend_Search_Lucene の機能を拡張して、 将来はセグメント管理の自動最適化を行えるようにする予定です。

14.2.2. インデックスの更新

既存のインデックスを更新する際にも同じ手順を使用します。ただひとつの違いは、 2 番目のパラメータを指定せずにインデックスをオープンしなければならないということです。

<?php

// 既存のインデックスをオープンします。
$index = new Zend_Search_Lucene('/data/my-index');

$doc = new Zend_Search_Lucene_Document();
// ドキュメントの URL を、検索結果の ID として保存します。
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
// ドキュメントの内容をインデックス化します。
$doc->addField(Zend_Search_Lucene_Field::UnStored('contents', $docContent));

// ドキュメントをインデックスに追加します。
$index->addDocument($doc);

// 変更内容をインデックスに書き込みます。
$index->commit();
?>

commit() を (明示的あるいは暗黙的に) コールするたびに、 新しいインデックスセグメントが作成されます。

Zend_Search_Lucene は、自動的にはセグメント管理を行いません。そのため、 セグメントの大きさには十分注意する必要があります。 セグメントを大きくするとよい結果が得られるでしょうが、 その反面大きなセグメントの作成時には大量のメモリを必要とします。

このバージョンの Zend_Search_Lucene で作成したインデックスを最適化するには、 Lucene Java and Luke (Lucene Index Toolbox - http://www.getopt.org/luke/) が使用可能です。

14.2.3. ドキュメントの更新

Lucene インデックスファイルは、ドキュメントの更新をサポートしていません。 更新するためには、いったん削除した上で改めて追加する必要があります。

そのためには、インデックス内部のドキュメント ID を使用して Zend_Search_Lucene::delete() メソッドをコールします。 この ID は、クエリでヒットした内容から 'id' プロパティで取得できます。

<?php
$removePath = ...;
$hits = $index->find('path:' . $removePath);
foreach ($hits as $hit) {
    $index->delete($hit->id);
}
$index->commit();
?>


[6] 既存の Lucene インデックスセグメントを更新することは、その性質上できません。 セグメントを更新するには、改めてセグメント全体を構成しなおす必要があります。 Lucene インデックスファイルの形式についての詳細は、以下を参照ください (http://lucene.apache.org/java/docs/fileformats.html)。 セグメントの数が増えると、インデックスの質が低下します。 しかし、インデックスの最適化を行うことで、これを防ぐことができます。 最適化により、複数のセグメントをひとつにまとめます。 この処理についても、セグメントを更新するわけではありません。 新しい大きなセグメントを作成し、新しいセグメントリスト ('segments.new' というファイル) を作成した後に、最適化後の新しいセグメントをリストに含めます。 もとの複数のセグメントはリストに含めません。そして、最後に 'segments.new' ファイルの名前を 'segments' に変更するのです。

最適化は段階的に進められます。まず最も小さなセグメント (例: 文書をひとつだけ追加した際に作成されたセグメントなど) が大きめのセグメントに統合され、さらにそれが別のセグメントに統合され、... といった具合です。 最適化はセグメントストリームを使用して進められるので、それほど多くのメモリを消費しません。 また、最適化の際に多くのリソースを消費することもありませんし、インデックスにロックをかけることもありません。 最適化の処理中にインデックスの検索や更新、他のセグメントとの統合を行うことも可能です。