Skip to content

Commit 58d9999

Browse files
authored
Merge pull request #31653 from loicmathieu/elasticsearch-java-guide
2 parents ac43aae + 3abc68f commit 58d9999

File tree

1 file changed

+81
-114
lines changed

1 file changed

+81
-114
lines changed

docs/src/main/asciidoc/elasticsearch.adoc

Lines changed: 81 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc
66
= Connecting to an Elasticsearch cluster
77
include::_attributes.adoc[]
88
:categories: data
9-
:summary: This guide covers how to use an Elasticsearch cluster using the low level or high level REST clients.
9+
:summary: This guide covers how to interact with an Elasticsearch cluster using the low level REST client or the Elasticsearch Java client.
1010

1111
Elasticsearch is a well known full text search engine and NoSQL datastore.
1212

13-
In this guide, we will see how you can get your REST services to use an Elasticsearch cluster.
13+
In this guide, we will see how you can get your REST services to interact with an Elasticsearch cluster.
1414

15-
Quarkus provides two ways of accessing Elasticsearch: via the lower level `RestClient` or via the `RestHighLevelClient` we will call them
16-
the low level and the high level clients.
15+
Quarkus provides two ways of accessing Elasticsearch:
16+
17+
* The lower level REST Client
18+
* The Elasticsearch Java client
19+
20+
[WARNING]
21+
====
22+
A third Quarkus extension for the "high level REST Client" exists, but is deprecated and will be removed in a future version,
23+
as this client has been deprecated by Elastic and has some licensing issues.
24+
25+
It is highly recommended to upgrade to the new Elasticsearch Java client extension.
26+
====
1727

1828
== Prerequisites
1929

@@ -36,20 +46,22 @@ First, we need a new project. Create a new project with the following command:
3646
:create-app-extensions: resteasy-reactive-jackson,elasticsearch-rest-client
3747
include::{includes}/devtools/create-app.adoc[]
3848

39-
This command generates a Maven structure importing the RESTEasy Reactive/JAX-RS, Jackson, and the Elasticsearch low level client extensions.
40-
After this, the `quarkus-elasticsearch-rest-client` extension has been added to your build file.
49+
This command generates a Maven structure importing the RESTEasy Reactive, Jackson, and Elasticsearch low level REST client extensions.
50+
51+
The Elasticsearch low level REST client comes with the `quarkus-elasticsearch-rest-client` extension
52+
that has been added to your build file.
4153

42-
If you want to use the high level client instead, replace the `elasticsearch-rest-client` extension by the `elasticsearch-rest-high-level-client` extension.
54+
If you want to use the Elasticsearch Java client instead, replace the `quarkus-elasticsearch-rest-client` extension by the `quarkus-elasticsearch-java-client` extension.
4355

4456
[NOTE]
4557
====
4658
We use the `resteasy-reactive-jackson` extension here and not the JSON-B variant because we will use the Vert.x `JsonObject` helper
4759
to serialize/deserialize our objects to/from Elasticsearch and it uses Jackson under the hood.
4860
====
4961

50-
If you don’t want to generate a new project, add the following dependencies to your build file.
62+
To add the extensions to an existing project, follow the instructions below.
5163

52-
For the Elasticsearch low level client, add:
64+
For the Elasticsearch low level REST client, add the following dependency to your build file:
5365

5466
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
5567
.pom.xml
@@ -66,21 +78,21 @@ For the Elasticsearch low level client, add:
6678
implementation("io.quarkus:quarkus-elasticsearch-rest-client")
6779
----
6880

69-
For the Elasticsearch high level client, add:
81+
For the Elasticsearch Java client, add the following dependency to your build file:
7082

7183
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
7284
.pom.xml
7385
----
7486
<dependency>
7587
<groupId>io.quarkus</groupId>
76-
<artifactId>quarkus-elasticsearch-rest-high-level-client</artifactId>
88+
<artifactId>quarkus-elasticsearch-java-client</artifactId>
7789
</dependency>
7890
----
7991

8092
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
8193
.build.gradle
8294
----
83-
implementation("io.quarkus:quarkus-elasticsearch-rest-high-level-client")
95+
implementation("io.quarkus:quarkus-elasticsearch-java-client")
8496
----
8597

