こんにちは。株式会社 Interfamilia の見習い社員、Wakaです。2度目の記事投稿になります。
最近は天気が不安定で、昼間は日差しが強くて暑いのに、夕方は涼しくて肌寒い!なんてことが多いですね。
体調管理に気をつけねば…と思う今日この頃です。
今回の記事では、自分が日々の開発業務で学んだ知識をご紹介したいと思います!
目次
- OS:Windows 10 Pro
- Java:11.0.9 (OpenJDK)
- Elasticsearch :7.12.1
- Kibana:7.12.1
Elasticsearch Java API の検索クエリをJSON形式で確認したい!
Elasticsearch と Java の連携処理を実装したい場合は、専用ライブラリの利用が欠かせません。
Javaアプリケーション側でクエリを組み立て、RestHighLevelClient
を使用して、Elasticsearch にクエリを投げる!といった要領で使います。かなり柔軟な操作を実現できるようです。
しかし、ここで気になるのが… Javaから投げたクエリが本当に正しいのか? という問題です。
特にElasticsearchの検索処理は、想定通りの結果が得られないことも多々あります。
自分の想定とは全く異なる検索クエリが送られ、間違った処理が実行されている可能性も無きにしも非ず…です。
こうした場合、Java から Elasticsearch に送った検索クエリ内容を、JSON形式でログ出力 すれば、調査がしやすくなります。
その具体的な方法について、今回テストコードを書いて確認してみました。
手順説明にあたって必要な、Elasticsearch Java API 関連の前提知識です。
- 多機能なREST Client クラスです。
- Javaアプリケーションから、http経由で Elasticsearch にアクセス。全ての操作の起点となります。
- Elasticsearch に検索クエリを送る、大元となるクラスです。
SearchSourceBuilder
を引数として source()
メソッドに渡すことで、Es検索用リクエストモデルを生成します。
1
2
3
4
| SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
|
- 検索クエリ全体を制御・統括するクラスです。
query()
メソッドで 任意の QuerryBuilders
を追加することで、検索条件を細かく指定できます。
1
2
| SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy"));
|
- 検索クエリ作成用のユーティリティクラスです。
matchQuery()
や termQuery()
など、様々なメソッドが存在。複雑な検索条件を指定できます。
- 検索結果を格納するレスポンスモデルです。
RestHighLevelClient
の search()
メソッドで検索を実行。処理結果を SearchResponse
形式で取得可能です。
1. Elasticsearch のインデックス作成
ログ出力手順の確認にあたり、Elasticsearch に テスト用インデックスを作成しました。
Kibana で以下のコマンドを実行します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| PUT /animals/
POST /animals/_doc/1
{
"name" : "タカ",
"type" : "鳥類",
"description" : "猛禽類、空の王者"
}
POST /animals/_doc/2
{
"name" : "トラ",
"type" : "哺乳類",
"description" : "肉食動物、森林の覇者"
}
POST /animals/_doc/3
{
"name" : "バッタ",
"type" : "昆虫",
"description" : "驚きの跳躍力、個人的にオンブバッタが好き"
}
|
GET /animals/_search
で、ドキュメントが作成されたことを確認します。問題無さそうです。
Eclipse で新規のGradleプロジェクトを作成して、Elasticsearch Java API をインポートします。
公式ドキュメント「Maven Repository | Java REST Client [master] | Elastic」
の記載内容を参考に、build.gradle
の設定項目として、以下の repositories / dependencies を追記しました。
1
2
3
| repositories {
maven { url "https://snapshots.elastic.co/maven/" }
}
|
1
2
3
| dependencies {
compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.12.1'
}
|
デフォルトの設定を少しだけ修正。最終的に以下のような形に落ち着きました。
※ログ出力用に Logback もインポートしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| plugins {
id 'java-library'
}
repositories {
jcenter()
maven { url "https://snapshots.elastic.co/maven/" }
}
dependencies {
api 'org.apache.commons:commons-math3:3.6.1'
implementation 'com.google.guava:guava:28.2-jre'
testImplementation 'junit:junit:4.12'
compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.12.1'
compile 'ch.qos.logback:logback-classic:1.1.3'
}
|
一通りの準備ができたので、Javaのソースコードを書いていきます。
今回はログ出力の手順確認がしたいだけなので、mainメソッドだけの簡潔な構成になっています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| package test;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EsLogTest {
private static final Logger log = LoggerFactory.getLogger(EsLogTest.class);
public static void main(String[] args) {
log.info("【ログ出力 開始】");
try (RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http")))) {
//クエリを組み立てる
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("name", "タカ"));
SearchRequest searchRequest = new SearchRequest().indices("animals").source(sourceBuilder);
//検索クエリをログ出力
log.info(sourceBuilder.toString());
//Esにクエリを投げ、検索結果を取得
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//検索結果をログ出力
log.info(searchResponse.toString());
log.info("【ログ出力 終了】");
} catch (IOException e) {
e.printStackTrace();
}
}
}
|
クエリ作成の大元となる SearchSourceBuilder
に対して toString()
を実行すると、検索クエリの文字列 (JSON形式) が取得可能 です。
あとは、この文字列を Logback等でログ出力すれば、今回の目標が達成できます!
27
28
29
30
31
32
33
| //クエリを組み立てる
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("name", "タカ"));
SearchRequest searchRequest = new SearchRequest().indices("animals").source(sourceBuilder);
//検索クエリをログ出力
log.info(sourceBuilder.toString());
|
今回のテーマから若干逸れますが、検索処理を実行後のレスポンスの内容も、おまけでログ出力してみました。
SearchResponse
形式で実行結果を受け取れるので、それに対して toString()
を実施。String化・ログ出力します。
35
36
37
38
39
| //Esにクエリを投げ、検索結果を取得
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//検索結果をログ出力
log.info(searchResponse.toString());
|
上記の EsLogTest.java
をコンパイル&実行すると、以下のようなログが出力されました。
3行目 (黄色ハイライトの箇所) に、お目当ての検索クエリがありました!
ちゃんとJSON形式で出力されていますね。
1
2
3
4
5
| 14:24:44.306 [main] INFO test.EsLogTest - 【ログ出力 開始】
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
14:24:46.071 [main] INFO test.EsLogTest - {"query":{"term":{"name":{"value":"タカ","boost":1.0}}}}
14:24:46.288 [main] INFO test.EsLogTest - {"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.9808291,"hits":[{"_index":"animals","_type":"_doc","_id":"1","_score":0.9808291,"_source":{"name":"タカ","type":"鳥類","description":"猛禽類、空の王者"}}]}}
14:24:46.288 [main] INFO test.EsLogTest - 【ログ出力 終了】
|
上の例では、2行目の箇所で Log4j2 関連のエラーが発生しています。
コレについては、build.gradle
に以下のような dependencies を追記したところ、解消されました。
1
2
3
| dependencies {
implementation 'org.apache.logging.log4j:log4j-core:2.14.1'
}
|
出力された検索クエリのJSONを整形して、Kibanaで実行してテストしてみます。
すると、Javaのログで出力されたレスポンス内容 (5行目) と、同一の結果が確認できました。
以上、Elasticsearch Java API の検索クエリをJSON形式でログ出力するための手順説明でした。
SearchSourceBuilder
を String文字列に変換して、ログ出力する だけ。非常にシンプルです。
また、今回は調査が及びませんでしたが、Elasticsearch Java API は、ドキュメントの更新・削除等の処理も用意されています。
こうした操作に関しても、クエリ内容をログ出力するための方法が用意されている…ハズだと思われます。
今後も継続して調査を行い、最適な方法を編み出せたら、弊ブログで連携したいと思います。それではまた!