Elasticsearch Java Client 9.0.0
Discover what changed in the 9.0.0 version of the Java client.
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 fromdouble
toDouble
, now optional
- elasticsearch._types.aggregations.ExtendedStatsAggregate
stdDeviation
: modified fromdouble
toDouble
, now optionalstdDeviationPopulation
: modified fromdouble
toDouble
, now optionalstdDeviationSampling
: modified fromdouble
toDouble
, now optionalsumOfSquares
: modified fromdouble
toDouble
, now optionalvariance
: modified fromdouble
toDouble
, now optionalvariancePopulation
: modified fromdouble
toDouble
, now optionalvarianceSampling
: modified fromdouble
toDouble
, now optional
- elasticsearch._types.aggregations.SingleMetricAggregateBase
value
: modified fromdouble
toDouble
, now optional
- elasticsearch._types.aggregations.StandardDeviationBounds
lower
: modified fromdouble
toDouble
, now optionallowerPopulation
: modified fromdouble
toDouble
, now optionallowerSampling
: modified fromdouble
toDouble
, now optionalupper
: modified fromdouble
toDouble
, now optionalupperPopulation
: modified fromdouble
toDouble
, now optionalupperSampling
: modified fromdouble
toDouble
, now optional
- elasticsearch._types.aggregations.StatsAggregate
avg
: modified fromdouble
toDouble
, now optionalmax
: modified fromdouble
toDouble
, now optionalmin
: modified fromdouble
toDouble
, now optional
- elasticsearch._types.aggregations.StringStatsAggregate
avgLength
: modified fromdouble
toDouble
, now optionalentropy
: modified fromdouble
toDouble
, now optionalmaxLength
: modified fromint
toInteger
, now optionalminLength
: modified fromint
toInteger
, now optional
- elasticsearch._types.aggregations.TTestAggregate
value
: modified fromdouble
toDouble
, now optional
Action
Steps for mitigating deprecation impact:
- Make sure to handle the possible
null
value correctly to avoid NullPointerException - Remove any workaround that was previously suggested to understand whether the
0
returned was actually0
ornull
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 fromString
toelasticsearch._types.ScriptSource
- elasticsearch._types.ScriptTransform
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch._types.StoredScript
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.core.msearch_template.TemplateConfig
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.core.RenderSearchTemplateRequest
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.core.search.PhraseSuggestCollateQuery
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.core.SearchTemplateRequest
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.ingest.ScriptProcessor
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.watcher.ScriptCondition
source
: modified fromString
toelasticsearch._types.ScriptSource
- elasticsearch.ingest.ProcessorBase
if_
: modified fromString
toelasticsearch._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.EsqlFormat
toelasticsearch.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
valueBody
is nowcertificates
- elasticsearch.snapshot.RepositoryVerifyIntegrityResponse
valueBody
is nowresult
- elasticsearch.snapshot.GetRepositoryResponse
valueBody
is nowrepositories
- elasticsearch.inference.TextEmbeddingResponse
valueBody
is nowinferenceResult
- elasticsearch.cluster.StateResponse
valueBody
is nowstate
- elasticsearch.cat.TransformsResponse
valueBody
is nowtransforms
- elasticsearch.cat.ThreadPoolResponse
valueBody
is nowthreadPools
- elasticsearch.cat.TemplatesResponse
valueBody
is nowtemplates
- elasticsearch.cat.TasksResponse
valueBody
is nowtasks
- elasticsearch.cat.SnapshotsResponse
valueBody
is nowsnapshots
- elasticsearch.cat.ShardsResponse
valueBody
is nowshards
- elasticsearch.cat.SegmentsResponse
valueBody
is nowsegments
- elasticsearch.cat.RepositoriesResponse
valueBody
is nowrepositories
- elasticsearch.cat.RecoveryResponse
valueBody
is nowrecoveryRecords
- elasticsearch.cat.PluginsResponse
valueBody
is nowplugins
- elasticsearch.cat.PendingTasksResponse
valueBody
is nowpendingTasks
- elasticsearch.cat.NodesResponse
valueBody
is nownodes
- elasticsearch.cat.NodeattrsResponse
valueBody
is nownodeAttributes
- elasticsearch.cat.MlTrainedModelsResponse
valueBody
is nowtrainedModels
- elasticsearch.cat.MlJobsResponse
valueBody
is nowjobs
- elasticsearch.cat.MlDataFrameAnalyticsResponse
valueBody
is nowdataFrameAnalytics
- elasticsearch.cat.MlDatafeedsResponse
valueBody
is nowdatafeeds
- elasticsearch.cat.MasterResponse
valueBody
is nowmasters
- elasticsearch.cat.IndicesResponse
valueBody
is nowindices
- elasticsearch.cat.HealthResponse
valueBody
is nowhealthRecords
- elasticsearch.cat.FielddataResponse
valueBody
is nowfielddataRecords
- elasticsearch.cat.CountResponse
valueBody
is nowcountRecords
- elasticsearch.cat.ComponentTemplatesResponse
valueBody
is nowcomponentTemplates
- elasticsearch.cat.AllocationResponse
valueBody
is nowallocations
- elasticsearch.cat.AliasesResponse
valueBody
is 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 fromint
toInteger
, now optional
- elasticsearch._types.query_dsl.SpanTermQuery
value
: modified fromString
toelasticsearch._types.FieldValue
- elasticsearch.async_search.SubmitRequest
maxConcurrentShardRequests
: modified fromLong
toInteger
- elasticsearch.core.msearch.RequestItem
body
: modified fromelasticsearch.core.msearch.MultisearchBody
toelasticsearch.core.search.SearchRequestBody
- elasticsearch.core.MsearchRequest
maxConcurrentSearches
: modified fromLong
toInteger
maxConcurrentShardRequests
: modified fromLong
toInteger
- elasticsearch.core.SearchRequest
maxConcurrentShardRequests
: modified fromLong
toInteger
- elasticsearch.fleet.FleetSearchRequest
maxConcurrentShardRequests
: modified fromLong
toInteger
- elasticsearch.graph.VertexInclude
boost
: modified fromdouble
toDouble
, now optional
- elasticsearch.security.RoleMappingRule
field
: modified fromelasticsearch.security.FieldRule
toutil.NamedValue
- elasticsearch._types.mapping.DenseVectorProperty
elementType
: modified fromString
toelasticsearch._types.mapping.DenseVectorElementType
similarity
: modified fromString
toelasticsearch._types.mapping.DenseVectorSimilarity
- elasticsearch._types.mapping.DynamicTemplate
runtime
: modified fromelasticsearch._types.mapping.Property
toelasticsearch._types.mapping.RuntimeField
- elasticsearch._types.mapping.ObjectProperty
subobjects
: modified fromBoolean
toelasticsearch._types.mapping.Subobjects
- elasticsearch._types.mapping.TypeMapping
subobjects
: modified fromBoolean
toelasticsearch._types.mapping.Subobjects
- elasticsearch.ccr.follow_info.FollowerIndexParameters
maxOutstandingReadRequests
: modified fromint
toLong
, now optionalmaxOutstandingWriteRequests
: modified fromint
toInteger
, now optionalmaxReadRequestOperationCount
: modified fromint
toInteger
, now optionalmaxWriteBufferCount
: modified fromint
toInteger
, now optionalmaxWriteRequestOperationCount
: modified fromint
toInteger
, now optional
- elasticsearch.ccr.FollowRequest
maxOutstandingWriteRequests
: modified fromLong
toInteger
maxReadRequestOperationCount
: modified fromLong
toInteger
maxWriteBufferCount
: modified fromLong
toInteger
maxWriteRequestOperationCount
: modified fromLong
toInteger
- elasticsearch.core.ScriptsPainlessExecuteRequest
context
: modified fromString
toelasticsearch.core.scripts_painless_execute.PainlessContext
- elasticsearch.indices.get_data_lifecycle.DataStreamWithLifecycle
lifecycle
: modified fromelasticsearch.indices.DataStreamLifecycle
toelasticsearch.indices.DataStreamLifecycleWithRollover
- elasticsearch.ml.TrainedModelDeploymentNodesStats
routingState
: modified fromelasticsearch.ml.TrainedModelAssignmentRoutingTable
toelasticsearch.ml.TrainedModelAssignmentRoutingStateAndReason
- elasticsearch.search_application.PutRequest
searchApplication
: modified fromelasticsearch.search_application.SearchApplication
toelasticsearch.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.