【Lucene】Analyzerの基本動作

【Lucene】Analyzerの基本動作

インデックスにDocumentを登録するときのテキストデータからのキーワードの抽出、インデックスからDocumentを検索するときの検索文字列からのキーワードの抽出にはAnalyzerが使用されます。

ここではAnalyzerの基本動作を理解するために、標準的なAnalyzerとなるStandardAnalyzerとほぼ同様な動作をするAnalyzerクラスを自作したサンプルプログラムを以下に示します。

自作したAnalyzerクラスは以下のようになります。

import java.io.Reader, import org.apache.lucene.analysis.Analyzer, import org.apache.lucene.analysis.TokenStream, import org.apache.lucene.analysis.Tokenizer, import org.apache.lucene.analysis.core.LowerCaseFilter, import org.apache.lucene.analysis.core.StopAnalyzer, import org.apache.lucene.analysis.core.StopFilter, import org.apache.lucene.analysis.standard.StandardFilter, import org.apache.lucene.analysis.standard.StandardTokenizer, import org.apache.lucene.util.Version, public class SampleStandardAnalyzer extends Analyzer {//---(1) @Override protected TokenStreamComponents createComponents(String fieldName, Reader reader) {//---(2) Tokenizer tokenizer = new StandardTokenizer(Version.LUCENE_46, reader),//---(3) TokenStream tokenStream = new StandardFilter(Version.LUCENE_46, tokenizer),//---(4) tokenStream = new LowerCaseFilter(Version.LUCENE_46, tokenStream),//---(5) tokenStream = new StopFilter(Version.LUCENE_46, tokenStream, StopAnalyzer.ENGLISH_STOP_WORDS_SET),//---(6) return new TokenStreamComponents(tokenizer, tokenStream),//---(7) } }

上記プログラムの解説を行います。

(1)

自作のAnalyzerクラスはLuceneのAnalyzerクラスを継承するようにします。

(2)

LuceneのAnalyzerクラスは抽象クラスとなっており、抽象メソッドcreateComponentsを持っていますので、自作のAnalyzerクラスでオーバーライドしその実装を行います。

createComponentsメソッドではAnalyzerの動作を決める設定を行うことになります。

(3)

文字列をキーワードに分割する処理を行うのがTokenizerです。

Tokenizerにはいくつかの種類があり、使用するものによって文字列をキーワードに分割する方法が変わります。

ここではStandardAnalyzerでも使用されているStandardTokenizerを使用しています。

コンストラクタでは使用するLuceneのバージョンとcreateComponentsメソッドの引数で渡されるReaderを指定しています。

(4)

(4)、(5)、(6)はTokenizerで分割したキーワードに行うフィルター処理になります。

StandardFilterはStandardAnalyzerでも使用されているものですが、過去のバージョンとの互換用の処理を行っており、現バージョンでは特別なフィルター処理は行っていないようです。

コンストラクタでは使用するLuceneのバージョンと(3)で生成したTokenizerを指定しています。

フィルター処理はいくつか重ねていくことができるのですが、フィルタークラスはTokenFilterクラスを継承し、コンストラクタではTokenStreamクラスを受け取るようになっており、このTokenStreamはTokenizerとTokenFilterの両方が継承していますので、Tokenizerに対してフィルター処理を行うことも、そのフィルター結果に対してさらにフィルター処理を行うこともできるようになっています。

(5)

LowerCaseFilterはStandardAnalyzerでも使用されているもので、キーワードの大文字を小文字に変換します。

コンストラクタでは使用するLuceneのバージョンと(4)で生成したTokenStreamを指定しています。

(6)

StopFilterはStandardAnalyzerでも使用されているもので、キーワードから指定した文字列のセットに含まれているものを除外します。

コンストラクタでは使用するLuceneのバージョンと(5)で生成したTokenStreamと、除外したい文字列のセットとしてStopAnalyzer.ENGLISH_STOP_WORDS_SETを指定しています。

StopAnalyzer.ENGLISH_STOP_WORDS_SETは「this」、「is」、「a」、「and」といった英文で使用頻度が高く、検索キーワードに使用しても効果的ではない文字列のセットになっています。

(7)

createComponentsメソッドの戻りとなるTokenStreamComponentsを生成しています。

コンストラクタでは(3)で生成したTokenizerと最終的なフィルター結果となる(6)で生成したTokenStreamを指定しています。

特にフィルター処理が必要でない場合、TokenStreamComponentsのコンストラクタにはTokenizerのみを指定することもできます。

この自作したAnalyzerの動作を基本処理 インデックスの登録のページ基本処理 インデックスの確認のページで紹介したサンプルプログラムを元に確認してみます。

プログラムの主な変更箇所を中心に以下に示します。

import java.io.File, import java.util.Iterator, import java.util.List, import org.apache.lucene.analysis.Analyzer, import org.apache.lucene.document.Document, import org.apache.lucene.document.Field, import org.apache.lucene.document.TextField, import org.apache.lucene.index.AtomicReader, import org.apache.lucene.index.AtomicReaderContext, import org.apache.lucene.index.DirectoryReader, import org.apache.lucene.index.Fields, import org.apache.lucene.index.IndexReader, import org.apache.lucene.index.IndexWriter, import org.apache.lucene.index.IndexWriterConfig, import org.apache.lucene.index.IndexWriterConfig.OpenMode, import org.apache.lucene.index.Terms, import org.apache.lucene.index.TermsEnum, import org.apache.lucene.store.Directory, import org.apache.lucene.store.FSDirectory, import org.apache.lucene.util.BytesRef, import org.apache.lucene.util.Version, public class WriteIndex { public static void main(String[] args) { try { WriteIndex wi = new WriteIndex(), wi.main(), } catch (Exception e) { e.printStackTrace(), } } private void main() throws Exception { //(略) try { //(略) analyzer = new SampleStandardAnalyzer(),//---(1) IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_46, analyzer), config.setOpenMode(OpenMode.CREATE), writer = new IndexWriter(dir, config), Document doc = null, doc = new Document(),//---(2) doc.add(new TextField('txt1', 'abc def,ghi.jkl-mno_pqr', Field.Store.YES)), writer.addDocument(doc), doc = new Document(),//---(3) doc.add(new TextField('txt1', 'ABC STU ABC', Field.Store.YES)), writer.addDocument(doc), doc = new Document(),//---(4) doc.add(new TextField('txt1', 'This is a pen', Field.Store.YES)), writer.addDocument(doc), doc = new Document(),//---(5) doc.add(new TextField('txt1', 'あいう、アイウ、アイウ。123 123 一二三', Field.Store.YES)), writer.addDocument(doc), writer.commit(), //登録したインデックスの確認 checkIndex(writer), } finally { //(略) } } private void checkIndex(IndexWriter writer) throws Exception { //(略) } }

上記プログラムの解説を行います。

(1)

Analyzerに自作したSampleStandardAnalyzerを使用しています。

このサンプルプログラムの実行結果は以下のようになります。

----- fieldName:txt1 ----- fieldText:123 fieldText:abc fieldText:def fieldText:ghi.jkl fieldText:mno_pqr fieldText:pen fieldText:stu fieldText:あ fieldText:い fieldText:う fieldText:アイウ fieldText:一 fieldText:三 fieldText:二 fieldText:123 fieldText:abc fieldText:アイウ

(2)でDocumentのテキストデータとして「abc def,ghi.jkl-mno_pqr」と指定したものは、「abc」、「def」、「ghi.jkl」、「mno_pqr」に分割されました。

StandardTokenizerは半角スペース、「,」、「-」は区切り文字として扱いますが、「.」、「_」は扱わないようです。

(3)でDocumentのテキストデータとして「ABC STU ABC」と指定したものは、「abc」、「stu」、「abc」に分割されました。

LowerCaseFilterにより大文字から小文字への変換が行われています。「abc」は(2)で指定したものと同じになりますので、1つにまとめられています。全角英字で指定した「ABC」も「abc」と全角小文字に変換されています。

(4)でDocumentのテキストデータとして「This is a pen」と指定したものは、「pen」のみに分割されました。

StopFilterにより「This」、「is」、「a」は除外されています。

(5)でDocumentのテキストデータとして「あいう、アイウ、アイウ。123 123 一二三」(「一」の前は全角スペース)と指定したものは、「あ」、「い」、「う」、「アイウ」、「アイウ」、「123」、「123」、「一」、「二」、「三」に分割されました。

StandardTokenizerは全角の「、」、「。」、全角スペースも区切り文字として扱うようです。ひらがな、漢字は1文字ずつに分割し、全角カタカナ、半角カタカナ、全角数字はしないようです。また、全角カタカナと半角カタカナ、全角数字と半角数字は区別しています。

Analyzerの基本動作はこのようになりますが、このままでは日本語が正しく扱えません。日本語を扱えるようにする方法は別途解説を行います。