8698
== Creating your first JSON REST service
@@ -102,8 +114,10 @@ public class Fruit {
102114

103115
Nothing fancy. One important thing to note is that having a default constructor is required by the JSON serialization layer.
104116

105-
Now create a `org.acme.elasticsearch.FruitService` that will be the business layer of our application and store/load the fruits from the Elasticsearch instance.
106-
Here we use the low level client, if you want to use the high level client instead follow the instructions in the <<using-the-high-level-rest-client,Using the High Level REST Client>> paragraph instead.
117+
Now create a `org.acme.elasticsearch.FruitService` that will be the business layer of our application
118+
and will store/load the fruits from the Elasticsearch instance.
119+
Here we use the low level REST client, if you want to use the Java API client instead,
120+
follow the instructions in the <<using-the-elasticsearch-java-client,Using the Elasticsearch Java Client>> paragraph instead.
107121

108122
[source,java]
109123
----
@@ -179,14 +193,11 @@ public class FruitService {
179193
}
180194
}
181195
----
182-
183-
In this example you can note the following:
184-
185-
1. We inject an Elasticsearch low level `RestClient` into our service.
186-
2. We create an Elasticsearch request.
187-
3. We use Vert.x `JsonObject` to serialize the object before sending it to Elasticsearch, you can use whatever you want to serialize to JSON.
188-
4. We send the request (indexing request here) to Elasticsearch.
189-
5. In order to deserialize the object from Elasticsearch, we again use Vert.x `JsonObject`.
196+
<1> We inject an Elasticsearch low level `RestClient` into our service.
197+
<2> We create an Elasticsearch request.
198+
<3> We use Vert.x `JsonObject` to serialize the object before sending it to Elasticsearch, you can use whatever you want to serialize your objects to JSON.
199+
<4> We send the request (indexing request here) to Elasticsearch.
200+
<5> In order to deserialize the object from Elasticsearch, we again use Vert.x `JsonObject`.
190201

191202
Now, create the `org.acme.elasticsearch.FruitResource` class as follows:
192203

@@ -245,15 +256,15 @@ The implementation is pretty straightforward and you just need to define your en
245256
== Configuring Elasticsearch
246257
The main property to configure is the URL to connect to the Elasticsearch cluster.
247258

248-
A sample configuration should look like this:
259+
For a typical clustered Elasticsearch service, a sample configuration would look like the following:
249260

250261
[source,properties]
251262
----
252263
# configure the Elasticsearch client for a cluster of two nodes
253264
quarkus.elasticsearch.hosts = elasticsearch1:9200,elasticsearch2:9200
254265
----
255266

256-
In this example, we are using a single instance running on localhost:
267+
In our case, we are using a single instance running on localhost:
257268

258269
[source,properties]
259270
----
@@ -264,9 +275,10 @@ quarkus.elasticsearch.hosts = localhost:9200
264275
If you need a more advanced configuration, you can find the comprehensive list of supported configuration properties at the end of this guide.
265276

266277
[[dev-services]]
267-
=== Dev Services (Configuration Free Databases)
278+
=== Dev Services
279+
268280
Quarkus supports a feature called Dev Services that allows you to start various containers without any config.
269-
In the case of Elasticsearch this support extends to the default Elasticsearch connection.
281+
In the case of Elasticsearch, this support extends to the default Elasticsearch connection.
270282
What that means practically is that, if you have not configured `quarkus.elasticsearch.hosts`, Quarkus will automatically
271283
start an Elasticsearch container when running tests or dev mode, and automatically configure the connection.
272284

@@ -276,8 +288,8 @@ we recommend that you use the `%prod.` profile to define your Elasticsearch sett
276288

277289
For more information you can read the xref:elasticsearch-dev-services.adoc[Dev Services for Elasticsearch guide].
278290

279-
280291
=== Programmatically Configuring Elasticsearch
292+
281293
On top of the parametric configuration, you can also programmatically apply additional configuration to the client by implementing a `RestClientBuilder.HttpClientConfigCallback` and annotating it with `ElasticsearchClientConfig`. You may provide multiple implementations and configuration provided by each implementation will be applied in a randomly ordered cascading manner.
282294

283295
For example, when accessing an Elasticsearch cluster that is set up for TLS on the HTTP layer, the client needs to trust the certificate that Elasticsearch is using. The following is an example of setting up the client to trust the CA that has signed the certificate that Elasticsearch is using, when that CA certificate is available in a PKCS#12 keystore.
@@ -321,6 +333,7 @@ public class SSLContextConfigurator implements RestClientBuilder.HttpClientConfi
321333
}
322334
}
323335
----
336+
324337
See https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/_encrypted_communication.html[Elasticsearch documentation] for more details on this particular example.
325338

326339
[NOTE]
@@ -344,12 +357,9 @@ docker run --name elasticsearch -e "discovery.type=single-node" -e "ES_JAVA_OPT
344357

345358
== Running the application
346359

347-
Now let's run our application via Quarkus dev mode:
360+
Let's start our application in dev mode:
348361

349-
:devtools-wrapped:
350-
+
351362
include::{includes}/devtools/dev.adoc[]
352-
:!devtools-wrapped:
353363

