【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以外にも種類がありますので、それらは別途解説を行います。