Elasticsearch Java High Level REST ClientでCRUD操作を行う
本記事ではローカル開発環境にElasticsearchサーバを立て、公式Javaクライアント(High Level REST Client)を使ってCRUD操作を行う手順を書きます。
本記事に使ったコードは下記リポジトリでも公開しています。
使用しているソフトウェアのバージョン
- Elasticsearch 7.10.1
- Kibana 7.10.1
- Elasticsearch Java High Level REST Client 7.10.1
- Java 11(AdoptOpenJDK 11.0.9.1)
- Gradle 6.7.1
- Docker 19.03.12
- macOS 11.1
環境構築
- http://localhost:9200 でElasticsearchにアクセスできる
- http://localhost:5601 でKibanaにアクセスできる(Elasticsearchの動作確認用)
- CLIで動作するJavaアプリケーションで上記エンドポイントにアクセスできる
という状態を目標に構築します。
DockerでElasticsearch + Kibana
docker-compose を使ってElasticsearchとKibanaのサーバを立てます。
version: '2.2'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
environment:
- discovery.type=single-node
- cluster.name=es-docker-cluster
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data01:/usr/share/elasticsearch/data
ports:
- 9200:9200
kibana:
image: docker.elastic.co/kibana/kibana:7.10.1
ports:
- 5601:5601
volumes:
data01:
driver: local
Dockerの設定については下記公式ドキュメントを参考にしています。
$ docker-compose up -d
でDockerコンテナを起動し、動作確認します。
Elasticsearchはcurlで動作確認。
$ curl -XGET "http://localhost:9200/"
{
"name" : "75936f071a0a",
"cluster_name" : "es-docker-cluster",
"cluster_uuid" : "PXddY1qDQr-y1vkY6mBwrQ",
"version" : {
"number" : "7.10.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "1c34507e66d7db1211f66f3513706fdf548736aa",
"build_date" : "2020-12-05T01:00:33.671820Z",
"build_snapshot" : false,
"lucene_version" : "8.7.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
Kibanaは http://localhost:5601 にブラウザでアクセスします。
Javaアプリケーションの作成
次にGradleでJavaアプリケーションを作成します。
$ gradle init --type java-application \
--dsl groovy \
--test-framework junit-jupiter \
--project-name esjava \
--package esjava
こんな感じのディレクトリ構成になるはず。
$ tree
.
├── app
│ ├── build.gradle
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── esjava
│ │ │ └── App.java
│ │ └── resources
│ └── test
│ ├── java
│ │ └── esjava
│ │ └── AppTest.java
│ └── resources
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
12 directories, 8 files
app/build.gradle を編集して、GradleのdependenciesにElasticsearchのクライアントライブラリを追加します(gradle init
で生成されたコメントは削除し、guavaも使わないのでdependenciesから削除しています)。
plugins {
id 'application'
}
repositories {
jcenter()
maven { url 'https://snapshots.elastic.co/maven/' }
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.10.1'
}
application {
mainClass.set('esjava.App')
}
tasks.named('test') {
useJUnitPlatform()
}
Elasticsearchが localhost:9200 で動いている状態で、以下のJavaコードを実行することで動作確認できます。
package esjava;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new SearchRequest();
var response = client.search(request, RequestOptions.DEFAULT);
System.out.println(response.status().toString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
アプリケーションの実行結果は以下のようになります( gradlew
ではなくIDEの実行機能を使っても問題ありません)。
$ ./gradlew run
> Task :app:run
# ※Elasticsearchクライアントから警告が出るが省略
OK
BUILD SUCCESSFUL in 2s
2 actionable tasks: 1 executed, 1 up-to-date
ElasticsearchをJavaクライアントで操作する
ドキュメントの作成
curlならこんな感じで、 _doc
にリクエストを送ります。POSTメソッドを使うとid指定なしで作成、PUTメソッドならid指定もできます。
$ curl -XPOST "http://localhost:9200/my_index/_doc/" \
-H 'Content-Type: application/json' \
-d '{"message":"Hello, Elasticsearch!"}'
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "mWzetnYB_4LLU29fksD0",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
これをJavaで書くとこう。Document APIs の Index APIを使います。リクエストに使うJSONの組み立て方法はいくつかありますが、ここでは request.source()
にMapオブジェクトを渡しています。
package esjava;
import java.io.IOException;
import java.util.Map;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new IndexRequest("my_index");
// request.id("1"); // 指定したidでリソースを作成 or 更新(HTTPのPUT相当)
request.source(Map.of("message", "Hello, Elasticsearch!"));
var response = client.index(request, RequestOptions.DEFAULT);
System.out.printf("created: %s\n", response.getId());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
ドキュメントをid指定して取得
作成したドキュメントのidは、Elasticsearchに生成を任せている場合、 mWzetnYB_4LLU29fksD0 のようなランダムな文字列になっています。このidを指定することでドキュメントを取得できます。
$ curl -XGET "http://localhost:9200/my_index/_doc/mWzetnYB_4LLU29fksD0?pretty"
{
"_index" : "my_index",
"_type" : "_doc",
"_id" : "mWzetnYB_4LLU29fksD0",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"message" : "Hello, Elasticsearch!"
}
}
Javaでは Document APIs の Get API を使います。
package esjava;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new GetRequest("my_index", "mWzetnYB_4LLU29fksD0");
var response = client.get(request, RequestOptions.DEFAULT);
System.out.println(response.getSourceAsString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
メタデータが不要で _source
フィールドの値のみ取得できれば良い場合は、 _source
にリクエストを投げます。
$ curl -XGET "http://localhost:9200/my_index/_source/mWzetnYB_4LLU29fksD0?pretty"
{
"message" : "Hello, Elasticsearch!"
}
Javaの場合は Document APIs の Get Source APIを使います。
package esjava;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.GetSourceRequest;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new GetSourceRequest("my_index", "mWzetnYB_4LLU29fksD0");
var response = client.getSource(request, RequestOptions.DEFAULT);
System.out.println(response.toString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
ドキュメントの検索
_search
に検索クエリを送るとさまざまな検索が行えます。
$ curl -XGET "http://localhost:9200/my_index/_search" \
-H 'Content-Type: application/json' \
-d '{"query":{"match":{"message":"Elasticsearch"}}}'
# レスポンスは割愛
Javaでは Search APIs の Search API を使います。
package esjava;
import java.io.IOException;
import java.util.Arrays;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new SearchRequest("my_index");
request.source(
SearchSourceBuilder.searchSource()
.query(QueryBuilders.matchQuery("message", "Elasticsearch")));
var response = client.search(request, RequestOptions.DEFAULT);
Arrays.stream(response.getHits().getHits())
.forEach(h -> System.out.println(h.getSourceAsString()));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
ドキュメントの更新
更新は完全な置き換えと一部フィールドの書き換えでエンドポイントが異なります。
置き換えの場合は _doc
にPUTリクエストを送ります。
curl -XPUT "http://elasticsearch:9200/my_index/_doc/mWzetnYB_4LLU29fksD0" \
-H 'Content-Type: application/json' \
-d '{"message":"Hello, Again"}'
Javaでは Document APIsのIndex APIを使います。
package esjava;
import java.io.IOException;
import java.util.Map;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new IndexRequest("my_index");
request.id("mWzetnYB_4LLU29fksD0");
request.source(Map.of("message", "Hello, Again"));
var response = client.index(request, RequestOptions.DEFAULT);
System.out.println(response.getResult());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
部分更新は _update
にPOSTリクエストを送ります(POSTというと新規作成というイメージがありますが、ここでは「更新」というリソースを作成する、という意味だと思います)。
curl -XPOST "http://localhost:9200/my_index/_update/mWzetnYB_4LLU29fksD0" \
-H 'Content-Type: application/json' \
-d '{"doc":{"message":"Hello, Again"}}'
Javaでは Document APIs の Update APIを使います。
package esjava;
import java.io.IOException;
import java.util.Map;
import org.apache.http.HttpHost;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new UpdateRequest("my_index", "mWzetnYB_4LLU29fksD0");
request.doc(Map.of("message", "Hello, Again"));
var response = client.update(request, RequestOptions.DEFAULT);
System.out.println(response.getResult());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
ドキュメントを削除する
curl -XDELETE "http://localhost:9200/my_index/_doc/mWzetnYB_4LLU29fksD0"
Javaでは Documents APIs の Delete API を使います。
package esjava;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
public class App {
public static void main(String[] args) {
try (var client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")))) {
var request = new DeleteRequest("my_index", "mWzetnYB_4LLU29fksD0");
var response = client.delete(request, RequestOptions.DEFAULT);
System.out.println(response.getResult());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
むすび
以上、Elasticsearch Java High Level REST Clientで基本的なCRUD操作を行う方法を紹介しました。High Level REST Clientでは、Document APIsを使うことでCRUD操作を行えます。
Document APIsには他にも、ドキュメントの存在確認(Exists)、まとめて取得(Multi GET)など様々なAPIがあります。
Elasticsearchの本領は「検索(Search)」ですが、そちらは記事を改めてまとめたいと思います。
Discussion