Elasticsearch Java Client 9.0.0
Discover what changed in the 9.0.0 version of the Java client.
Java version update
While previous versions of the client used to target Java 8, from version 9.0 forward the client will target Java 17.
Impact
Any project that targets Java 8 and needs to import version 9.0 of the elasticsearch Java client.
Action
Projects that import version 9.0 of the elasticsearch Java client as a dependency will have to be updated to be compatible Java 17.
RestClient optional dependency
The new version of the client introduces the Rest5Client as an alternative to the legacy RestClient. The RestClient is part of the external dependency elasticsearch-rest-client, which is now optional since an alternative is available.
Impact
Any project updating the elasticsearch Java client.
Action
Either update the code to use the new Rest5Client or import the elasticsearch-rest-client dependency:
// gradle
implementation("org.elasticsearch.client:elasticsearch-rest-client:9.0.0")
<!--maven-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>9.0.0</version>
</dependency>
Server accurate aggregation number values
In previous versions of the client, when the server returned number that was both always present and could be null (for example Aggregation results), the client would default to 0 and deserialize the number into a primitive data type instead of an Object. This design choice was changed in this version, changing the fields type to support null and be coherent with the server response.
For more information, check the relevant issue.
Impact
The following classes are affected:
- elasticsearch._types.aggregations.ArrayPercentilesItem
value: modified fromdoubletoDouble, now optional
- elasticsearch._types.aggregations.ExtendedStatsAggregate
stdDeviation: modified fromdoubletoDouble, now optionalstdDeviationPopulation: modified fromdoubletoDouble, now optionalstdDeviationSampling: modified fromdoubletoDouble, now optionalsumOfSquares: modified fromdoubletoDouble, now optionalvariance: modified fromdoubletoDouble, now optionalvariancePopulation: modified fromdoubletoDouble, now optionalvarianceSampling: modified fromdoubletoDouble, now optional
- elasticsearch._types.aggregations.SingleMetricAggregateBase
value: modified fromdoubletoDouble, now optional
- elasticsearch._types.aggregations.StandardDeviationBounds
lower: modified fromdoubletoDouble, now optionallowerPopulation: modified fromdoubletoDouble, now optionallowerSampling: modified fromdoubletoDouble, now optionalupper: modified fromdoubletoDouble, now optionalupperPopulation: modified fromdoubletoDouble, now optionalupperSampling: modified fromdoubletoDouble, now optional
- elasticsearch._types.aggregations.StatsAggregate
avg: modified fromdoubletoDouble, now optionalmax: modified fromdoubletoDouble, now optionalmin: modified fromdoubletoDouble, now optional
- elasticsearch._types.aggregations.StringStatsAggregate
avgLength: modified fromdoubletoDouble, now optionalentropy: modified fromdoubletoDouble, now optionalmaxLength: modified frominttoInteger, now optionalminLength: modified frominttoInteger, now optional
- elasticsearch._types.aggregations.TTestAggregate
value: modified fromdoubletoDouble, now optional
Action
Steps for mitigating deprecation impact:
- Make sure to handle the possible
nullvalue correctly to avoid NullPointerException - Remove any workaround that was previously suggested to understand whether the
0returned was actually0ornull
Script builder update
The Script class used to only support inline string scripts, now it can also support other formats like mustache.
For more information, check the relevant issue.
Impact
The following classes are affected:
- elasticsearch._types.Script
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch._types.ScriptTransform
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch._types.StoredScript
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.core.msearch_template.TemplateConfig
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.core.RenderSearchTemplateRequest
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.core.search.PhraseSuggestCollateQuery
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.core.SearchTemplateRequest
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.ingest.ScriptProcessor
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.watcher.ScriptCondition
source: modified fromStringtoelasticsearch._types.ScriptSource
- elasticsearch.ingest.ProcessorBase
if_: modified fromStringtoelasticsearch._types.Script
Action
Since the Script builder now supports two variants (string and object), the builder has to be updated to specify the type.
Example with PutScript:
- Old string script
esClient.putScript(p -> p .id("my-script") .script(s -> s .lang("painless") .source("Math.log(_score * 2) + params['my_modifier']") ) ); - New string script
esClient.putScript(p -> p .id("my-script") .script(s -> s .lang("painless") .source(so -> so .scriptString("Math.log(_score * 2) + params['my_modifier']") ) ) ); - New object script
esClient.putScript(p -> p .id("my-script") .script(s -> s .lang("mustache") .source(so -> so .scriptTemplate(st -> st .query(q -> q .match(m -> m .field("message") .query("{{query_string}}") ) ) ) ) ) );
Support for include_named_queries_score
include_named_queries_score is a query parameter that can be enabled for SearchRequest and changes the type of matched_queries in SearchResponse.hits.hits from List<String> to Map<String, Double>. Previous versions of the client didn't support the options, since the json deserialize couldn't know which response type to expect. To support the feature, new versions of the client will always treat matched_queries as a Map<String, Double>, where the values are null in case the original field was returned as a list.
For more information, check the relevant issue.
Impact
The following classes are affected:
- elasticsearch.core.search.Hit
matchedQueries: modified fromList<String>toMap<String, Double>
Action
Reading matched_queries will be different since it's now a Map, so instead of iterating on the List values now the Map's KeySet has to be iterated to obtain the same result as before.
Package change for EsqlFormat
Breaking change caused by refactoring the ESQL package.
Impact
The following classes are affected:
- elasticsearch.esql.QueryRequest
format: modified fromelasticsearch.esql.query.EsqlFormattoelasticsearch.esql.EsqlFormat
Action
Change the import from elasticsearch.esql.query.EsqlFormat to elasticsearch.esql.EsqlFormat
Body name and getter change for Responses
Response types should have had a specific body name and getter matching the body type, but in previous versions of the client it was just called valueBody.
Impact
The following classes are affected:
- elasticsearch.ssl.CertificatesResponse
valueBodyis nowcertificates
- elasticsearch.snapshot.RepositoryVerifyIntegrityResponse
valueBodyis nowresult
- elasticsearch.snapshot.GetRepositoryResponse
valueBodyis nowrepositories
- elasticsearch.inference.TextEmbeddingResponse
valueBodyis nowinferenceResult
- elasticsearch.cluster.StateResponse
valueBodyis nowstate
- elasticsearch.cat.TransformsResponse
valueBodyis nowtransforms
- elasticsearch.cat.ThreadPoolResponse
valueBodyis nowthreadPools
- elasticsearch.cat.TemplatesResponse
valueBodyis nowtemplates
- elasticsearch.cat.TasksResponse
valueBodyis nowtasks
- elasticsearch.cat.SnapshotsResponse
valueBodyis nowsnapshots
- elasticsearch.cat.ShardsResponse
valueBodyis nowshards
- elasticsearch.cat.SegmentsResponse
valueBodyis nowsegments
- elasticsearch.cat.RepositoriesResponse
valueBodyis nowrepositories
- elasticsearch.cat.RecoveryResponse
valueBodyis nowrecoveryRecords
- elasticsearch.cat.PluginsResponse
valueBodyis nowplugins
- elasticsearch.cat.PendingTasksResponse
valueBodyis nowpendingTasks
- elasticsearch.cat.NodesResponse
valueBodyis nownodes
- elasticsearch.cat.NodeattrsResponse
valueBodyis nownodeAttributes
- elasticsearch.cat.MlTrainedModelsResponse
valueBodyis nowtrainedModels
- elasticsearch.cat.MlJobsResponse
valueBodyis nowjobs
- elasticsearch.cat.MlDataFrameAnalyticsResponse
valueBodyis nowdataFrameAnalytics
- elasticsearch.cat.MlDatafeedsResponse
valueBodyis nowdatafeeds
- elasticsearch.cat.MasterResponse
valueBodyis nowmasters
- elasticsearch.cat.IndicesResponse
valueBodyis nowindices
- elasticsearch.cat.HealthResponse
valueBodyis nowhealthRecords
- elasticsearch.cat.FielddataResponse
valueBodyis nowfielddataRecords
- elasticsearch.cat.CountResponse
valueBodyis nowcountRecords
- elasticsearch.cat.ComponentTemplatesResponse
valueBodyis nowcomponentTemplates
- elasticsearch.cat.AllocationResponse
valueBodyis nowallocations
- elasticsearch.cat.AliasesResponse
valueBodyis nowaliases
Action
Replace the valueBody() getter with the specific getter depending on the class used.
Map to NamedValue indicesBoost, dynamicTemplates
indicesBoost and dynamicTemplates were wrongly mapped as List<Map>, but the server doesn't actually accept more than one value in both cases, so the type has been changed to List<NamedValue>.
Impact
The following classes are affected:
- elasticsearch.core.SearchRequest
indicesBoost: modified fromList<Map<String,Double>>toList<NamedValue<Double>>
- elasticsearch.async_search.SubmitRequest
indicesBoost: modified fromList<Map<String,Double>>toList<NamedValue<Double>>
- elasticsearch.indices.PutMappingRequest
dynamicTemplates: modified fromList<Map<String, DynamicTemplate>>toList<NamedValue<DynamicTemplate>>
Action
Change the builder to use the correct type.
Example with SearchRequest:
- Old
esClient.search(s -> s .index("*") .indicesBoost(Map.of("index", 1.0)) ,Void.class); - New
esClient.search(s -> s .index("*") .indicesBoost(NamedValue.of("index", 1.0)) ,Void.class);
Removed deprecated classes
The Elasticsearch server does not accept these requests anymore, or they were replaced with a more specific request.
Impact
The following classes are affected:
- elasticsearch._types.analysis.LanguageAnalyzer: removed
- elasticsearch.nodes.NodeReloadError: removed
- elasticsearch.search_application.list.SearchApplicationListItem: removed
- elasticsearch.inference.*:
- InferenceRequest, InferenceResponse, InferenceResult, InferenceResultVariant: removed as part of a complete refactor of the Inference API
Action
Find out which are the new requests accepted by the 9.0.0 version of the server.
Miscellaneous changes
The reason for these changes is to better reflect what the Elasticsearch server actually accepts/sends, or to fix bugs.
Impact
The following classes are affected:
- elasticsearch._types.analysis.FingerprintAnalyzer
maxOutputSize: modified frominttoInteger, now optional
- elasticsearch._types.query_dsl.SpanTermQuery
value: modified fromStringtoelasticsearch._types.FieldValue
- elasticsearch.async_search.SubmitRequest
maxConcurrentShardRequests: modified fromLongtoInteger
- elasticsearch.core.msearch.RequestItem
body: modified fromelasticsearch.core.msearch.MultisearchBodytoelasticsearch.core.search.SearchRequestBody
- elasticsearch.core.MsearchRequest
maxConcurrentSearches: modified fromLongtoIntegermaxConcurrentShardRequests: modified fromLongtoInteger
- elasticsearch.core.SearchRequest
maxConcurrentShardRequests: modified fromLongtoInteger
- elasticsearch.fleet.FleetSearchRequest
maxConcurrentShardRequests: modified fromLongtoInteger
- elasticsearch.graph.VertexInclude
boost: modified fromdoubletoDouble, now optional
- elasticsearch.security.RoleMappingRule
field: modified fromelasticsearch.security.FieldRuletoutil.NamedValue
- elasticsearch._types.mapping.DenseVectorProperty
elementType: modified fromStringtoelasticsearch._types.mapping.DenseVectorElementTypesimilarity: modified fromStringtoelasticsearch._types.mapping.DenseVectorSimilarity
- elasticsearch._types.mapping.DynamicTemplate
runtime: modified fromelasticsearch._types.mapping.Propertytoelasticsearch._types.mapping.RuntimeField
- elasticsearch._types.mapping.ObjectProperty
subobjects: modified fromBooleantoelasticsearch._types.mapping.Subobjects
- elasticsearch._types.mapping.TypeMapping
subobjects: modified fromBooleantoelasticsearch._types.mapping.Subobjects
- elasticsearch.ccr.follow_info.FollowerIndexParameters
maxOutstandingReadRequests: modified frominttoLong, now optionalmaxOutstandingWriteRequests: modified frominttoInteger, now optionalmaxReadRequestOperationCount: modified frominttoInteger, now optionalmaxWriteBufferCount: modified frominttoInteger, now optionalmaxWriteRequestOperationCount: modified frominttoInteger, now optional
- elasticsearch.ccr.FollowRequest
maxOutstandingWriteRequests: modified fromLongtoIntegermaxReadRequestOperationCount: modified fromLongtoIntegermaxWriteBufferCount: modified fromLongtoIntegermaxWriteRequestOperationCount: modified fromLongtoInteger
- elasticsearch.core.ScriptsPainlessExecuteRequest
context: modified fromStringtoelasticsearch.core.scripts_painless_execute.PainlessContext
- elasticsearch.indices.get_data_lifecycle.DataStreamWithLifecycle
lifecycle: modified fromelasticsearch.indices.DataStreamLifecycletoelasticsearch.indices.DataStreamLifecycleWithRollover
- elasticsearch.ml.TrainedModelDeploymentNodesStats
routingState: modified fromelasticsearch.ml.TrainedModelAssignmentRoutingTabletoelasticsearch.ml.TrainedModelAssignmentRoutingStateAndReason
- elasticsearch.search_application.PutRequest
searchApplication: modified fromelasticsearch.search_application.SearchApplicationtoelasticsearch.search_application.SearchApplicationParameters
Action
For types that went from the primitive type to the matching Object, make sure to handle the possible null value.
For msearch.RequestItem, MultisearchBody was just an alias to map SearchRequestBody, so it can be replaced without other changes.
For DenseVectorProperty, choose the Enum value that matches the old String.
New ElasticsearchClient builder
ElasticsearchClient now can be created with just a few lines of code:
ElasticsearchClient esClient = ElasticsearchClient.of(b -> b
.host(serverUrl)
.apiKey(apiKey)
);
The classic version of the builder is still available, both with the new Rest5Client and the legacy RestClient; while the Rest5Client is included with the Java client's dependency, the legacy RestClient must be imported separately:
implementation("org.elasticsearch.client:elasticsearch-rest-client:9.0.0")
New Rest5Client
A new version of the transport layer, based on the Apache HttpClient 5 is now available to use, as an alternative to the legacy RestClient.
More information on the dedicated section: Rest5Client.
BulkIngester retry policy
Retry logic can now be enabled allowing the BulkIngester to retry operations that failed with error 429 (too many requests), hoping that the error will recover and the request will go through. Users can configure the desired backoff policy using the backoffPolicy() method in the BulkIngester builder:
BulkIngester ingester = BulkIngester.of(b -> b
.client(client)
...
.listener(listener)
.flushInterval(1000, TimeUnit.MILLISECONDS)
.backoffPolicy(BackoffPolicy.constantBackoff(50L, 8))
This is an example of constant backoff, meaning the single failed operation will be retried 8 times every 50 milliseconds.
Default class for methods requiring TDocument
Some requests in the client require a second parameter to define the result class, for example search, meaning the compiler will complain while the query is being written, which can be annoying. We added overload methods that use Void.class as default type, so that the correct type can be eventually added later into writing the query.
Example with SearchRequest:
- Old:
esClient.search(s -> s .index("my-index") .query(q -> q .matchAll(m -> m) ) ,Object.class); - New:
esClient.search(s -> s .index("my-index") .query(q -> q .matchAll(m -> m) ) );
Builder setters overloads with variant type
Added more setters allowing to build requests with a specific type variant instead of having to use the parent class and then select the desired variant later.
Example with Query, where the query field can now accept a MatchAllQuery (or any other variant) directly:
- Old:
esClient.search(s -> s .index("my-index") .query(q -> q .matchAll(m -> m) ) ); - New:
esClient.search(s -> s .index("my-index") .query(MatchAllQuery.of(m -> m)) );
Example with Aggregation, where the aggregations field can now accept AverageAggregation (or any other variant) directly:
- Old:
// using functional builder shortcut esClient.search(s -> s .aggregations("agg", a -> a .avg(av -> av .field("price") ) ) ); // using Aggregation class builder esClient.search(s -> s .aggregations("agg", Aggregation.of(ag -> ag .avg(av -> av .field("price")) ) ) ); - New:
esClient.search(s -> s .aggregations("agg", AverageAggregation.of(av -> av .field("price")) ) );
Nothing was deprecated in this version of the client.