354364
You can add new fruits to the list via the following curl command:
355365

@@ -358,84 +368,52 @@ You can add new fruits to the list via the following curl command:
358368
curl localhost:8080/fruits -d '{"name": "bananas", "color": "yellow"}' -H "Content-Type: application/json"
359369
----
360370

361-
And search for fruits by name or color via the flowing curl command:
371+
And search for fruits by name or color via the following curl command:
362372

363373
[source,bash,subs=attributes+]
364374
----
365375
curl localhost:8080/fruits/search?color=yellow
366376
----
367377

368-
== Using the High Level REST Client
369-
370-
Quarkus provides support for the Elasticsearch High Level REST Client but keep in mind that it comes with some caveats:
371-
372-
- It drags a lot of dependencies - especially Lucene -, which doesn't fit well with Quarkus philosophy. The Elasticsearch team is aware of this issue, and it might improve sometime in the future.
373-
- It is tied to a certain version of the Elasticsearch server: you cannot use a High Level REST Client version 7 to access a server version 6.
374-
375-
[WARNING]
376-
====
377-
Due to the license change made by Elastic for the Elasticsearch High Level REST Client,
378-
we are keeping in Quarkus the last Open Source version of this particular client, namely 7.10,
379-
and it won't be upgraded to newer versions.
378+
== Using the Elasticsearch Java Client
380379

381-
Given this client was deprecated by Elastic and replaced by a new Open Source Java client,
382-
the Elasticsearch High Level REST Client extension is considered deprecated and will be removed from the Quarkus codebase at some point in the future.
383-
384-
Note that contrary to the High Level REST client, we are using the latest version of the Low Level REST client (which is still Open Source),
385-
and, while we believe it should work, the situation is less than ideal and might cause some issues.
386-
Feel free to override the versions of the clients in your applications depending on your requirements,
387-
but be aware of https://www.elastic.co/blog/elastic-license-v2[the new licence of the High Level REST Client] for versions 7.11+:
388-
it is not Open Source and has several usage restrictions.
389-
390-
We will eventually provide an extension for the new Open Source Java client, but it will require changes in your applications
391-
as it is an entirely new client.
392-
====
393-
394-
Here is a version of the `FruitService` using the high level client instead of the low level one:
380+
Here is a version of the `FruitService` using the Elasticsearch Java Client instead of the low level one:
395381

