これは、なにをしたくて書いたもの?
前に、こういうエントリーを書きました。
Apache Luceneでベクトル検索(kNN検索)を試す - CLOVER🍀
自分はApache Luceneで簡単なプログラムを書く時に、インデックスの保存先をインメモリーにすることが多いのですが、その用途で
いつも使っていたRAMDirectory
がなくなっていてちょっと困ったので。
今はByteBuffersDirectory
を使うのがよさそうです。
Apache Lucene 9.0.0でのRAMDirectoryの削除
RAMDirectory
が削除されたのは、Apache Lucene 9.0.0のようです。
Lucene Change Log / Release 9.0.0 / API Changes
対象のissue。
Placeholder for the remainder of the original patch, removing all 8.x-deprecated RAMDirectory classes and replacing their use cases with ByteBuffersDirectory.
[LUCENE-8474] Remove deprecated RAMDirectory - ASF JIRA
Apache Lucene 8.0.0の時点で、RAMDirectory
は非推奨になっていたようですね。
Deprecated.
This class uses inefficient synchronization and is discouraged in favor of MMapDirectory. It will be removed in future versions of Lucene.
RAMDirectory (Lucene 8.0.0 API)
同時実行性が低く、パフォーマンスが悪いことが理由のようです。
[LUCENE-8438] RAMDirectory speed improvements and cleanup - ASF JIRA
というわけで、置き換え先はByteBuffersDirectory
ですね。
ByteBuffersDirectory (Lucene 9.10.0 core API)
名前のとおり、java.nio.ByteBuffer
を使ったApache LuceneのDirectory
の実装です。
ただこのクラス、実験的APIなんですけどね…。
WARNING: This API is experimental and might change in incompatible ways in the next release.
もっとも、これを使うのはテスト用途だったりすると思うので、特に問題ないでしょう。
今回はこちらを使ったコードを載せて終わりにしようと思います。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.2 2024-01-16 OpenJDK Runtime Environment (build 21.0.2+13-Ubuntu-122.04.1) OpenJDK 64-Bit Server VM (build 21.0.2+13-Ubuntu-122.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.2, vendor: Private Build, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-107-generic", arch: "amd64", family: "unix"
準備
Maven依存関係など。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>9.10.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>9.10.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.25.3</version> <scope>test</scope> </dependency> </dependencies>
Query Parserはなんとなく使った感じですね。
ByteBuffersDirectoryを使う
ByteBuffersDirectory
を使ったテストコードはこちら。
src/test/java/org/littlewings/lucene/directory/ByteBuffersDirectoryTest.java
package org.littlewings.lucene.directory; 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.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.StoredFields; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; class ByteBuffersDirectoryTest { @Test void inMemory() throws IOException, ParseException { try (Directory directory = new ByteBuffersDirectory()) { Analyzer analyzer = new StandardAnalyzer(); IndexWriterConfig config = new IndexWriterConfig(analyzer); try (IndexWriter writer = new IndexWriter(directory, config)) { Document document1 = new Document(); document1.add(new TextField("field1", "Apache Lucene", Field.Store.YES)); writer.addDocument(document1); Document document2 = new Document(); document2.add(new TextField("field1", "Elasticsearch", Field.Store.YES)); writer.addDocument(document2); Document document3 = new Document(); document3.add(new TextField("field1", "Apache Solr", Field.Store.YES)); writer.addDocument(document3); } try (DirectoryReader reader = DirectoryReader.open(directory)) { IndexSearcher searcher = new IndexSearcher(reader); QueryParser queryParser = new QueryParser("field1", analyzer); Query query = queryParser.parse("field1: Apache"); TopDocs topDocs = searcher.search(query, 10); ScoreDoc[] scoreDocs = topDocs.scoreDocs; StoredFields storedFields = searcher.storedFields(); List<Document> resultDocuments = Arrays .stream(scoreDocs) .map(scoreDoc -> { try { return storedFields.document(scoreDoc.doc); } catch (IOException e) { throw new UncheckedIOException(e); } }) .toList(); assertThat(resultDocuments.get(0).getField("field1").stringValue()).isEqualTo("Apache Lucene"); assertThat(resultDocuments.get(1).getField("field1").stringValue()).isEqualTo("Apache Solr"); } } } }
使い方はとても簡単で、インスタンスを作成してApache LuceneのDirectory
として使えばOKです。
try (Directory directory = new ByteBuffersDirectory()) {
おわりに
Apache Lucene 9.0.0より前で使えていたRAMDirectory
の代替になるのは、ByteBuffersDirectory
ということを書きました。
話としてはそれだけなのですが、RAMDirectory
が見つからなかった時に「さて代わりは???」と探すのにちょっと困ったので
メモとして。