【Lucene】Queryの基本動作

【Lucene】Queryの基本動作

インデックスからDocumentを検索するときの検索条件の指定にはQueryが使用されます。

ここではQueryの基本動作を理解するために、TermQueryとBooleanQueryを使用したサンプルプログラムを元に解説を行っていきます。

検索対象として以下のような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', 'ab cd ef', Field.Store.YES)), doc.add(new StringField('str1', 'gh ij kl', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'abc def ghi', Field.Store.YES)), doc.add(new StringField('str1', 'jkl mno pqr', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'abcd efgh ijkl', Field.Store.YES)), doc.add(new StringField('str1', 'mnop qrst uvwx', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'fghi de abc', Field.Store.YES)), doc.add(new StringField('str1', 'opqr mn jkl', Field.Store.YES)), writer.addDocument(doc), writer.commit(), //登録したインデックスの確認 checkIndex(writer), } finally { //(略) } } private void checkIndex(IndexWriter writer) throws Exception { //(略) } }

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

----- fieldName:str1 ----- fieldText:gh ij kl fieldText:jkl mno pqr fieldText:mnop qrst uvwx fieldText:opqr mn jkl ----- fieldName:txt1 ----- fieldText:ab fieldText:abc fieldText:abcd fieldText:cd fieldText:de fieldText:def fieldText:ef fieldText:efgh fieldText:fghi fieldText:ghi fieldText:ijkl

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

Document ID フィールド「txt1」のキーワード フィールド「str1」のキーワード
0 ab gh ij kl
cd
ef
1 abc jkl mno pqr
def
ghi
2 abcd mnop qrst uvwx
efgh
ijkl
3 fghi opqr mn jkl
de
abc

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

検索を行うサンプルプログラムは、基本処理 インデックスの検索のページで紹介したものを元に、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.TermQuery, import org.apache.lucene.search.TopDocs, 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), TermQuery termQuery = new TermQuery(new Term('txt1', 'abc')),//---(1) System.out.println('termQuery:' + termQuery), TopDocs topDocs = searcher.search(termQuery, 5),//---(2) ScoreDoc[] scoreDocs = topDocs.scoreDocs, for (ScoreDoc scoreDoc : scoreDocs) { //(略) } } finally { if (reader != null) { reader.close(), } if (dir != null) { dir.close(), } } } }

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

(1)

キーワードに一致するDocumentを検索するときの条件の指定にはTermQueryを使用します。

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

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

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

(2)

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

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

termQuery:txt1:abc ---------- docId:1 ---------- ----- fieldName:txt1 ----- fieldText:abc def ghi ----- fieldName:str1 ----- fieldText:jkl mno pqr ---------- docId:3 ---------- ----- fieldName:txt1 ----- fieldText:fghi de abc ----- fieldName:str1 ----- fieldText:opqr mn jkl

インデックスの検索を行うときに条件を指定するQueryは、「txt1:abc」のようにフィールド名とキーワードを「:」で区切った形式の文字列で表現されています。

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

続いてキーワードを2つ指定して検索を行う例を以下に示します。

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

//(略) BooleanQuery booleanQuery = new BooleanQuery(),//---(1) booleanQuery.add(new TermQuery(new Term('txt1', 'abc')), Occur.MUST),//---(2) booleanQuery.add(new TermQuery(new Term('txt1', 'def')), Occur.MUST), System.out.println('booleanQuery:' + booleanQuery), TopDocs topDocs = searcher.search(booleanQuery, 5),//---(3) //(略)

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

(1)

複数の検索キーワードにAND、ORを組み合わせて条件指定したい場合には、BooleanQueryを使用します。

(2)

BooleanQueryのaddメソッドでQueryを追加していくことで、複数の条件を組み合わせていきます。

第2引数では第1引数に指定したQueryの条件の適用のされ方を、列挙型BooleanClause.Occurで指定します。適用のされ方には以下のようなものがあります。

MUST 指定した条件を必ず満たすDocumentが検索対象となります。
SHOULD SHOULDで指定した条件が複数ある場合に、その内の1つでも条件を満たすDocumentがあれば検索対象となります。
MUST_NOT 指定した条件を満たすDocumentは検索対象外となります。

ここではMUSTの条件を2つ指定して、フィールド名「txt1」にキーワード「abc」と「def」の両方を含むDocumentを検索するようにしています。

(3)

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

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

booleanQuery:+txt1:abc +txt1:def ---------- docId:1 ---------- ----- fieldName:txt1 ----- fieldText:abc def ghi ----- fieldName:str1 ----- fieldText:jkl mno pqr

BooleanQueryでMUSTを指定した条件は頭に「+」が付いています。また、2つの条件は半角スペースで区切られています。

検索条件に指定した通り、フィールド名「txt1」にキーワード「abc」と「def」の両方を含むDocumentが検索結果として取得されています。

上記ではMUSTを使用しましたので、続いてSHOULDを使用する例を以下に示します。

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

//(略) BooleanQuery booleanQuery = new BooleanQuery(), booleanQuery.add(new TermQuery(new Term('txt1', 'abc')), Occur.SHOULD),//---(1) booleanQuery.add(new TermQuery(new Term('txt1', 'ijkl')), Occur.SHOULD), System.out.println('booleanQuery:' + booleanQuery), TopDocs topDocs = searcher.search(booleanQuery, 5), //(略)

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

(1)

ここではSHOULDの条件を2つ指定して、フィールド名「txt1」にキーワード「abc」または「ijkl」を含むDocumentを検索するようにしています。

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

booleanQuery:txt1:abc txt1:ijkl ---------- docId:2 ---------- ----- fieldName:txt1 ----- fieldText:abcd efgh ijkl ----- fieldName:str1 ----- fieldText:mnop qrst uvwx ---------- docId:1 ---------- ----- fieldName:txt1 ----- fieldText:abc def ghi ----- fieldName:str1 ----- fieldText:jkl mno pqr ---------- docId:3 ---------- ----- fieldName:txt1 ----- fieldText:fghi de abc ----- fieldName:str1 ----- fieldText:opqr mn jkl

BooleanQueryでSHOULDを指定した条件は頭に何も付いていません。

検索条件に指定した通り、フィールド名「txt1」にキーワード「abc」または「ijkl」を含むDocumentが検索結果として取得されています。

最後にMUST_NOTを使用する例を以下に示します。

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

//(略) BooleanQuery booleanQuery = new BooleanQuery(), booleanQuery.add(new TermQuery(new Term('txt1', 'abc')), Occur.MUST),//---(1) booleanQuery.add(new TermQuery(new Term('txt1', 'fghi')), Occur.MUST_NOT), System.out.println('booleanQuery:' + booleanQuery), TopDocs topDocs = searcher.search(booleanQuery, 5), //(略)

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

(1)

ここではMUSTの条件とMUST_NOTの条件を指定して、フィールド名「txt1」にキーワード「abc」を含み、「fghi」を含まないDocumentを検索するようにしています。

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

booleanQuery:+txt1:abc -txt1:fghi ---------- docId:1 ---------- ----- fieldName:txt1 ----- fieldText:abc def ghi ----- fieldName:str1 ----- fieldText:jkl mno pqr

BooleanQueryでMUST_NOTを指定した条件は頭に「-」が付いています。

検索条件に指定した通り、フィールド名「txt1」にキーワード「abc」を含み、「fghi」を含まないDocumentが検索結果として取得されています。

Queryの基本動作としましては、BooleanQueryを使用して複数のQueryを組み合わせていくものとなります。組み合わせに使用できるQueryにはここで使用したTermQuery以外にも種類がありますので、それらは別途解説を行います。