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のインデックスの仕方による検索結果の違い

https://stackoverflow.com/questions/42482451/lucene-6-recommended-way-to-store-numeric-fields-with-term-vocabulary

ここでは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
-------------

参考

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.