Elastic Stack (Elasticsearch) Advent Calendar 2019の11日目の記事です
はじめに
「Java High Level REST Client」を使用することで、Javaアプリからhttpを介してElasticsearchへアクセスできる
以前は「TransportClient」が使用されていたが8.0で廃止されているため、
「Java High Level REST Client Tips」 もしくは「Java Low Level REST Client」の使用が推奨されている
今回は、検索(Search API)に絞った使用例を紹介します
注意点
- Java High Level REST ClientはJava 1.8が必要
- Elasticsearchとの接続は、マイナーバージョンの通信保証する
例)
Elasticsearch(7.0) Java High Level REST(7.0) → OK
Elasticsearch(7.0) Java High Level REST(6.8) → NG
※通信を保証するだけで、バージョンアップ時の新機能は対応してないことが多い
準備
Elasticsearchの環境 → ElasticCloud
Java8(JDK1.8.0)
インデックス「qiita」を作成
PUT /qiita
{
"mappings": {
"properties": {
"user": { "type": "keyword" },
"post_date": { "type": "date" },
"active": { "type": "boolean" },
"message": { "type": "text" }
}
}
}
POST qiita/_doc
{
"user" : "qiita",
"post_date" : "2019-12-11T00:10:30Z",
"active":"false",
"message" : "trying out High Level REST Client"
}
GET qiita/_search
...
{
"_index" : "qiita",
"_type" : "_doc",
"_id" : "yjDo5m4Bj4TzcUq3pmoX",
"_score" : 1.0,
"_source" : {
"user" : "qiita",
"post_date" : "2019-12-11T00:10:30Z",
"active" : "false",
"message" : "trying out High Level REST Client"
}
}
...
pon.xml
<dependencies>
...
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.5.0</version>
</dependency>
...
</dependencies>
接続用Clinent作成
基本的には、Initializationの例で問題ない
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
client.close();
ElasticCloudの場合
ElasticCloudなど認証機能を導入している場合は、下記の設定が必要がある
String username = "elastic";
String password = "pass";
String host = "host";
int port = 9243;
int nextPort = 9244;
String protocol = "https";
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
RestClientBuilder client = RestClient.builder(
new HttpHost(host, port, protocol),
new HttpHost(host, nextPort, protocol))
.setHttpClientConfigCallback((h) -> h.setDefaultCredentialsProvider(credentialsProvider));
RestHighLevelClient client = new RestHighLevelClient(client);
client.close();
データ取得
先ほど作成したclientを使用することで「GET qiita/_search」と似た取得ができます
try (RestHighLevelClient client = new RestHighLevelClient(client)) {
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
// quite*のようにワイルドカード指定できる
SearchRequest request = new SearchRequest("qiita").source(searchBuilder);
// データ取得
SearchHits hits = client.search(request, RequestOptions.DEFAULT).getHits();
for(SearchHit hit : hits) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
// 1レコードごとの値を設定
String user = (String) sourceAsMap.get("user");
String post_date = (String) sourceAsMap.get("post_date");
String message = (String) sourceAsMap.get("message");
System.out.println(String.format("user:%s data:%s message:%s",user , post_date, message));
}
}catch (IOException io) {}
参考: Search API
サイズの設定
「SearchSourceBuilder」に設定する
サイズはデフォルトは10件しか取得しない
// default 10
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
searchBuilder.size(100);
ソート
「SearchSourceBuilder」に設定する
SortOrderでASCとDESCを切り替える
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
searchBuilder.sort(new FieldSortBuilder("_id").order(SortOrder.DESC));
検索クエリ(フィルタ)
Building Queriesに書かれているのは一通り使える(全部試したわけではない)が簡単なものを紹介する
基本、「SearchSourceBuilder」に設定する
Range Query(期間指定)
日本時刻の「2019-12-12T00:10:30」から「2019-12-13T00:10:31」まで取得している
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
QueryBuilder query = QueryBuilders
.rangeQuery("post_date")
.from("2019-12-12T00:10:30")
.to("2019-12-13T00:10:31")
.timeZone("+09:00");
searchBuilder.query(query);
注意が必要なのは、下記のように指定すると
「2019-12-09T00:00:00.000」〜「2019-12-13T23:59:59.999」まで取得する
QueryBuilder query = QueryBuilders
.rangeQuery("post_date")
.from("2019-12-12")
.to("2019-12-13")
.timeZone("+09:00");
searchBuilder.query(query);
12日のみのデータを取得するには下記のようになる
fromを含める場合は「includeLower」:true
toを含める場合は「includeUpper」 :true
QueryBuilder query = QueryBuilders
.rangeQuery("post_date")
.from("2019-12-12")
.to("2019-12-13")
.includeLower(true)
.includeUpper(false)
.timeZone("+09:00");
searchBuilder.query(query);
Match Query
全文検索のクエリは下記
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
QueryBuilder query = QueryBuilders.matchQuery("message", "REST Level ");
QueryBuilder query = QueryBuilders.matchQuery("message", "Level REST");
QueryBuilder query = QueryBuilders.matchPhraseQuery("message", "REST Level");
// フレーズ検索なので取得0件
QueryBuilder query = QueryBuilders.matchPhraseQuery("message", "Level REST");
searchBuilder.query(query);
Term Query
完全一致で検索される、Termクエリは下記
QueryBuilder query = QueryBuilders.termQuery("user", "qiita");
Bool Query(AND OR NOT)
Elastic | Elastic SQL | 説明 |
---|---|---|
must | AND | |
filter | AND | スコアを無視する |
should | OR | |
mustnot | NOT | |
※スコアを使用しない検索の場合はなるべくfilterを使う | ||
Elasticsearchのbool queryを利用してAND OR NOTを書いてみるがとても分かりやすい |
AND
「must」「filter」どちらでも良い
「filter」の例はこんな感じ
SearchSourceBuilder searchBuilder = SearchSourceBuilder.searchSource();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
QueryBuilder query1 = QueryBuilders.matchQuery("message", "JAVA");
QueryBuilder query2 = QueryBuilders.matchQuery("message", "REST");
boolQuery.filter(query1);
boolQuery.filter(query2);
searchBuilder.query(boolQuery);
OR
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
QueryBuilder query1 = QueryBuilders.matchQuery("message", "JAVA");
QueryBuilder query2 = QueryBuilders.matchQuery("message", "REST");
boolQuery.should(query1);
boolQuery.should(query2);
NOT
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
QueryBuilder query = QueryBuilders.termQuery("user", "qiita");
boolQuery.mustNot(query);
JSON形式で検索するには?
下記のようにJSON形式でも取得することができる
Search Template API
SearchTemplateRequest request = new SearchTemplateRequest();
request.setRequest(new SearchRequest("qiita"));
request.setScriptType(ScriptType.INLINE);
request.setScript(
"{" +
" \"query\": { \"match\" : { \"{{field}}\" : \"{{value}}\" } }," +
" \"size\" : \"{{size}}\"" +
"}");
Map<String, Object> scriptParams = new HashMap<>();
scriptParams.put("field", "message");
scriptParams.put("value", "REST");
scriptParams.put("size", 5);
request.setScriptParams(scriptParams);
SearchTemplateResponse response = client.searchTemplate(request, RequestOptions.DEFAULT);
SearchResponse searchResponse = response.getResponse();
SearchHit[] results = searchResponse.getHits().getHits();
Frozen indices
6.8.1の「Java High Level REST Client」より使用することができる
Elasticsaerchに「Frozen indices」の実装された時には反映されていないため注意が必要
How to Search Freeze Index using Java High Level REST Client
SearchRequest request = new SearchRequest("qiita").source(searchBuilder);
request.indicesOptions(IndicesOptions.fromOptions(
true,
true,
true,
false,
request.indicesOptions().allowAliasesToMultipleIndices(),
request.indicesOptions().forbidClosedIndices(),
request.indicesOptions().ignoreAliases(),
true // ←ここでfrozen indexを検索できるか設定 true:検索可能
));
参考資料
終わりに
「Java High Level REST Client」の日本語記事が少なく、英語・中国記事を読むことが多いです。
ドキュメントに大体書いてあるのですが、新機能追加後は「Java High Level REST Client」では対応していないみたいなことがありソースやリリースノートを読まないといけないこともあります。
明日は@NAO_MK2さんです。