Transitioning to the New Java Client
Starting with Elasticsearch 7.15, the RestHighLevelClient was deprecated, making way for the new Elasticsearch Java API Client as the official standard for version 8.0 and beyond. This modern client supports virtually all Elasticsearch APIs (excluding Vector title search and Find structure), natively handles all API data types, and removes the need for raw JSON value properties.
Project Setup
Add the required dependencies to your Maven project:
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.12.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
Establishing the Connection
To avoid repetitive boilerplate, a utility method can manage the client lifecycle:
public ElasticsearchClient buildClient() throws IOException {
RestClient restClient = RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
Index Management
Creating an Index
ElasticsearchClient esClient = buildClient();
CreateIndexResponse createResponse = esClient.indices().create(req -> req.index("app_users"));
System.out.println("Index Created: " + createResponse.acknowledged());
Retrieving an Index
ElasticsearchClient esClient = buildClient();
GetIndexResponse getResponse = esClient.indices().get(req -> req.index("app_users"));
System.out.println("Index Mappings: " + getResponse.result());
Deleting an Index
ElasticsearchClient esClient = buildClient();
DeleteIndexResponse deleteResponse = esClient.indices().delete(req -> req.index("app_users"));
System.out.println("Index Deleted: " + deleteResponse.acknowledged());
Document Operations
Define a data structure for the documents:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String fullName;
private String gender;
private Integer years;
}
Inserting a Document
ElasticsearchClient esClient = buildClient();
Person person = new Person("Alice Smith", "female", 28);
CreateResponse insertResponse = esClient.create(req -> req
.index("app_users")
.id("usr_001")
.document(person)
);
System.out.println("Insert Result: " + insertResponse.result());
Fetching a Document
ElasticsearchClient esClient = buildClient();
GetResponse<Person> getResponse = esClient.get(req -> req
.index("app_users")
.id("usr_001"),
Person.class
);
System.out.println("Fetched Document: " + getResponse.source());
Updating a Document
ElasticsearchClient esClient = buildClient();
Map<String, Object> updateFields = Map.of("years", 29);
UpdateResponse<Person> updateResponse = esClient.update(req -> req
.index("app_users")
.id("usr_001")
.doc(updateFields),
Person.class
);
System.out.println("Update Result: " + updateResponse.result());
Removing a Document
ElasticsearchClient esClient = buildClient();
DeleteResponse deleteResponse = esClient.delete(req -> req
.index("app_users")
.id("usr_001")
);
System.out.println("Delete Result: " + deleteResponse.result());
Bulk Document Insertion
ElasticsearchClient esClient = buildClient();
List<BulkOperation> bulkOps = List.of(
BulkOperation.of(op -> op.create(cr -> cr.index("app_users").id("usr_002").document(new Person("Bob", "male", 32)))),
BulkOperation.of(op -> op.create(cr -> cr.index("app_users").id("usr_003").document(new Person("Charlie", "male", 25))))
);
BulkResponse bulkResponse = esClient.bulk(req -> req.index("app_users").operations(bulkOps));
System.out.println("Bulk Insert Errors: " + bulkResponse.errors());
Bulk Document Deletion
ElasticsearchClient esClient = buildClient();
List<BulkOperation> deleteOps = List.of(
BulkOperation.of(op -> op.delete(dr -> dr.index("app_users").id("usr_002"))),
BulkOperation.of(op -> op.delete(dr -> dr.index("app_users").id("usr_003")))
);
BulkResponse bulkDeleteResponse = esClient.bulk(req -> req.operations(deleteOps));
System.out.println("Bulk Delete Errors: " + bulkDeleteResponse.errors());
Search Operations
Match All
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> searchResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.matchAll(m -> m)),
Person.class
);
searchResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Pagination
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> pageResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.matchAll(m -> m))
.from(0)
.size(5),
Person.class
);
pageResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Sorting Results
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> sortResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.matchAll(m -> m))
.sort(s -> s.field(f -> f.field("years").order(SortOrder.Asc))),
Person.class
);
sortResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Field Filtering
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> filterResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.matchAll(m -> m))
.source(src -> src.filter(f -> f.includes("fullName", "years"))),
Person.class
);
filterResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Boolean Combination Queries
Using must requires all conditions to be met, while should requires at least one to match.
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> boolMustResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.bool(b -> b
.must(m -> m.match(t -> t.field("years").query(25)))
.mustNot(mn -> mn.match(t -> t.field("gender").query("male")))
)),
Person.class
);
SearchResponse<Person> boolShouldResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.bool(b -> b
.should(s -> s.match(t -> t.field("years").query(32)))
.should(s -> s.match(t -> t.field("gender").query("male")))
)),
Person.class
);
Range Queries
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> rangeResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.range(r -> r.field("years").gte(JsonData.of(25)).lt(JsonData.of(30)))),
Person.class
);
rangeResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Fuzzy Queries
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> fuzzyResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.fuzzy(f -> f.field("fullName").value("Alce").fuzziness("1"))),
Person.class
);
fuzzyResponse.hits().hits().forEach(hit -> System.out.println(hit.source()));
Highlighting Matches
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> highlightResponse = esClient.search(req -> req
.index("app_users")
.query(q -> q.term(t -> t.field("fullName").value("Alice")))
.highlight(h -> h.fields("fullName", f -> f.preTags("<em>").postTags("</em>"))),
Person.class
);
highlightResponse.hits().hits().forEach(hit -> System.out.println(hit.highlight()));
Aggregation Queries
ElasticsearchClient esClient = buildClient();
SearchResponse<Person> maxAgeResponse = esClient.search(req -> req
.index("app_users")
.aggregations("max_years", a -> a.max(m -> m.field("years"))),
Person.class
);
maxAgeResponse.aggregations().get("max_years").max().value();
SearchResponse<Person> groupByAgeResponse = esClient.search(req -> req
.index("app_users")
.aggregations("age_distribution", a -> a.terms(t -> t.field("years"))),
Person.class
);
groupByAgeResponse.aggregations().get("age_distribution").lterms().buckets().array().forEach(bucket ->
System.out.println("Age: " + bucket.key() + ", Count: " + bucket.docCount())
);