LoginSignup
1
3

More than 5 years have passed since last update.

Luceneでのインデックス作成と検索

Last updated at Posted at 2018-04-17

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
-------------

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3