I am trying to set up the new Elasticsearch Java API Client. Here is my configuration:
// Set up elasticsearch cluster hosts
String[] elasticsearchHosts = elasticsearchEndpoints.split(",");
HttpHost[] httpHosts = Arrays.stream(elasticsearchHosts)
.map(HttpHost::create).toArray(HttpHost[]::new);
// Create the low level client
final RestClientBuilder restClientbuilder = RestClient.builder(httpHosts);
// Set elasticsearch user credentials
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
restClientbuilder.setHttpClientConfigCallback(
b -> b.setDefaultCredentialsProvider(credentialsProvider));
RestClient restClient = restClientbuilder.build();
// Create the transport with a Jackson mapper which maps classes to json
ElasticsearchTransport transport = new RestClientTransport(restClient,
new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
and I have a simple search:
Query termQuery = TermQuery.of(m -> m
.field("myField")
.value("myValue"))
._toQuery();
var searchRequest =
co.elastic.clients.elasticsearch.core.SearchRequest.of(s -> s
.index("index-name")
.query(termQuery));
SearchResponse<myDTOClass>
searchResponse = elasticsearchJavaApiClient.search(searchRequest, myDTOClass.class);
It sends the request successfully but the problem is the response. Here is the relevant stack trace error:
co.elastic.clients.transport.TransportException: node: https://myDomain:9200/, status: 200, [es/search] Failed to decode response
...
Caused by: co.elastic.clients.json.JsonpMappingException: Error deserializing co.elastic.clients.elasticsearch.core.search.Hit: jakarta.json.JsonException: Jackson exception (JSON path: hits.hits[0]._source) (line no=1, column no=336, offset=-1)
at co.elastic.clients.json.JsonpMappingException.from0(JsonpMappingException.java:134)
at co.elastic.clients.json.JsonpMappingException.from(JsonpMappingException.java:121)
at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:218)
at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:148)
...
Caused by: jakarta.json.JsonException: Jackson exception
at co.elastic.clients.json.jackson.JacksonUtils.convertException(JacksonUtils.java:39)
at co.elastic.clients.json.jackson.JacksonJsonpMapper$JacksonValueParser.deserialize(JacksonJsonpMapper.java:142)
...
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.OffsetDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 335] (through reference chain: com.mygw.dto.MyModel["MyField"])
SOLUTION: I found the reason and wanted to share with you.
The reason is that I used OffsetDateTime in my myDTOClass.class but Jackson library does not parse Java 8 Time classes. So you need to add the module to your configuration above.
Relevant part:
...
RestClient restClient = restClientbuilder.build();
ObjectMapper mapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();
// Create the transport with a Jackson mapper which maps classes to json
ElasticsearchTransport transport = new RestClientTransport(restClient,
new JacksonJsonpMapper(mapper));
...
And of course you need to add lombok annotations to myDTOClass.class
@Getter
@ToString
@NoArgsConstructor
WORKAROUND: Instead of using myDTOClass.class, you can use Void.class to test the connection and ObjectNode.class to get the response in raw json.
SearchResponse<myDTOClass> searchResponse = elasticsearchJavaApiClient.search(searchRequest, myDTOClass.class);
Here is the documentation: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/reading.html
In the end this was the solution for me:
SearchResponse<ObjectNode> searchResponse = elasticsearchJavaApiClient.search(searchRequest, ObjectNode.class);
Of course I had to convert the raw json object into my own myDTOClass.