396382
[source,java]
397383
----
398-
import java.io.IOException;
399-
import java.util.ArrayList;
400-
import java.util.List;
401-
384+
import co.elastic.clients.elasticsearch.ElasticsearchClient;
385+
import co.elastic.clients.elasticsearch._types.FieldValue;
386+
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
387+
import co.elastic.clients.elasticsearch.core.*;
388+
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
402389
import jakarta.enterprise.context.ApplicationScoped;
403390
import jakarta.inject.Inject;
391+
import org.acme.elasticsearch.Fruit;
404392
405-
import org.elasticsearch.action.get.GetRequest;
406-
import org.elasticsearch.action.get.GetResponse;
407-
import org.elasticsearch.action.index.IndexRequest;
408-
import org.elasticsearch.action.search.SearchRequest;
409-
import org.elasticsearch.action.search.SearchResponse;
410-
import org.elasticsearch.client.RequestOptions;
411-
import org.elasticsearch.client.RestHighLevelClient;
412-
import org.elasticsearch.common.xcontent.XContentType;
413-
import org.elasticsearch.index.query.QueryBuilders;
414-
import org.elasticsearch.search.SearchHit;
415-
import org.elasticsearch.search.SearchHits;
416-
import org.elasticsearch.search.builder.SearchSourceBuilder;
417-
418-
import io.vertx.core.json.JsonObject;
393+
import java.io.IOException;
394+
import java.util.List;
395+
import java.util.stream.Collectors;
419396
420397
@ApplicationScoped
421398
public class FruitService {
422399
@Inject
423-
RestHighLevelClient restHighLevelClient; // <1>
400+
ElasticsearchClient client; // <1>
424401
425402
public void index(Fruit fruit) throws IOException {
426-
IndexRequest request = new IndexRequest("fruits"); // <2>
427-
request.id(fruit.id);
428-
request.source(JsonObject.mapFrom(fruit).toString(), XContentType.JSON); // <3>
429-
restHighLevelClient.index(request, RequestOptions.DEFAULT); // <4>
403+
IndexRequest<Fruit> request = IndexRequest.of( // <2>
404+
b -> b.index("fruits")
405+
.id(fruit.id)
406+
.document(fruit)); // <3>
407+
client.index(request); // <4>
430408
}
431409
432410
public Fruit get(String id) throws IOException {
433-
GetRequest getRequest = new GetRequest("fruits", id);
434-
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
435-
if (getResponse.isExists()) {
436-
String sourceAsString = getResponse.getSourceAsString();
437-
JsonObject json = new JsonObject(sourceAsString); // <5>
438-
return json.mapTo(Fruit.class);
411+
GetRequest getRequest = GetRequest.of(
412+
b -> b.index("fruits")
413+
.id(id));
414+
GetResponse<Fruit> getResponse = client.get(getRequest, Fruit.class);
415+
if (getResponse.found()) {
416+
return getResponse.source();
439417
}
440418
return null;
441419
}
@@ -449,46 +427,35 @@ public class FruitService {
449427
}
450428
451429
private List<Fruit> search(String term, String match) throws IOException {
452-
SearchRequest searchRequest = new SearchRequest("fruits");
453-
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
454-
searchSourceBuilder.query(QueryBuilders.matchQuery(term, match));
455-
searchRequest.source(searchSourceBuilder);
456-
457-
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
458-
SearchHits hits = searchResponse.getHits();
459-
List<Fruit> results = new ArrayList<>(hits.getHits().length);
460-
for (SearchHit hit : hits.getHits()) {
461-
String sourceAsString = hit.getSourceAsString();
462-
JsonObject json = new JsonObject(sourceAsString);
463-
results.add(json.mapTo(Fruit.class));
464-
}
465-
return results;
430+
SearchRequest searchRequest = SearchRequest.of(
431+
b -> b.index("fruits")
432+
.query(QueryBuilders.match().field(term).query(FieldValue.of(match)).build()._toQuery()));
433+
434+
SearchResponse<Fruit> searchResponse = client.search(searchRequest, Fruit.class);
435+
HitsMetadata<Fruit> hits = searchResponse.hits();
436+
return hits.hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
466437
}
467438
}
468439
----
469-
470-
In this example you can note the following:
471-
472-
1. We inject an Elasticsearch `RestHighLevelClient` inside the service.
473-
2. We create an Elasticsearch index request.
474-
3. We use Vert.x `JsonObject` to serialize the object before sending it to Elasticsearch, you can use whatever you want to serialize to JSON.
475-
4. We send the request to Elasticsearch.
476-
5. In order to deserialize the object from Elasticsearch, we again use Vert.x `JsonObject`.
440+
<1> We inject an `ElasticsearchClient` inside the service.
441+
<2> We create an Elasticsearch index request using a builder.
442+
<3> We directly pass the object to the request as the Java API client has a serialization layer.
443+
<4> We send the request to Elasticsearch.
477444

478445
== Hibernate Search Elasticsearch
479446

480-
Quarkus supports Hibernate Search with Elasticsearch via the `hibernate-search-orm-elasticsearch` extension.
447+
Quarkus supports Hibernate Search with Elasticsearch via the `quarkus-hibernate-search-orm-elasticsearch` extension.
481448

482449
Hibernate Search Elasticsearch allows to synchronize your JPA entities to an Elasticsearch cluster and offers a way to query your Elasticsearch cluster using the Hibernate Search API.
483450

484-
If you're interested in it, you can read the xref:hibernate-search-orm-elasticsearch.adoc[Hibernate Search with Elasticsearch guide].
451+
If you are interested in it, please consult the xref:hibernate-search-orm-elasticsearch.adoc[Hibernate Search with Elasticsearch guide].
485452

486453
== Cluster Health Check
487454

488-
If you are using the `quarkus-smallrye-health` extension, both the extension will automatically add a readiness health check
455+
If you are using the `quarkus-smallrye-health` extension, both extensions will automatically add a readiness health check
489456
to validate the health of the cluster.
490457

491-
So when you access the `/q/health/ready` endpoint of your application you will have information about the cluster status.
458+
So when you access the `/q/health/ready` endpoint of your application, you will have information about the cluster status.
492459
It uses the cluster health endpoint, the check will be down if the status of the cluster is **red**, or the cluster is not available.
493460

494461
This behavior can be disabled by setting the `quarkus.elasticsearch.health.enabled` property to `false` in your `application.properties`.
@@ -507,7 +474,7 @@ You can then point your browser to `http://localhost:8080/fruits.html` and use y
507474

508475
== Conclusion
509476

510-
Accessing an Elasticsearch cluster from a low level or a high level client is easy with Quarkus as it provides easy configuration, CDI integration and native support for it.
477+
Accessing an Elasticsearch cluster from the low level REST client or the Elasticsearch Java client is easy with Quarkus as it provides easy configuration, CDI integration and native support for it.
511478

512479
== Configuration Reference
513480

0 commit comments

Comments
 (0)