Luceneでのインデックスの作成と、検索、IntPointのインデックスの仕方による検索結果についてです。
環境
lucene-7.2.1
準備
luceneをダウンロードしてきて、
- lucene-analyzers-common-7.2.1
- lucene-core-7.2.1
このあたりをAdd External JARsで追加しておきます。
インデックス作成
public void feed() {
// Analyzerを何か選ぶ
Analyzer analyzer = new WhitespaceAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
// OpenMode.CREATE_OR_APPENDならあったら追記
// OpenMode.APPENDなら追記
// OpenMode.CREATEは新規作成
iwc.setOpenMode(OpenMode.CREATE);
// 出力するディレクトリ先
Path path = Paths.get("src/main/resources/test");
Directory directory = null;
IndexWriter writer = null;
try {
directory = FSDirectory.open(path);
writer = new IndexWriter(directory, iwc);
for (int i = 0; i < 10; i++) {
// Documentクラスの作成
Document doc = new Document();
// textフィールドにtest textの値を入れる
doc.add(new TextField("text", "test text", Field.Store.YES));
// idフィールドにidの値を入れる
doc.add(new StringField("id", "id" + i, Field.Store.YES));
// cntフィールドに0から9の値を入れる
doc.add(new IntPoint("cnt", i));
doc.add(new StoredField("cnt", i));
doc.add(new NumericDocValuesField("cnt", i));
// 書き込み
writer.addDocument(doc);
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
このコードを実行すると、このようなデータが入るイメージです。
text | id | cnt |
---|---|---|
test text | id0 | 0 |
test text | id1 | 1 |
test text | id2 | 2 |
test text | id3 | 3 |
test text | id4 | 4 |
test text | id5 | 5 |
test text | id6 | 6 |
test text | id7 | 7 |
test text | id8 | 8 |
test text | id9 | 9 |
検索をする
検索をするためにこんな感じのコードを書いてみます。
public void search() {
Path path = Paths.get("src/main/resources/test");
Directory directory = null;
// TermQueryでテキストの検索クエリを作る textフィールドにtestを含むもの
TermQuery tq = new TermQuery(new Term("text", "test"));
// IntPoint.newRangeQueryでintPointの範囲検索
Query rq = IntPoint.newRangeQuery("cnt", 1, 5);
// BooleanQuery.Builderでクエリを作る
BooleanQuery.Builder builder = new BooleanQuery.Builder();
Query q = builder.add(tq, BooleanClause.Occur.MUST) // Occur.MUSTでつなげていくとAND検索
.add(rq, BooleanClause.Occur.FILTER).build();
// 最後の引数がtrueで降順
Sort sort = new Sort(new SortField("cnt", SortField.Type.INT, true));
try {
directory = FSDirectory.open(path);
DirectoryReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
ScoreDoc[] hits = searcher.search(q, 100, sort).scoreDocs;
System.out.println("start search");
for (int i = 0; i < hits.length; i++) {
Document hitDoc = searcher.doc(hits[i].doc);
System.out.println("id:" + hitDoc.get("id"));
System.out.println("cnt:" + hitDoc.get("cnt"));
System.out.println("-------------");
}
reader.close();
directory.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
}
検索の実行結果
上のコードを実行すると、以下の出力がされます。
start search
id:id5
cnt:5
-------------
id:id4
cnt:4
-------------
id:id3
cnt:3
-------------
id:id2
cnt:2
-------------
id:id1
cnt:1
-------------
textのフィールドにtestを含み、cntのフィールドが1から5の範囲のものが
降順でソートされて返されています。
IntPointのインデックスの仕方による検索結果の違い
ここではLongPointとStoredFieldとNumericDocValuesFieldの3つを
同じフィールド名で指定してます。
3つも指定するの?ということで、減らしたりして、
上に書いたsearch()メソッドを実行して検索をやってみました。
IntPointとStoredFieldとNumericDocValuesFieldをすべて指定
// cntフィールドに0から10の値を入れる
doc.add(new IntPoint("cnt", i));
doc.add(new StoredField("cnt", i));
doc.add(new NumericDocValuesField("cnt", i));
search()を実行しての検索結果
start search
id:id5
cnt:5
-------------
id:id4
cnt:4
-------------
id:id3
cnt:3
-------------
id:id2
cnt:2
-------------
id:id1
cnt:1
-------------
ちゃんと1から5の範囲で降順で返ってきます。
StoredFieldをなくしてインデックスしてみる
// cntフィールドに0から10の値を入れる
doc.add(new IntPoint("cnt", i));
//doc.add(new StoredField("cnt", i));
doc.add(new NumericDocValuesField("cnt", i));
search()を実行しての検索結果
start search
id:id5
cnt:null
-------------
id:id4
cnt:null
-------------
id:id3
cnt:null
-------------
id:id2
cnt:null
-------------
id:id1
cnt:null
-------------
1から5の範囲で降順にはなっていました。
cntのフィールドの値がnullでした。
NumericDocValuesFieldをなくしてインデックスしてみる
// cntフィールドに0から10の値を入れる
doc.add(new IntPoint("cnt", i));
doc.add(new StoredField("cnt", i));
//doc.add(new NumericDocValuesField("cnt", i));
search()を実行しての検索結果
Exception in thread "main" java.lang.IllegalStateException: unexpected docvalues type NONE for field 'cnt' (expected=NUMERIC). Re-index with correct docvalues type.
検索に失敗しました。
ソートを行うコードを外して実行すると以下のように結果が返ってくるので、
NumericDocValuesFieldを指定しないと、ソートするときに失敗するようです。
start search
id:id1
cnt:1
-------------
id:id2
cnt:2
-------------
id:id3
cnt:3
-------------
id:id4
cnt:4
-------------
id:id5
cnt:5
-------------