kakiro-webカキローウェブ

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

【Lucene】WildcardQueryを使用したワイルドカード検索

前方一致検索等、ワイルドカードを使用した検索条件を持つQueryは、WildcardQueryを使用することで作成することができます。

ここではWildcardQueryについてサンプルプログラムを元に解説を行っていきます。

検索対象として以下のようなDocumentを使用します。基本処理 インデックスの登録のページで紹介したサンプルプログラムを元に、インデックスに登録するDocumentを変更した箇所を以下に示します。

AnalyzerにはStandardAnalyzerを使用しています。

import java.io.File;
import java.util.Iterator;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
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 StandardAnalyzer(Version.LUCENE_46);

            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_46, analyzer);
            config.setOpenMode(OpenMode.CREATE);

            writer = new IndexWriter(dir, config);

            Document doc = null;

            doc = new Document();
            doc.add(new TextField("txt1", "abc def", Field.Store.YES));
            doc.add(new StringField("str1", "ghi jkl", Field.Store.YES));

            writer.addDocument(doc);

            doc = new Document();
            doc.add(new TextField("txt1", "abcd efgh", Field.Store.YES));
            doc.add(new StringField("str1", "ijkl mnop", Field.Store.YES));

            writer.addDocument(doc);

            doc = new Document();
            doc.add(new TextField("txt1", "abcde fghij", Field.Store.YES));
            doc.add(new StringField("str1", "klmno pqrst", Field.Store.YES));

            writer.addDocument(doc);

            doc = new Document();
            doc.add(new TextField("txt1", "zabcd efghi", Field.Store.YES));
            doc.add(new StringField("str1", "jklmn opqrs", Field.Store.YES));

            writer.addDocument(doc);

            writer.commit();

            //登録したインデックスの確認
            checkIndex(writer);
        } finally {
            //(略)
        }
    }

    private void checkIndex(IndexWriter writer) throws Exception {
        //(略)
    }

}

これにより登録されるインデックスは以下のようになります。

----- fieldName:str1 -----
fieldText:ghi jkl
fieldText:ijkl mnop
fieldText:jklmn opqrs
fieldText:klmno pqrst
----- fieldName:txt1 -----
fieldText:abc
fieldText:abcd
fieldText:abcde
fieldText:def
fieldText:efgh
fieldText:efghi
fieldText:fghij
fieldText:zabcd

Documentは以下のように登録されています。

Document ID フィールド「txt1」のキーワード フィールド「str1」のキーワード
0 abc ghi jkl
def
1 abcd ijkl mnop
efgh
2 abcde klmno pqrst
fghij
3 zabcd jklmn opqrs
efghi

上記のようにインデックスに登録したDocumentに対し、まずはワイルドカード「*」を指定して検索を行う例を以下に示します。

ワイルドカード「*」は0文字以上の任意の文字列とマッチすることを条件とするものとなります。

検索を行うサンプルプログラムは、基本処理 インデックスの検索のページで紹介したものを元に、Queryを指定する部分を変更したものとなっています。

import java.io.File;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class SearchIndex {

    public static void main(String[] args) {
        try {
            SearchIndex si = new SearchIndex();
            si.main();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void main() throws Exception {
        //検索を行うインデックスが登録されているディレクトリのパス
        String indexPath = "index";

        Directory dir = null;
        IndexReader reader = null;

        try {
            dir = FSDirectory.open(new File(indexPath));

            reader = DirectoryReader.open(dir);

            IndexSearcher searcher = new IndexSearcher(reader);

            WildcardQuery wildcardQuery = new WildcardQuery(new Term("txt1", "abc*"));//---(1)

            System.out.println("wildcardQuery:" + wildcardQuery);

            TopDocs topDocs = searcher.search(wildcardQuery, 5);//---(2)

            ScoreDoc[] scoreDocs = topDocs.scoreDocs;

            for (ScoreDoc scoreDoc : scoreDocs) {
                //(略)
            }
        } finally {
            if (reader != null) {
                reader.close();
            }

            if (dir != null) {
                dir.close();
            }
        }
    }

}

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

(1)

ワイルドカードを使用した検索条件の指定にはWildcardQueryを使用します。

WildcardQueryはコンストラクタにキーワードを示すTermを指定して生成を行っています。

そのTermはコンストラクタで検索対象とするフィールド名と、キーワードを指定して生成を行っています。

ここではフィールド名「txt1」に対し、キーワードを「abc*」と指定し「abc」で前方一致検索を行うよう条件の指定を行っています。

(2)

IndexSearcherのsearchメソッドに(1)で生成したWildcardQueryを指定して、インデックスの検索を行っています。

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

wildcardQuery:txt1:abc*
---------- docId:0 ----------
----- fieldName:txt1 -----
fieldText:abc def
----- fieldName:str1 -----
fieldText:ghi jkl
---------- docId:1 ----------
----- fieldName:txt1 -----
fieldText:abcd efgh
----- fieldName:str1 -----
fieldText:ijkl mnop
---------- docId:2 ----------
----- fieldName:txt1 -----
fieldText:abcde fghij
----- fieldName:str1 -----
fieldText:klmno pqrst

WildcardQueryを使用したQueryは、「txt1:abc*」のようにフィールド名とキーワードを「:」で区切った形式の文字列で表現されています。

ここではフィールド名「txt1」にキーワードが「abc」で前方一致するものを含むDucumentが検索結果として取得されています。

「abc」で始まっていれば、それに続く文字は何文字でも(0文字でも)検索対象となっています。

ワイルドカード「*」は、「*abc」と指定することで後方一致で検索を行うことができ、「*abc*」と指定することで部分一致で検索を行うことができます。

続いてワイルドカード「?」を指定して検索を行う例を以下に示します。

ワイルドカード「?」は任意の1文字とマッチすることを条件とするものとなります。

サンプルプログラムはQueryを指定する部分のみを抜粋して示します。

//(略)
            WildcardQuery wildcardQuery = new WildcardQuery(new Term("txt1", "abc?"));//---(1)

            System.out.println("wildcardQuery:" + wildcardQuery);

            TopDocs topDocs = searcher.search(wildcardQuery, 5);//---(2)
//(略)

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

(1)

ワイルドカードを使用した検索条件の指定にはWildcardQueryを使用します。

WildcardQueryはコンストラクタにキーワードを示すTermを指定して生成を行っています。

そのTermはコンストラクタで検索対象とするフィールド名と、キーワードを指定して生成を行っています。

ここではフィールド名「txt1」に対し、キーワードを「abc?」と指定し「abc」の後に任意の1文字があるものの検索を行うよう条件の指定を行っています。

(2)

IndexSearcherのsearchメソッドに(1)で生成したWildcardQueryを指定して、インデックスの検索を行っています。

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

wildcardQuery:txt1:abc?
---------- docId:1 ----------
----- fieldName:txt1 -----
fieldText:abcd efgh
----- fieldName:str1 -----
fieldText:ijkl mnop

WildcardQueryを使用したQueryは、「txt1:abc?」のようにフィールド名とキーワードを「:」で区切った形式の文字列で表現されています。

ここではフィールド名「txt1」にキーワードが「abc」の後に任意の1文字があるものを含むDucumentが検索結果として取得されています。

「abc」の後が0文字のものや2文字以上のものは検索対象外となっています。

尚、ワイルドカードとなる文字「*」、「?」を検索したい場合は、「abc\\*」のように「\\」でエスケープを行うことで指定が可能となります。

WildcardQueryの使用例は以上です。