Hibernate Searchの5.6に向けて、Alphaリリースが続いています。現在は、Alpha3です。
Hibernate Search 5.6では、Elasticsearchとの統合が試験的に追加されます。
Hibernate Search 5.6.0.Alpha2 introduces Elasticsearch integration - In Relation To
Third milestone for Elasticsearch support - In Relation To
まあ、けっこう前からRoadmapに載っていたので、それほど驚くことではないのですが。
ただ、前はSolrも対象になっていた気がするのですが…記憶違いかな?
Hibernate Search × Elasticsearch
要するに、何かというとこういう感じらしいです。
Hibernate Searchは、これまでLuceneを直接利用していました。いわゆるEmbeddedな使い方で、インデキシング時などにはバックグラウンド処理が走るなどのオーバーヘッドがあったりしました。これに対して今回追加されようとしているElasticsearchとの統合は、Luceneのインデックス管理の部分をElasticsearchに任せる形のものになります。インデキシングやクエリを投げるのにRPCが必要になりますが、Elasticsearchによるスケールアウトのメリットを得ることができます。
まだまだ実験的サポート段階
とはいえ、今のHibernate SearchのEmbeddedな機能に比べると、まだまだ見劣りします。基本的なCRUDとクエリは実行できますが、未サポートなものもたくさんあります。
Integration with Elasticsearch
現状では、以下が制限として挙げられています。
- Analyzer support(5.6.0 Alpha3でクリアしたっぽいですが)
- Filters
- Faceting
- Optimisation
- Timeouts
- Delete by queries
- Resolution for Date type mapping is ignored
- Scrolling on large results
- MoreLikeThis queries
- Mixing Lucene based indexes and Elasticsearch based indexes
ファセットが使えないのは、なかなか痛いような…。ソースコードを見ていると、対応していきそうな雰囲気があるので、気長に待つとしましょう。
なお、Elasticsearchとの統合には、Jestが利用されています。
Jest/jest at master · searchbox-io/Jest · GitHub
このあたり見ると、HTTP通信のタイムアウトとかは、今は決め打ちですねぇ。
前置きはこれくらいにして、使ってみます。
準備
まずは、依存関係の定義から。Hibernate Searchは、JPAと統合する形で使います。
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.0.6.Final</version> </dependency> <dependency> <groupId>org.jboss.spec.javax.transaction</groupId> <artifactId>jboss-transaction-api_1.2_spec</artifactId> <version>1.0.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>5.6.0.Alpha3</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-backend-elasticsearch</artifactId> <version>5.6.0.Alpha3</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.191</version> <scope>test</scope> </dependency>
「hibernate-search-backend-elasticsearch」が、Elasticsearchとの統合に必要なモジュールです。
データベースは、H2としました。
また、テスト向けにJUnitとAssertJも利用します。
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.3.0</version> <scope>test</scope> </dependency>
設定
こちらを見ながら、以下のように設定しました。
src/test/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="hibernate.search.pu" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <!-- Hibenate Search --> <property name="hibernate.search.lucene_version" value="LUCENE_CURRENT"/> <property name="hibernate.search.elasticsearch.host" value="http://localhost:9200"/> <property name="hibernate.search.default.indexmanager" value="elasticsearch"/> <property name="hibernate.search.elasticsearch.index_management_strategy" value="NONE"/> </properties> </persistence-unit> </persistence>
JPAの部分は端折りまして、Hibernate Searchの部分について。
<property name="hibernate.search.lucene_version" value="LUCENE_CURRENT"/> <property name="hibernate.search.elasticsearch.host" value="http://localhost:9200"/> <property name="hibernate.search.default.indexmanager" value="elasticsearch"/> <property name="hibernate.search.elasticsearch.index_management_strategy" value="NONE"/>
「hibernate.search.lucene_version」はLuceneのバージョンを指定しますが、特筆事項はありません。Elasticsearchとの連携部分は、それぞれ以下のように。
- hibernate.search.elasticsearch.host … Elasticsearchとの接続先URLを指定
- hibernate.search.default.indexmanager … Elasticsearchと統合する場合、「elasticsearch」を指定
- hibernate.search.elasticsearch.index_management_strategy … インデックスの作成方法を指定
- hibernate.search.elasticsearch.index_management_wait_timeout … (今回設定していませんが)インデックス作成後、利用可能になるまで待つタイムアウトを設定
「hibernate.search.elasticsearch.index_management_strategy」ですが、NONE、MERGE、CREATE、CREATE_DELETEが指定できます。NONEだと何もしませんが、MERGEだとインデックス定義とマッピング定義を更新(コンフリクトしていなければ)、CREATEだとインデックスを1度削除してから作り直し、CREATE_DELETEだとCREATEに加えてシャットダウン時にインデックスを削除します。
CREATE_DELETEがテスト向けなので、最初これを指定していたらElasticsearchから応答が返らなくて…NONEにしました。
というか、マッピング定義は自分で書いた方がいい気もするので、まあいいかなぁと。Elasticsearchをシングルノードのデフォルトで起動するとクラスタのステータスがYELLOWなので、Hibernate Search 5.6.0 Alpha3で更新された「We’ll wait for Elasticsearch to be "green" before attempting to use it at boot」とぶつかっている気がします…。
ちなみに、今回は使っていませんがEntityの定義からマッピングは作成しようとはしてくれるみたいです。
https://github.com/hibernate/hibernate-search/blob/5.6.0.Alpha3/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchIndexManager.java#L224
https://github.com/hibernate/hibernate-search/blob/5.6.0.Alpha3/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchIndexManager.java#L284
Entity
続いて、JPAのEntityを定義します。
src/test/java/org/littlewings/hibernate/elasticsearch/Contents.java
package org.littlewings.hibernate.elasticsearch; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; @Entity @Table(name = "contents") @Indexed(index = "myindex") public class Contents implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column @Field private String content; public static Contents create(String content) { Contents c = new Contents(); c.setContent(content); return c; } public Contents() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
ちょっとstaticなファクトリメソッド設けてますが、基本的には通常のJPA&Hibernate Searchで使うEntityです。
今回はインデックスの作成方針をNONEにしていないので、インデックス定義やマッピング定義をHibernate Searchは自動的に定義しません。とはいえ、ドキュメント登録時にインデックス名とタイプ名は必要なわけです。
この名前ですが、@IndexedアノテーションおよびEntityのクラス名で決まります。
デフォルトでは、インデックス名はEntityのFQCNを小文字にしたものです。変更したい場合は@Indexedアノテーションのindexを設定します。
今回のEntity名は「org.littlewings.hibernate.elasticsearch.Contents」なので、デフォルトのインデックス名は「org.littlewings.hibernate.elasticsearch.contents」です。ですが、これだと長いのでindexを設定して「myindex」としました。
タイプについては、Entityのクラス名で決まってしまいます。
https://github.com/hibernate/hibernate-search/blob/5.6.0.Alpha3/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/impl/ElasticsearchIndexWorkVisitor.java#L177-L184
つまり、今回は「org.littlewings.hibernate.elasticsearch.Contents」となります。
マッピング
今回はKuromojiを導入し、以下のマッピングとしました。
{ "settings": { "index": { "number_of_shards" : 1, "number_of_replicas" : 1, "analysis": { "tokenizer": { "kuromoji_tokenizer_search": { "type": "kuromoji_tokenizer", "mode": "search", "discard_punctuation" : "true", "user_dictionary" : "userdict_ja.txt" } }, "analyzer": { "kuromoji_analyzer": { "type": "custom", "tokenizer": "kuromoji_tokenizer_search", "filter": ["kuromoji_baseform", "kuromoji_part_of_speech", "cjk_width", "stop", "ja_stop", "kuromoji_stemmer", "lowercase"] } } } } }, "mappings": { "org.littlewings.hibernate.elasticsearch.Contents": { "_source": { "enabled": true }, "_all": { "enabled": true }, "properties": { "content": { "type": "string", "store": "yes", "index": "analyzed", "analyzer": "kuromoji_analyzer" } } } } }
ElasticsearchおよびKuromojiプラグインは、インストール済み、起動済みとします。
$ curl -XPUT 'http://localhost:9200/myindex' -d @index.json
動かしてみる
では、実際にHibernate SearchとElasticsearchをつなげてみましょう。以下のテストコードを埋めていきます。
src/test/java/org/littlewings/hibernate/elasticsearch/SeparatedElasticsearchTest.java
package org.littlewings.hibernate.elasticsearch; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.hibernate.search.backend.elasticsearch.ElasticsearchQueries; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.jpa.Search; import org.hibernate.search.query.engine.spi.QueryDescriptor; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class SeparatedElasticsearchTest { @Test public void gettingStarted() { // ここに、処理を書く } }
EntityManager em = Persistence.createEntityManagerFactory("hibernate.search.pu").createEntityManager(); ///// create data EntityTransaction transaction = em.getTransaction(); transaction.begin(); em.persist(Contents.create("東京都は、日本の首都です。")); em.persist(Contents.create("明日は、晴れるでしょう。")); em.persist(Contents.create("Java EE 7 徹底入門")); em.persist(Contents.create("高速スケーラブル検索エンジン ElasticSearch Server")); em.persist(Contents.create("[改訂新版] Apache Solr入門 オープンソース全文検索エンジン")); transaction.commit();
Hibernate SearchのFullTextEntityManagerを取得します。ここまでは、通常のHibernate Searchの使い方と同じです。
///// get FullTextEntityManager
FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
違うのはここからで、ElasticsearchQueriesからQueryStringのJSONを元にQueryDescriptorを作成し、その後にFullTextQueryを取得して検索するという流れになります。
///// search QueryDescriptor queryDescriptor1 = ElasticsearchQueries.fromQueryString("content:東京都 OR content:elasticsearch"); FullTextQuery query1 = ftem .createFullTextQuery(queryDescriptor1, Contents.class) .setSort(new Sort(new SortField("id", SortField.Type.LONG))); List<Contents> results1 = query1.getResultList(); assertThat(results1).hasSize(2); assertThat(results1.get(0).getContent()).isEqualTo("東京都は、日本の首都です。"); assertThat(results1.get(1).getContent()).isEqualTo("高速スケーラブル検索エンジン ElasticSearch Server");
ちゃんとソートは指定できるところがナイス。
Query Stringを使ったクエリの書き方は、こちらを参照してください。
https://www.elastic.co/guide/en/elasticsearch/reference/2.x/query-dsl-query-string-query.html
JSON文字列として、クエリ自体のリクエストを渡すこともできます。
QueryDescriptor query = ElasticsearchQueries.fromJson(
"{ 'query': { 'match' : { 'lastName' : 'Brand' } } }");
形態素解析が効いているか確認するために、「東京都」を「京都」で検索してみます。
///// search QueryDescriptor queryDescriptor2 = ElasticsearchQueries.fromQueryString("content:京都 OR content:elasticsearch"); FullTextQuery query2 = ftem .createFullTextQuery(queryDescriptor2, Contents.class) .setSort(new Sort(new SortField("id", SortField.Type.LONG))); List<Contents> results2 = query2.getResultList(); assertThat(results2).hasSize(1); assertThat(results2.get(0).getContent()).isEqualTo("高速スケーラブル検索エンジン ElasticSearch Server");
引っかからなくなりました。
削除すると、Elasticsearch側のインデックスにも反映されます。
///// delete transaction.begin(); em.remove(em.find(Contents.class, 4L)); transaction.commit(); ///// search QueryDescriptor queryDescriptor3 = ElasticsearchQueries.fromQueryString("content:elasticsearch"); FullTextQuery query3 = ftem .createFullTextQuery(queryDescriptor3, Contents.class); List<Contents> results3 = query3.getResultList(); assertThat(results3).isEmpty();
できないのは、クエリを使用した削除、です。
なお、curlでインデックスに保存された値を見ると、こんな感じになっています。
$ curl 'http://localhost:9200/myindex/_search?pretty&q=*' { "took" : 3, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "hits" : { "total" : 4, "max_score" : 1.0, "hits" : [ { "_index" : "myindex", "_type" : "org.littlewings.hibernate.elasticsearch.Contents", "_id" : "1", "_score" : 1.0, "_source" : { "content" : "東京都は、日本の首都です。" } }, { "_index" : "myindex", "_type" : "org.littlewings.hibernate.elasticsearch.Contents", "_id" : "2", "_score" : 1.0, "_source" : { "content" : "明日は、晴れるでしょう。" } }, { "_index" : "myindex", "_type" : "org.littlewings.hibernate.elasticsearch.Contents", "_id" : "3", "_score" : 1.0, "_source" : { "content" : "Java EE 7 徹底入門" } }, { "_index" : "myindex", "_type" : "org.littlewings.hibernate.elasticsearch.Contents", "_id" : "5", "_score" : 1.0, "_source" : { "content" : "[改訂新版] Apache Solr入門 オープンソース全文検索エンジン" } } ] } }
typeが長い…。
※「高速スケーラブル検索エンジン ElasticSearch Server」がないのは、テストコードで削除しているからです
とりあえず、動かせましたね。
Embedded Elasticsearchと合わせて使う
こういうテストコードで書くと、Embeddedな構成で書きたくなるものです。
というわけで、書いてみました。
Maven依存関係に、以下を追加。
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>2.2.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.elasticsearch.plugin</groupId> <artifactId>analysis-kuromoji</artifactId> <version>2.2.1</version> <scope>test</scope> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>4.2.2</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> <scope>test</scope> </dependency>
Kuromojiプラグインも、もちろん使用します。Commons IOは、ディレクトリ削除用です。
src/test/resourcesディレクトリに、persistence.xmlを含めて以下のようにファイルを用意。
$ find src/test/resources -type f src/test/resources/META-INF/persistence.xml src/test/resources/config/userdict_ja.txt src/test/resources/sources.json src/test/resources/mappings.json src/test/resources/plugins/analysis-kuromoji/plugin-descriptor.properties
EmbeddedなElasticsearchと、Kuromojiプラグインの設定については以下を参照してください。
Elasticsearch 2.xをEmbeddedableに使う - CLOVER
では、これらのファイルを使ってテストコードを書いていきます。
src/test/java/org/littlewings/hibernate/elasticsearch/EmbeddedElasticsearchTest.java
package org.littlewings.hibernate.elasticsearch; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.apache.commons.io.FileUtils; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.elasticsearch.client.Client; import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.node.Node; import org.elasticsearch.node.NodeBuilder; import org.hibernate.search.backend.elasticsearch.ElasticsearchQueries; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.jpa.Search; import org.hibernate.search.query.engine.spi.QueryDescriptor; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class EmbeddedElasticsearchTest { protected Node node; protected Path dataDirectory; // 以降を埋めていく }
データディレクトリについては、起動時に一時ディレクトリとして作成、終了時に破棄する方針とします。また、マッピング定義、フィールド定義はファイルに保存したものを読み込んで設定するようにします。
というわけで、こういうメソッドを作成。
protected Path createTemporaryDirectory(String prefix) { try { return Files.createTempDirectory(prefix).toAbsolutePath(); } catch (IOException e) { throw new UncheckedIOException(e); } } protected String loadResourceAsString(String path) { try (InputStream is = getClass().getClassLoader().getResourceAsStream(path); InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader reader = new BufferedReader(isr)) { StringBuilder builder = new StringBuilder(); int c; while ((c = reader.read()) != -1) { builder.append((char) c); } return builder.toString(); } catch (IOException e) { throw new UncheckedIOException(e); } }
マッピング定義については、以下のように用意。
src/test/resources/mappings.json
{ "analysis": { "tokenizer": { "kuromoji_tokenizer_search": { "type": "kuromoji_tokenizer", "mode": "search", "discard_punctuation": "true", "user_dictionary": "userdict_ja.txt" } }, "analyzer": { "kuromoji_analyzer": { "type": "custom", "tokenizer": "kuromoji_tokenizer_search", "filter": [ "kuromoji_baseform", "kuromoji_part_of_speech", "cjk_width", "stop", "ja_stop", "kuromoji_stemmer", "lowercase" ] } } } }
フィールド定義については、以下のように用意。
src/test/resources/sources.json
{ "org.littlewings.hibernate.elasticsearch.Contents": { "_source": { "enabled": true }, "_all": { "enabled": true }, "properties": { "content": { "type": "string", "store": "yes", "index": "analyzed", "analyzer": "kuromoji_analyzer" } } } }
ちなみに、Kuromojiプラグイン用のplugin-descriptor.propertiesの中身も。
src/test/resources/plugins/analysis-kuromoji/plugin-descriptor.properties
description=The Japanese (kuromoji) Analysis plugin integrates Lucene kuromoji analysis module into elasticsearch. version=2.2.1 name=analysis-kuromoji site=false jvm=true classname=org.elasticsearch.plugin.analysis.kuromoji.AnalysisKuromojiPlugin java.version=1.7 elasticsearch.version=2.2.1 isolated=true
あとは、@BeforeでElasticsearchを起動して、インデックス、マッピングの定義を作成。
@Before public void setUp() { dataDirectory = createTemporaryDirectory("elasticsearch-"); Settings settings = Settings .settingsBuilder() .put("cluster.name", "test-cluster") .put("network.host", "127.0.0.1") .put("http.port", 9200) .put("transport.tcp.port", 9300) .put("discovery.zen.ping_timeout", "3ms") .put("path.data", dataDirectory.toFile().getPath()) .put("path.home", ".") .put("path.plugins", "./src/test/resources/plugins") .put("path.conf", "./src/test/resources/config") .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .build(); node = NodeBuilder .nodeBuilder() .settings(settings) .node(); Client client = node.client(); IndicesAdminClient indicesAdminClient = client.admin().indices(); indicesAdminClient .prepareCreate("myindex") .setSettings(loadResourceAsString("mappings.json")) .get(); indicesAdminClient .preparePutMapping("myindex") .setType(Contents.class.getName()) .setSource(loadResourceAsString("sources.json")) .get(); }
小さく構成したいので、number_of_shardsやnumber_of_replicasを絞り、discovery.zen.ping_timeoutも短くしています。
Settings settings = Settings .settingsBuilder() .put("cluster.name", "test-cluster") .put("network.host", "127.0.0.1") .put("http.port", 9200) .put("transport.tcp.port", 9300) .put("discovery.zen.ping_timeout", "3ms") .put("path.data", dataDirectory.toFile().getPath()) .put("path.home", ".") .put("path.plugins", "./src/test/resources/plugins") .put("path.conf", "./src/test/resources/config") .put("index.number_of_shards", 1) .put("index.number_of_replicas", 0) .build();
HTTPリッスンは有効にしています。Jestがつなぐので。
@Afterでは、Elasticsearchのノードを落として、データディレクトリとして利用していた一時ディレクトリを削除します。
@After public void tearDown() { node.close(); try { FileUtils.deleteDirectory(dataDirectory.toFile()); } catch (IOException e) { throw new UncheckedIOException(e); } }
これで、先ほどのElasticsearchを別プロセスで起動していたテストコードが動作します。
@Test public void gettingStarted() { EntityManager em = Persistence.createEntityManagerFactory("hibernate.search.pu").createEntityManager(); ///// create data EntityTransaction transaction = em.getTransaction(); transaction.begin(); em.persist(Contents.create("東京都は、日本の首都です。")); em.persist(Contents.create("明日は、晴れるでしょう。")); em.persist(Contents.create("Java EE 7 徹底入門")); em.persist(Contents.create("高速スケーラブル検索エンジン ElasticSearch Server")); em.persist(Contents.create("[改訂新版] Apache Solr入門 オープンソース全文検索エンジン")); transaction.commit(); ///// get FullTextEntityManager FullTextEntityManager ftem = Search.getFullTextEntityManager(em); ///// search QueryDescriptor queryDescriptor1 = ElasticsearchQueries.fromQueryString("content:東京都 OR content:elasticsearch"); FullTextQuery query1 = ftem .createFullTextQuery(queryDescriptor1, Contents.class) .setSort(new Sort(new SortField("id", SortField.Type.LONG))); List<Contents> results1 = query1.getResultList(); assertThat(results1).hasSize(2); assertThat(results1.get(0).getContent()).isEqualTo("東京都は、日本の首都です。"); assertThat(results1.get(1).getContent()).isEqualTo("高速スケーラブル検索エンジン ElasticSearch Server"); ///// search QueryDescriptor queryDescriptor2 = ElasticsearchQueries.fromQueryString("content:京都 OR content:elasticsearch"); FullTextQuery query2 = ftem .createFullTextQuery(queryDescriptor2, Contents.class) .setSort(new Sort(new SortField("id", SortField.Type.LONG))); List<Contents> results2 = query2.getResultList(); assertThat(results2).hasSize(1); assertThat(results2.get(0).getContent()).isEqualTo("高速スケーラブル検索エンジン ElasticSearch Server"); ///// delete transaction.begin(); em.remove(em.find(Contents.class, 4L)); transaction.commit(); ///// search QueryDescriptor queryDescriptor3 = ElasticsearchQueries.fromQueryString("content:elasticsearch"); FullTextQuery query3 = ftem .createFullTextQuery(queryDescriptor3, Contents.class); List<Contents> results3 = query3.getResultList(); assertThat(results3).isEmpty(); }
OKですー。
オマケ)
Elasticsearchとテストを統合したい人は、以下を使うとよいのかもしれません。
https://github.com/alexcojocaru/elasticsearch-maven-plugin
Hibernate Search自身が使っていました。