kakiro-webカキローウェブ

システム開発情報とコンピューター書籍の紹介サイト

【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の基本動作はこのようになりますが、このままでは日本語が正しく扱えません。日本語を扱えるようにする方法は別途解説を行います。