【Lucene】PhraseQueryについて

【Lucene】PhraseQueryについて

検索条件に指定された文字列からQueryを生成する際にはQueryParserを使用することが多くなるかと思いますが、ここではそのQueryParserの動作を理解するために必要なPhraseQueryについてサンプルプログラムを元に解説を行っていきます。

検索対象として以下のような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 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', 'abc ghi def', Field.Store.YES)), doc.add(new StringField('str1', 'jkl pqr mno', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'abcdef ghijkl', Field.Store.YES)), doc.add(new StringField('str1', 'mnopqr stuvwx', Field.Store.YES)), writer.addDocument(doc), writer.commit(), //登録したインデックスの確認 checkIndex(writer), } finally { //(略) } } private void checkIndex(IndexWriter writer) throws Exception { //(略) } }

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

----- fieldName:str1 ----- fieldText:jkl mno pqr fieldText:jkl pqr mno fieldText:mnopqr stuvwx ----- fieldName:txt1 ----- fieldText:abc fieldText:abcdef fieldText:def fieldText:ghi fieldText:ghijkl

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

Document ID フィールド「txt1」のキーワード フィールド「str1」のキーワード
0 abc jkl mno pqr
def
ghi
1 abc jkl pqr mno
ghi
def
2 abcdef mnopqr stuvwx
ghijkl

上記のようにインデックスに登録したDocumentに対し、PhraseQueryを使用して検索を行う例を以下に示します。

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

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

(1)

PhraseQueryは引数なしのコンストラクタで空の状態で生成を行い、addメソッドでTermを追加していくことでフレーズを組み立てていきます。

ここではフィールド名「txt1」に対し、キーワード「abc」と「def」をこの順で追加しています。

尚、1つのPhraseQueryに対し、異なるフィールド名を持つTermを追加しようとするとエラーになり、追加できないようになっています。

(2)

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

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

phraseQuery:txt1:'abc def' ---------- docId:0 ---------- ----- fieldName:txt1 ----- fieldText:abc def ghi ----- fieldName:str1 ----- fieldText:jkl mno pqr

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

TermQuery等とは違い、キーワードの部分は「’」で囲まれるようになっています。

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

PhraseQueryはこのようにキーワードの出現順を考慮した検索を行うものとなっています。

よって、フィールド「txt1」の値が「abc ghi def」となっているDucumentは、キーワードの出現順が異なるため検索対象外となっており、値が「abcdef ghijkl」となっているDucumentは、「abcdef」の部分は1つのキーワードになっているため「abc」、「dfe」の2つのキーワードが連続したものとはみなされず、検索対象外となっています。

PhraseQueryをもう少し理解し易いように続いてCJKAnalyzerを使用して日本語を扱った場合の例を以下に示します。

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

import java.io.File, import java.util.Iterator, import java.util.List, import org.apache.lucene.analysis.Analyzer, import org.apache.lucene.analysis.cjk.CJKAnalyzer, 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 CJKAnalyzer(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', 'あいうえお', Field.Store.YES)), doc.add(new StringField('str1', 'かきくけこ', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'あいうおうえ', Field.Store.YES)), doc.add(new StringField('str1', 'かきくこくけ', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'あい いう うえ えお', Field.Store.YES)), doc.add(new StringField('str1', 'かき きく くけ けこ', Field.Store.YES)), writer.addDocument(doc), doc = new Document(), doc.add(new TextField('txt1', 'あい いう えお うえ', Field.Store.YES)), doc.add(new StringField('str1', 'かき きく けこ くけ', Field.Store.YES)), writer.addDocument(doc), writer.commit(), //登録したインデックスの確認 checkIndex(writer), } finally { //(略) } } private void checkIndex(IndexWriter writer) throws Exception { //(略) } }

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

----- fieldName:str1 ----- fieldText:かき きく くけ けこ fieldText:かき きく けこ くけ fieldText:かきくけこ fieldText:かきくこくけ ----- fieldName:txt1 ----- fieldText:あい fieldText:いう fieldText:うえ fieldText:うお fieldText:えお fieldText:おう

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

Document ID フィールド「txt1」のキーワード フィールド「str1」のキーワード
0 あい かきくけこ
いう
うえ
えお
1 あい かきくこくけ
いう
うお
おう
うえ
2 あい かき きく くけ けこ
いう
うえ
えお
3 あい かき きく けこ くけ
いう
えお
うえ

上記のようにインデックスに登録したDocumentに対し、PhraseQueryを使用して検索を行う例を以下に示します。

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

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

(1)

PhraseQueryを使用してフィールド名「txt1」に対し、キーワード「いう」と「うえ」をこの順で追加しています。

(2)

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

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

phraseQuery:txt1:'いう うえ' ---------- docId:0 ---------- ----- fieldName:txt1 ----- fieldText:あいうえお ----- fieldName:str1 ----- fieldText:かきくけこ ---------- docId:2 ---------- ----- fieldName:txt1 ----- fieldText:あい いう うえ えお ----- fieldName:str1 ----- fieldText:かき きく くけ けこ

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

フィールド「txt1」の値が「あいうえお」となっているDucumentは、キーワードが「あい」、「いう」、「うえ」、「えお」と分割されますので、「いう」、「うえ」をこの順で含むものとなり、検索対象になっています。

フィールド「txt1」の値が「あい いう うえ えお」となっているDucumentもキーワードは上記と同じように分割されますので、検索対象になっています。

フィールド「txt1」の値が「あいうおうえ」となっているDucumentは、キーワードが「あい」、「いう」、「うお」、「おう」、「うえ」と分割され、「いう」、「うえ」をこの順では含みませんので、検索対象外になっています。

フィールド「txt1」の値が「あい いう えお うえ」となっているDucumentは、キーワードが「あい」、「いう」、「えお」、「うえ」と分割され、「いう」、「うえ」をこの順では含みませんので、検索対象外になっています。

PhraseQueryの使用例は以上です。

READ  【Lucene】基本処理 インデックスの登録

Share

  • Add this entry to Hatena Bookmark

Follow Me