Compare commits

..

64 Commits
main ... 5.3.10

Author SHA1 Message Date
Mark Paluch
c68f607374
Release version 5.3.10 (2024.0.10).
See #3056
2025-03-14 07:35:59 +01:00
Mark Paluch
fb9c70c51b
Prepare 5.3.10 (2024.0.10).
See #3056
2025-03-14 07:35:41 +01:00
Peter-Josef Meisch
0c4a641163
Add testcontainers-local.properties handling.
Original Pull Request #3062
Closes #3061

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 64f88ae9aca33d335ac2840f6bbbefe014cf4b7c)
(cherry picked from commit 8558c44714a36b4dc8f0fd7f65ccd733265bba49)
2025-03-09 12:58:55 +01:00
Peter-Josef Meisch
d7abbc719f
Fix mapping of property names in sort parameters.
Original Pull Request #3074
Closes #3072

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 42383624eadc79cdee0f564ad30c89d3eb486ae2)
(cherry picked from commit 88e2e9da2aeefff178d305d867a553b6345a914c)
2025-03-09 12:58:55 +01:00
Mark Paluch
bd99c90de1
After release cleanups.
See #3045
2025-02-14 09:52:51 +01:00
Mark Paluch
b5ad00bccf
Prepare next development iteration.
See #3045
2025-02-14 09:52:50 +01:00
Mark Paluch
01f39d6807
Release version 5.3.9 (2024.0.9).
See #3045
2025-02-14 09:50:00 +01:00
Mark Paluch
19970bacc6
Prepare 5.3.9 (2024.0.9).
See #3045
2025-02-14 09:49:41 +01:00
Mark Paluch
d7aed4a8cc
Update CI Properties.
See #3045
2025-02-11 15:22:27 +01:00
Mark Paluch
b708fc10db
After release cleanups.
See #3025
2025-01-17 10:34:47 +01:00
Mark Paluch
33c253b071
Prepare next development iteration.
See #3025
2025-01-17 10:34:46 +01:00
Mark Paluch
6bb18b9a0a
Release version 5.3.8 (2024.0.8).
See #3025
2025-01-17 10:32:18 +01:00
Mark Paluch
e5252da066
Prepare 5.3.8 (2024.0.8).
See #3025
2025-01-17 10:32:01 +01:00
Peter-Josef Meisch
a49b1b52a3
Update copyright notices to 2025.
Original Pull Request #3036
Closes #3034
2025-01-03 08:57:48 +01:00
Alfonso
7982ff7986
fix: use scripted field name to populate entity.
Original Pull Request: #3023
Closes: #3022

(cherry picked from commit 944e7e81ddf0482ea996be32253c9cbfd65d1e4f)
(cherry picked from commit 5d52918a5c90708408650b4c4c2a51ef157d4176)
2024-12-14 18:59:38 +01:00
Christoph Strobl
1c9441a1b4
After release cleanups.
See #3003
2024-12-13 10:54:22 +01:00
Christoph Strobl
e0cc849952
Prepare next development iteration.
See #3003
2024-12-13 10:54:21 +01:00
Christoph Strobl
1b50013fc2
Release version 5.3.7 (2024.0.7).
See #3003
2024-12-13 10:51:13 +01:00
Christoph Strobl
5418ef9e03
Prepare 5.3.7 (2024.0.7).
See #3003
2024-12-13 10:50:51 +01:00
Mark Paluch
8d81e499bc
After release cleanups.
See #2989
2024-11-15 11:47:58 +01:00
Mark Paluch
e457b1678b
Prepare next development iteration.
See #2989
2024-11-15 11:47:57 +01:00
Mark Paluch
6c34dc53f3
Release version 5.3.6 (2024.0.6).
See #2989
2024-11-15 11:45:38 +01:00
Mark Paluch
85b6acebb2
Prepare 5.3.6 (2024.0.6).
See #2989
2024-11-15 11:45:23 +01:00
Mark Paluch
00155c2b31
Update CI Properties.
See #2989
2024-11-15 10:39:02 +01:00
Peter-Josef Meisch
d0020be57d
fix geohash conversion
Original Pull Request #3002
Closes #3001

(cherry picked from commit 7f5bfffc348786a80444e9e16247a9dc657693f0)
2024-11-08 19:09:05 +01:00
Mark Paluch
20a6140fe9
Upgrade to Maven Wrapper 3.9.9.
See #2999
2024-11-07 09:49:32 +01:00
Mark Paluch
cdb48c8226
After release cleanups.
See #2981
2024-10-18 11:39:06 +02:00
Mark Paluch
493476567a
Prepare next development iteration.
See #2981
2024-10-18 11:39:04 +02:00
Mark Paluch
a7c148653f
Release version 5.3.5 (2024.0.5).
See #2981
2024-10-18 11:36:33 +02:00
Mark Paluch
3092db9e7d
Prepare 5.3.5 (2024.0.5).
See #2981
2024-10-18 11:36:17 +02:00
Mark Paluch
d8917f1cb1
Consistently run all CI steps with the same user.
See #2982
2024-10-09 09:27:19 +02:00
Jens Schauder
cc533b25f1
After release cleanups.
See #2966
2024-09-13 11:40:09 +02:00
Jens Schauder
c5231d879d
Prepare next development iteration.
See #2966
2024-09-13 11:40:08 +02:00
Jens Schauder
950ca0fc2a
Release version 5.3.4 (2024.0.4).
See #2966
2024-09-13 11:36:55 +02:00
Jens Schauder
95a86f558b
Prepare 5.3.4 (2024.0.4).
See #2966
2024-09-13 11:36:35 +02:00
Peter-Josef Meisch
8117e5a174
Remove Blockhound
Original Pull Request #2978
Closes #2977

(cherry picked from commit d06c122fd55e55b8886b1d3a3389516b08066784)
2024-09-04 18:17:42 +02:00
HAN SEUNGWOO
3a9a959918
Set refresh on DeleteByQueryRequest by DeleteQuery.
Original Pull Request #2976
Closes #2973

(cherry picked from commit b1b232d354374d4003a1d640e1aec96bf0a6d687)
2024-09-03 20:27:14 +02:00
Peter-Josef Meisch
a179dd0643
Add excludeFromSource handling to multifield.
Original Pull Request #2975
Closes #2971

(cherry picked from commit 555b5702467a904ecd40bac486d3ba4bdb71a744)
2024-08-31 21:25:40 +02:00
Peter-Josef Meisch
310ea07c6f
Update versions.adoc 2024-08-19 20:27:42 +02:00
Jens Schauder
34a277cd7d
After release cleanups.
See #2939
2024-08-16 10:08:54 +02:00
Jens Schauder
878dc029ec
Prepare next development iteration.
See #2939
2024-08-16 10:08:53 +02:00
Jens Schauder
c931812c6a
Release version 5.3.3 (2024.0.3).
See #2939
2024-08-16 10:05:57 +02:00
Jens Schauder
c514e020b8
Prepare 5.3.3 (2024.0.3).
See #2939
2024-08-16 10:05:39 +02:00
Mark Paluch
31a4ad715f
Upgrade to Maven Wrapper 3.9.8.
See #2959
2024-08-08 10:23:16 +02:00
Mark Paluch
03efe1b910
Update CI properties.
See #2939
2024-08-08 10:19:20 +02:00
Peter-Josef Meisch
9d5d2efb40
Update versions.adoc 2024-08-07 18:41:12 +02:00
Eric Haag
b7266961d9
Migrate build to Spring Develocity Conventions extension.
* Migrate build to Spring Develocity Conventions extension.

* Adopt Develocity environment variables.

Closes #2944
2024-08-01 14:54:23 +02:00
Mark Paluch
d96cd02572
Bundle Javadoc with Antora documentation site.
Closes #2948.
2024-07-31 14:53:25 +02:00
Jens Schauder
ceb0225850
After release cleanups.
See #2932
2024-07-12 19:12:15 +02:00
Jens Schauder
5c59f73e00
Prepare next development iteration.
See #2932
2024-07-12 19:12:14 +02:00
Jens Schauder
44b1c9e848
Release version 5.3.2 (2024.0.2).
See #2932
2024-07-12 19:09:19 +02:00
Jens Schauder
1770f98a74
Prepare 5.3.2 (2024.0.2).
See #2932
2024-07-12 19:09:01 +02:00
Peter-Josef Meisch
bad0a80313
Enable use of search_after with field_collapse.
Original Pull Request #2937
Closes #2935

(cherry picked from commit dd156b9e29650f80c3aad91a6632734b7159b029)
2024-07-06 11:37:51 +02:00
Peter-Josef Meisch
92dd6e8599
Update migration-guide-5.2-5.3.adoc 2024-07-04 20:58:41 +02:00
Mark Paluch
c793be8ab4
Switch to Broadcom docker proxy.
Closes #2934
2024-06-20 11:21:08 +02:00
Mark Paluch
07ae79f9ce
After release cleanups.
See #2913
2024-06-14 10:48:00 +02:00
Mark Paluch
47c84b84af
Prepare next development iteration.
See #2913
2024-06-14 10:47:59 +02:00
Mark Paluch
5ba1e5dc77
Release version 5.3.1 (2024.0.1).
See #2913
2024-06-14 10:45:40 +02:00
Mark Paluch
5ddcd55942
Prepare 5.3.1 (2024.0.1).
See #2913
2024-06-14 10:45:24 +02:00
Peter-Josef Meisch
be4a77ad21
Update nav.adoc, add loink to migration guide 5.2 to 5.3 2024-05-27 19:39:26 +02:00
Peter-Josef Meisch
7fa3cb74a1
Upgrade to Elasticsearch 8.13.4.
Original Pull Request #2917
Closes #2916
2024-05-18 18:43:34 +00:00
Peter-Josef Meisch
ba9edf8ec8
Fix max dim value for dense vector.
Closes #2911

(cherry picked from commit e997b39f68bc0967c06c27a43e52b83afdb0b522)
2024-05-18 18:26:23 +02:00
Mark Paluch
e4a39ae285
After release cleanups.
See #2896
2024-05-17 12:03:29 +02:00
Mark Paluch
7392222793
Prepare next development iteration.
See #2896
2024-05-17 11:51:48 +02:00
383 changed files with 2261 additions and 7911 deletions

View File

@ -8,7 +8,3 @@
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.text=ALL-UNNAMED
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED

View File

@ -1,3 +1,3 @@
#Thu Nov 07 09:47:28 CET 2024 #Thu Nov 07 09:49:32 CET 2024
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

2
Jenkinsfile vendored
View File

@ -9,7 +9,7 @@ pipeline {
triggers { triggers {
pollSCM 'H/10 * * * *' pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) upstream(upstreamProjects: "spring-data-commons/3.3.x", threshold: hudson.model.Result.SUCCESS)
} }
options { options {

View File

@ -62,7 +62,7 @@ public class MyService {
=== Using the RestClient === Using the RestClient
Please check the https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration[official documentation]. Please check the [official documentation](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration).
=== Maven configuration === Maven configuration
@ -168,7 +168,7 @@ Building the documentation builds also the project without running tests.
$ ./mvnw clean install -Pantora $ ./mvnw clean install -Pantora
---- ----
The generated documentation is available from `target/site/index.html`. The generated documentation is available from `target/antora/site/index.html`.
== Examples == Examples

View File

@ -1,20 +1,24 @@
# Java versions # Java versions
java.main.tag=24.0.1_9-jdk-noble java.main.tag=17.0.13_11-jdk-focal
java.next.tag=24.0.1_9-jdk-noble java.next.tag=22.0.2_9-jdk-jammy
# Docker container images - standard # Docker container images - standard
docker.java.main.image=library/eclipse-temurin:${java.main.tag} docker.java.main.image=library/eclipse-temurin:${java.main.tag}
docker.java.next.image=library/eclipse-temurin:${java.next.tag} docker.java.next.image=library/eclipse-temurin:${java.next.tag}
# Supported versions of MongoDB # Supported versions of MongoDB
docker.mongodb.6.0.version=6.0.23 docker.mongodb.4.4.version=4.4.25
docker.mongodb.7.0.version=7.0.20 docker.mongodb.5.0.version=5.0.21
docker.mongodb.8.0.version=8.0.9 docker.mongodb.6.0.version=6.0.10
docker.mongodb.7.0.version=7.0.2
# Supported versions of Redis # Supported versions of Redis
docker.redis.6.version=6.2.13 docker.redis.6.version=6.2.13
docker.redis.7.version=7.2.4 docker.redis.7.version=7.2.4
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.16
# Docker environment settings # Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home

50
pom.xml
View File

@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId> <artifactId>spring-data-elasticsearch</artifactId>
<version>6.0.0-SNAPSHOT</version> <version>5.3.10</version>
<parent> <parent>
<groupId>org.springframework.data.build</groupId> <groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId> <artifactId>spring-data-parent</artifactId>
<version>4.0.0-SNAPSHOT</version> <version>3.3.10</version>
</parent> </parent>
<name>Spring Data Elasticsearch</name> <name>Spring Data Elasticsearch</name>
@ -18,16 +18,16 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url> <url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties> <properties>
<springdata.commons>4.0.0-SNAPSHOT</springdata.commons> <springdata.commons>3.3.10</springdata.commons>
<!-- version of the ElasticsearchClient --> <!-- version of the ElasticsearchClient -->
<elasticsearch-java>9.0.3</elasticsearch-java> <elasticsearch-java>8.13.4</elasticsearch-java>
<hoverfly>0.19.0</hoverfly> <hoverfly>0.14.4</hoverfly>
<log4j>2.23.1</log4j> <log4j>2.18.0</log4j>
<jsonassert>1.5.3</jsonassert> <jsonassert>1.5.1</jsonassert>
<testcontainers>1.20.0</testcontainers> <testcontainers>1.18.0</testcontainers>
<wiremock>3.9.1</wiremock> <wiremock>2.35.1</wiremock>
<java-module-name>spring.data.elasticsearch</java-module-name> <java-module-name>spring.data.elasticsearch</java-module-name>
@ -131,11 +131,9 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- the old RestCLient is an optional dependency for user that still want to use it-->
<dependency> <dependency>
<groupId>org.elasticsearch.client</groupId> <groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>${elasticsearch-java}</version> <version>${elasticsearch-java}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -143,14 +141,6 @@
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
</dependency> </dependency>
<!-- Jackson JSON Mapper --> <!-- Jackson JSON Mapper -->
@ -265,8 +255,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wiremock</groupId> <groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId> <artifactId>wiremock-jre8</artifactId>
<version>${wiremock}</version> <version>${wiremock}</version>
<scope>test</scope> <scope>test</scope>
<exclusions> <exclusions>
@ -471,20 +461,8 @@
</profiles> </profiles>
<repositories> <repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories> </repositories>
</project> </project>

View File

@ -17,7 +17,7 @@ content:
- url: https://github.com/spring-projects/spring-data-commons - url: https://github.com/spring-projects/spring-data-commons
# Refname matching: # Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/ # https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: [ main, 3.4.x, 3.3.x ] branches: [ main, 3.2.x ]
start_path: src/main/antora start_path: src/main/antora
asciidoc: asciidoc:
attributes: attributes:

View File

@ -10,10 +10,6 @@
*** xref:migration-guides/migration-guide-5.0-5.1.adoc[] *** xref:migration-guides/migration-guide-5.0-5.1.adoc[]
*** xref:migration-guides/migration-guide-5.1-5.2.adoc[] *** xref:migration-guides/migration-guide-5.1-5.2.adoc[]
*** xref:migration-guides/migration-guide-5.2-5.3.adoc[] *** xref:migration-guides/migration-guide-5.2-5.3.adoc[]
*** xref:migration-guides/migration-guide-5.3-5.4.adoc[]
*** xref:migration-guides/migration-guide-5.4-5.5.adoc[]
*** xref:migration-guides/migration-guide-5.5-6.0.adoc[]
* xref:elasticsearch.adoc[] * xref:elasticsearch.adoc[]
** xref:elasticsearch/clients.adoc[] ** xref:elasticsearch/clients.adoc[]

View File

@ -10,7 +10,7 @@ In order for the auditing code to be able to decide whether an entity instance i
---- ----
package org.springframework.data.domain; package org.springframework.data.domain;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
public interface Persistable<ID> { public interface Persistable<ID> {
@Nullable @Nullable

View File

@ -6,10 +6,10 @@ This chapter illustrates configuration and usage of supported Elasticsearch clie
Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is connected to a single Elasticsearch node or a cluster. Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is connected to a single Elasticsearch node or a cluster.
Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of xref:elasticsearch/template.adoc[Elasticsearch Operations] and xref:elasticsearch/repositories/elasticsearch-repositories.adoc[Elasticsearch Repositories]. Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of xref:elasticsearch/template.adoc[Elasticsearch Operations] and xref:elasticsearch/repositories/elasticsearch-repositories.adoc[Elasticsearch Repositories].
[[elasticsearch.clients.rest5client]] [[elasticsearch.clients.restclient]]
== Imperative Rest5Client == Imperative Rest Client
To use the imperative (non-reactive) Rest5Client, a configuration bean must be configured like this: To use the imperative (non-reactive) client, a configuration bean must be configured like this:
==== ====
[source,java] [source,java]
@ -31,81 +31,7 @@ public class MyClientConfig extends ElasticsearchConfiguration {
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration] <.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
==== ====
The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods. The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[]] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
The following beans can then be injected in other Spring components:
====
[source,java]
----
import org.springframework.beans.factory.annotation.Autowired;@Autowired
ElasticsearchOperations operations; <.>
@Autowired
ElasticsearchClient elasticsearchClient; <.>
@Autowired
Rest5Client rest5Client; <.>
@Autowired
JsonpMapper jsonpMapper; <.>
----
<.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[]
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
<.> the low level `Rest5Client` from the Elasticsearch libraries
<.> the `JsonpMapper` user by the Elasticsearch `Transport`
====
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.restclient]]
== Deprecated Imperative RestClient
To use the imperative (non-reactive) RestClient - deprecated since version 6 - , the following dependency needs to be added, adapt the correct version. The exclusion is needed in a Spring Boot application:
====
[source,xml]
----
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>${elasticsearch-client.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
----
====
The configuration bean must be configured like this:
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ElasticsearchLegacyRestClientConfiguration;
@Configuration
public class MyClientConfig extends ElasticsearchLegacyRestClientConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() <.>
.connectedTo("localhost:9200")
.build();
}
}
----
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
====
The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
The following beans can then be injected in other Spring components: The following beans can then be injected in other Spring components:
@ -135,8 +61,8 @@ JsonpMapper jsonpMapper; <.>
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster. Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well. When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.reactiverest5client]] [[elasticsearch.clients.reactiverestclient]]
== Reactive Rest5Client == Reactive Rest Client
When working with the reactive stack, the configuration must be derived from a different class: When working with the reactive stack, the configuration must be derived from a different class:
@ -173,65 +99,6 @@ ReactiveElasticsearchOperations operations; <.>
@Autowired @Autowired
ReactiveElasticsearchClient elasticsearchClient; <.> ReactiveElasticsearchClient elasticsearchClient; <.>
@Autowired
Rest5Client rest5Client; <.>
@Autowired
JsonpMapper jsonpMapper; <.>
----
the following can be injected:
<.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[]
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
This is a reactive implementation based on the Elasticsearch client implementation.
<.> the low level `RestClient` from the Elasticsearch libraries
<.> the `JsonpMapper` user by the Elasticsearch `Transport`
====
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[] to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.reactiverestclient]]
== Deprecated Reactive RestClient
See the section above for the imperative code to use the deprecated RestClient for the necessary dependencies to include.
When working with the reactive stack, the configuration must be derived from a different class:
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchLegacyRestClientConfiguration;
@Configuration
public class MyClientConfig extends ReactiveElasticsearchLegacyRestClientConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() <.>
.connectedTo("localhost:9200")
.build();
}
}
----
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
====
The javadoc:org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
The following beans can then be injected in other Spring components:
====
[source,java]
----
@Autowired
ReactiveElasticsearchOperations operations; <.>
@Autowired
ReactiveElasticsearchClient elasticsearchClient; <.>
@Autowired @Autowired
RestClient restClient; <.> RestClient restClient; <.>
@ -316,25 +183,8 @@ In the case this is not enough, the user can add callback functions by using the
The following callbacks are provided: The following callbacks are provided:
[[elasticsearch.clients.configuration.callbacks.rest5]]
==== Configuration of the low level Elasticsearch `Rest5Client`:
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
`RestClient`:
====
[source,java]
----
ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291")
.withClientConfigurer(Rest5Clients.ElasticsearchRest5ClientConfigurationCallback.from(restClientBuilder -> {
// configure the Elasticsearch Rest5Client
return restClientBuilder;
}))
.build();
----
====
[[elasticsearch.clients.configuration.callbacks.rest]] [[elasticsearch.clients.configuration.callbacks.rest]]
==== Configuration of the deprecated low level Elasticsearch `RestClient`: ==== Configuration of the low level Elasticsearch `RestClient`:
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
`RestClient`: `RestClient`:
@ -343,7 +193,7 @@ This callback provides a `org.elasticsearch.client.RestClientBuilder` that can b
---- ----
ClientConfiguration.builder() ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291") .connectedTo("localhost:9200", "localhost:9291")
.withClientConfigurer(RestClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> { .withClientConfigurer(ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
// configure the Elasticsearch RestClient // configure the Elasticsearch RestClient
return restClientBuilder; return restClientBuilder;
})) }))
@ -351,29 +201,10 @@ ClientConfiguration.builder()
---- ----
==== ====
[[elasticsearch.clients.configurationcallbacks.httpasync5]]
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `Rest5Client`:
This callback provides a `org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder` to configure the HttpClient that is
used by the `Rest5Client`.
====
[source,java]
----
ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291")
.withClientConfigurer(Rest5Clients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
// configure the HttpAsyncClient
return httpAsyncClientBuilder;
}))
.build();
----
====
[[elasticsearch.clients.configurationcallbacks.httpasync]] [[elasticsearch.clients.configurationcallbacks.httpasync]]
==== Configuration of the HttpAsyncClient used by the deprecated low level Elasticsearch `RestClient`: ==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `RestClient`:
This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpClient that is This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpCLient that is
used by the `RestClient`. used by the `RestClient`.
==== ====
@ -381,7 +212,7 @@ used by the `RestClient`.
---- ----
ClientConfiguration.builder() ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291") .connectedTo("localhost:9200", "localhost:9291")
.withClientConfigurer(RestClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> { .withClientConfigurer(ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
// configure the HttpAsyncClient // configure the HttpAsyncClient
return httpAsyncClientBuilder; return httpAsyncClientBuilder;
})) }))

View File

@ -1,29 +1,10 @@
[[new-features]] [[new-features]]
= What's new = What's new
[[new-features.6-0-0]] [[new-features.5-3-1]]
== New in Spring Data Elasticsearch 6.0 == New in Spring Data Elasticsearch 5.3.1
* Upgarde to Spring 7 * Upgrade to Elasticsearch 8.13.4.
* Switch to jspecify nullability annotations
* Upgrade to Elasticsearch 9.0.3
* Use the new Elasticsearch Rest5Client as default
[[new-features.5-5-0]]
== New in Spring Data Elasticsearch 5.5
* Upgrade to Elasticsearch 8.18.1.
* Add support for the `@SearchTemplateQuery` annotation on repository methods.
* Scripted field properties of type collection can be populated from scripts returning arrays.
[[new-features.5-4-0]]
== New in Spring Data Elasticsearch 5.4
* Upgrade to Elasticsearch 8.15.3.
* Allow to customize the mapped type name for `@InnerField` and `@Field` annotations.
* Support for Elasticsearch SQL.
* Add support for retrieving request executionDuration.
[[new-features.5-3-0]] [[new-features.5-3-0]]
== New in Spring Data Elasticsearch 5.3 == New in Spring Data Elasticsearch 5.3

View File

@ -365,8 +365,6 @@ operations.putScript( <.>
To use a search template in a search query, Spring Data Elasticsearch provides the `SearchTemplateQuery`, an implementation of the `org.springframework.data.elasticsearch.core.query.Query` interface. To use a search template in a search query, Spring Data Elasticsearch provides the `SearchTemplateQuery`, an implementation of the `org.springframework.data.elasticsearch.core.query.Query` interface.
NOTE: Although `SearchTemplateQuery` is an implementation of the `Query` interface, not all of the functionality provided by the base class is available for a `SearchTemplateQuery` like setting a `Pageable` or a `Sort`. Values for this functionality must be added to the stored script like shown in the following example for paging parameters. If these values are set on the `Query` object, they will be ignored.
In the following code, we will add a call using a search template query to a custom repository implementation (see In the following code, we will add a call using a search template query to a custom repository implementation (see
xref:repositories/custom-implementations.adoc[]) as an example how this can be integrated into a repository call. xref:repositories/custom-implementations.adoc[]) as an example how this can be integrated into a repository call.
@ -451,3 +449,4 @@ var query = Query.findAll().addSort(Sort.by(order));
About the filter query: It is not possible to use a `CriteriaQuery` here, as this query would be converted into a Elasticsearch nested query which does not work in the filter context. So only `StringQuery` or `NativeQuery` can be used here. When using one of these, like the term query above, the Elasticsearch field names must be used, so take care, when these are redefined with the `@Field(name="...")` definition. About the filter query: It is not possible to use a `CriteriaQuery` here, as this query would be converted into a Elasticsearch nested query which does not work in the filter context. So only `StringQuery` or `NativeQuery` can be used here. When using one of these, like the term query above, the Elasticsearch field names must be used, so take care, when these are redefined with the `@Field(name="...")` definition.
For the definition of the order path and the nested paths, the Java entity property names should be used. For the definition of the order path and the nested paths, the Java entity property names should be used.

View File

@ -10,9 +10,7 @@ The Elasticsearch module supports all basic query building feature as string que
=== Declared queries === Declared queries
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. Deriving the query from the method name is not always sufficient and/or may result in unreadable method names.
In this case one might make use of the `@Query` annotation (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-query[Using the @Query Annotation] ). In this case one might make use of the `@Query` annotation (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-query[Using @Query Annotation] ).
Another possibility is the use of a search-template, (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-searchtemplate-query[Using the @SearchTemplateQuery Annotation] ).
[[elasticsearch.query-methods.criterions]] [[elasticsearch.query-methods.criterions]]
== Query creation == Query creation
@ -314,13 +312,11 @@ Repository methods can be defined to have the following return types for returni
* `SearchPage<T>` * `SearchPage<T>`
[[elasticsearch.query-methods.at-query]] [[elasticsearch.query-methods.at-query]]
== Using the @Query Annotation == Using @Query Annotation
.Declare query on the method using the `@Query` annotation. .Declare query on the method using the `@Query` annotation.
==== ====
The arguments passed to the method can be inserted into placeholders in the query string. The arguments passed to the method can be inserted into placeholders in the query string. The placeholders are of the form `?0`, `?1`, `?2` etc. for the first, second, third parameter and so on.
The placeholders are of the form `?0`, `?1`, `?2` etc. for the first, second, third parameter and so on.
[source,java] [source,java]
---- ----
interface BookRepository extends ElasticsearchRepository<Book, String> { interface BookRepository extends ElasticsearchRepository<Book, String> {
@ -345,20 +341,15 @@ It will be sent to Easticsearch as value of the query element; if for example th
} }
---- ----
==== ====
.`@Query` annotation on a method taking a Collection argument .`@Query` annotation on a method taking a Collection argument
==== ====
A repository method such as A repository method such as
[source,java] [source,java]
---- ----
@Query("{\"ids\": {\"values\": ?0 }}") @Query("{\"ids\": {\"values\": ?0 }}")
List<SampleEntity> getByIds(Collection<String> ids); List<SampleEntity> getByIds(Collection<String> ids);
---- ----
would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html[IDs query] to return all the matching documents. So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the query body
would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html[IDs query] to return all the matching documents.
So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the query body
[source,json] [source,json]
---- ----
{ {
@ -378,6 +369,7 @@ So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the
==== ====
https://docs.spring.io/spring-framework/reference/core/expressions.html[SpEL expression] is also supported when defining query in `@Query`. https://docs.spring.io/spring-framework/reference/core/expressions.html[SpEL expression] is also supported when defining query in `@Query`.
[source,java] [source,java]
---- ----
interface BookRepository extends ElasticsearchRepository<Book, String> { interface BookRepository extends ElasticsearchRepository<Book, String> {
@ -419,7 +411,6 @@ If for example the function is called with the parameter _John_, it would produc
.accessing parameter property. .accessing parameter property.
==== ====
Supposing that we have the following class as query parameter type: Supposing that we have the following class as query parameter type:
[source,java] [source,java]
---- ----
public record QueryParameter(String value) { public record QueryParameter(String value) {
@ -453,9 +444,7 @@ We can pass `new QueryParameter("John")` as the parameter now, and it will produ
.accessing bean property. .accessing bean property.
==== ====
https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/bean-references.html[Bean property] is also supported to access. https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/bean-references.html[Bean property] is also supported to access. Given that there is a bean named `queryParameter` of type `QueryParameter`, we can access the bean with symbol `@` rather than `#`, and there is no need to declare a parameter of type `QueryParameter` in the query method:
Given that there is a bean named `queryParameter` of type `QueryParameter`, we can access the bean with symbol `@` rather than `#`, and there is no need to declare a parameter of type `QueryParameter` in the query method:
[source,java] [source,java]
---- ----
interface BookRepository extends ElasticsearchRepository<Book, String> { interface BookRepository extends ElasticsearchRepository<Book, String> {
@ -504,7 +493,6 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
NOTE: collection values should not be quoted when declaring the elasticsearch json query. NOTE: collection values should not be quoted when declaring the elasticsearch json query.
A collection of `names` like `List.of("name1", "name2")` will produce the following terms query: A collection of `names` like `List.of("name1", "name2")` will produce the following terms query:
[source,json] [source,json]
---- ----
{ {
@ -544,7 +532,6 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
Page<Book> findByName(Collection<QueryParameter> parameters, Pageable pageable); Page<Book> findByName(Collection<QueryParameter> parameters, Pageable pageable);
} }
---- ----
This will extract all the `value` property values as a new `Collection` from `QueryParameter` collection, thus takes the same effect as above. This will extract all the `value` property values as a new `Collection` from `QueryParameter` collection, thus takes the same effect as above.
==== ====
@ -573,20 +560,3 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
---- ----
==== ====
[[elasticsearch.query-methods.at-searchtemplate-query]]
== Using the @SearchTemplateQuery Annotation
When using Elasticsearch search templates - (see xref:elasticsearch/misc.adoc#elasticsearch.misc.searchtemplates [Search Template support]) it is possible to specify that a repository method should use a template by adding the `@SearchTemplateQuery` annotation to that method.
Let's assume that there is a search template stored with the name "book-by-title" and this template need a parameter named "title", then a repository method using that search template can be defined like this:
[source,java]
----
interface BookRepository extends ElasticsearchRepository<Book, String> {
@SearchTemplateQuery(id = "book-by-title")
SearchHits<Book> findByTitle(String title);
}
----
The parameters of the repository method are sent to the seacrh template as key/value pairs where the key is the parameter name and the value is taken from the actual value when the method is invoked.

View File

@ -20,12 +20,12 @@ Whereas the birthdate is fix, the age depends on the time when a query is issued
==== ====
[source,java] [source,java]
---- ----
import org.jspecify.annotations.Nullable;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.ScriptedField; import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.lang.Nullable;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;

View File

@ -81,7 +81,7 @@ When a document is retrieved with the methods of the `DocumentOperations` inter
When searching with the methods of the `SearchOperations` interface, additional information is available for each entity, for example the _score_ or the _sortValues_ of the found entity. When searching with the methods of the `SearchOperations` interface, additional information is available for each entity, for example the _score_ or the _sortValues_ of the found entity.
In order to return this information, each entity is wrapped in a `SearchHit` object that contains this entity-specific additional information. In order to return this information, each entity is wrapped in a `SearchHit` object that contains this entity-specific additional information.
These `SearchHit` objects themselves are returned within a `SearchHits` object which additionally contains informations about the whole search like the _maxScore_ or requested aggregations or the execution duration it took to complete the request. These `SearchHit` objects themselves are returned within a `SearchHits` object which additionally contains informations about the whole search like the _maxScore_ or requested aggregations.
The following classes and interfaces are now available: The following classes and interfaces are now available:
.SearchHit<T> .SearchHit<T>

View File

@ -6,12 +6,9 @@ The following table shows the Elasticsearch and Spring versions that are used by
[cols="^,^,^,^",options="header"] [cols="^,^,^,^",options="header"]
|=== |===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework
| 2025.1 (in development) | 6.0.x | 9.0.3 | 7.0.x | 2024.0 | 5.3.3 | 8.13.4 | 6.1.x
| 2025.0 | 5.5.x | 8.18.1 | 6.2.x | 2023.1 (Vaughan) | 5.2.x | 8.11.1 | 6.1.x
| 2024.1 | 5.4.x | 8.15.5 | 6.1.x | 2023.0 (Ullmann) | 5.1.xfootnote:oom[Out of maintenance] | 8.7.1 | 6.0.x
| 2024.0 | 5.3.xfootnote:oom[Out of maintenance] | 8.13.4 | 6.1.x
| 2023.1 (Vaughan) | 5.2.xfootnote:oom[] | 8.11.1 | 6.1.x
| 2023.0 (Ullmann) | 5.1.xfootnote:oom[] | 8.7.1 | 6.0.x
| 2022.0 (Turing) | 5.0.xfootnote:oom[] | 8.5.3 | 6.0.x | 2022.0 (Turing) | 5.0.xfootnote:oom[] | 8.5.3 | 6.0.x
| 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x | 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x
| 2021.1 (Q) | 4.3.xfootnote:oom[] | 7.15.2 | 5.3.x | 2021.1 (Q) | 4.3.xfootnote:oom[] | 7.15.2 | 5.3.x

View File

@ -5,17 +5,14 @@ This section describes breaking changes from version 5.2.x to 5.3.x and how remo
[[elasticsearch-migration-guide-5.2-5.3.breaking-changes]] [[elasticsearch-migration-guide-5.2-5.3.breaking-changes]]
== Breaking Changes == Breaking Changes
During the parameter replacement in `@Query` annotated repository methods previous versions wrote the String _"null"_ into the query that was sent to Elasticsearch
During the parameter replacement in `@Query` annotated repository methods previous versions wrote the String `"null"` into the query that was sent to Elasticsearch when the actual parameter value was `null`. when the actual parameter value was `null`. As Elasticsearch does not store `null` values, this behaviour could lead to problems, for example whent the fields to be
As Elasticsearch does not store `null` values, this behaviour could lead to problems, for example whent the fields to be searched contains the string `"null"`. searched contains the string `"null"`. In Version 5.3 a `null` value in a parameter will cause a `ConversionException` to be thrown. If you are using `"null"` as the
In Version 5.3 a `null` value in a parameter will cause a `ConversionException` to be thrown.
If you are using `"null"` as the
`null_value` defined in a field mapping, then pass that string into the query instead of a Java `null`. `null_value` defined in a field mapping, then pass that string into the query instead of a Java `null`.
[[elasticsearch-migration-guide-5.2-5.3.deprecations]] [[elasticsearch-migration-guide-5.2-5.3.deprecations]]
== Deprecations == Deprecations
=== Removals === Removals
The deprecated classes `org.springframework.data.elasticsearch.ELCQueries` The deprecated classes `org.springframework.data.elasticsearch.ELCQueries`
and `org.springframework.data.elasticsearch.client.elc.QueryBuilders` have been removed, use `org.springframework.data.elasticsearch.client.elc.Queries` instead. and `org.springframework.data.elasticsearch.client.elc.QueryBuilders` have been removed, use `org.springframework.data.elasticsearch.client.elc.Queries` instead.

View File

@ -1,23 +0,0 @@
[[elasticsearch-migration-guide-5.3-5.4]]
= Upgrading from 5.3.x to 5.4.x
This section describes breaking changes from version 5.3.x to 5.4.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-5.3-5.4.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-5.3-5.4.breaking-changes.knn-search]]
=== knn search
The `withKnnQuery` method in `NativeQueryBuilder` has been replaced with `withKnnSearches` to build a `NativeQuery` with knn search.
`KnnQuery` and `KnnSearch` are two different classes in elasticsearch java client and are used for different queries, with different parameters supported:
- `KnnSearch`: is https://www.elastic.co/guide/en/elasticsearch/reference/8.13/search-search.html#search-api-knn[the top level `knn` query] in the elasticsearch request;
- `KnnQuery`: is https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html[the `knn` query inside `query` clause];
If `KnnQuery` is still preferable, please be sure to construct it inside `query` clause manually, by means of `withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query query)` clause in `NativeQueryBuilder`.
[[elasticsearch-migration-guide-5.3-5.4.deprecations]]
== Deprecations
=== Removals

View File

@ -1,30 +0,0 @@
[[elasticsearch-migration-guide-5.4-5.5]]
= Upgrading from 5.4.x to 5.5.x
This section describes breaking changes from version 5.4.x to 5.5.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-5.4-5.5.breaking-changes]]
== Breaking Changes
[[elasticsearch-migration-guide-5.4-5.5.deprecations]]
== Deprecations
Some classes that probably are not used by a library user have been renamed, the classes with the old names are still there, but are deprecated:
|===
|old name|new name
|ElasticsearchPartQuery|RepositoryPartQuery
|ElasticsearchStringQuery|RepositoryStringQuery
|ReactiveElasticsearchStringQuery|ReactiveRepositoryStringQuery
|===
=== Removals
The following methods that had been deprecated since release 5.3 have been removed:
```
DocumentOperations.delete(Query, Class<?>)
DocumentOperations.delete(Query, Class<?>, IndexCoordinates)
ReactiveDocumentOperations.delete(Query, Class<?>)
ReactiveDocumentOperations.delete(Query, Class<?>, IndexCoordinates)
```

View File

@ -1,25 +0,0 @@
[[elasticsearch-migration-guide-5.5-6.0]]
= Upgrading from 5.5.x to 6.0.x
This section describes breaking changes from version 5.5.x to 6.0.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-5.5-6.0.breaking-changes]]
== Breaking Changes
From version 6.0 on, Spring Data Elasticsearch uses the Elasticsearch 9 libraries and as default the new `Rest5Client` provided by these libraries. It is still possible to use the old `RestClient`, check xref:elasticsearch/clients.adoc[Elasticsearch clients] for information. The configuration callbacks for this `RestClient` have been moved from `org.springframework.data.elasticsearch.client.elc.ElasticsearchClients` to the `org.springframework.data.elasticsearch.client.elc.rest_client.RestClients` class.
[[elasticsearch-migration-guide-5.5-6.0.deprecations]]
== Deprecations
All the code using the old `RestClient` has been moved to the `org.springframework.data.elasticsearch.client.elc.rest_client` package and has been deprecated. Users should switch to the classes from the `org.springframework.data.elasticsearch.client.elc.rest5_client` package.
=== Removals
The `org.springframework.data.elasticsearch.core.query.ScriptType` enum has been removed. To distinguish between an inline and a stored script set the appropriate values in the `org.springframework.data.elasticsearch.core.query.ScriptData` record.
These methods have been removed because the Elasticsearch Client 9 does not support them anymore:
```
org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchIndicesClient.unfreeze(UnfreezeRequest)
org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchIndicesClient.unfreeze(Function<UnfreezeRequest.Builder, ObjectBuilder<UnfreezeRequest>>)
```

View File

@ -15,9 +15,9 @@
*/ */
package org.springframework.data.elasticsearch; package org.springframework.data.elasticsearch;
import java.util.List; import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable; import java.util.List;
/** /**
* Object describing an Elasticsearch error * Object describing an Elasticsearch error
@ -26,7 +26,8 @@ import org.jspecify.annotations.Nullable;
* @since 4.4 * @since 4.4
*/ */
public class ElasticsearchErrorCause { public class ElasticsearchErrorCause {
@Nullable private final String type; @Nullable
private final String type;
private final String reason; private final String reason;

View File

@ -15,8 +15,8 @@
*/ */
package org.springframework.data.elasticsearch; package org.springframework.data.elasticsearch;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.UncategorizedDataAccessException; import org.springframework.dao.UncategorizedDataAccessException;
import org.springframework.lang.Nullable;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch

View File

@ -1,79 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* Identifies an alias for the index.
*
* @author Youssef Aouichaoui
* @since 5.4
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Repeatable(Aliases.class)
public @interface Alias {
/**
* @return Index alias name. Alias for {@link #alias}.
*/
@AliasFor("alias")
String value() default "";
/**
* @return Index alias name. Alias for {@link #value}.
*/
@AliasFor("value")
String alias() default "";
/**
* @return Query used to limit documents the alias can access.
*/
Filter filter() default @Filter;
/**
* @return Used to route indexing operations to a specific shard.
*/
String indexRouting() default "";
/**
* @return Used to route indexing and search operations to a specific shard.
*/
String routing() default "";
/**
* @return Used to route search operations to a specific shard.
*/
String searchRouting() default "";
/**
* @return Is the alias hidden?
*/
boolean isHidden() default false;
/**
* @return Is it the 'write index' for the alias?
*/
boolean isWriteIndex() default false;
}

View File

@ -1,36 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Container annotation that aggregates several {@link Alias} annotations.
*
* @author Youssef Aouichaoui
* @see Alias
* @since 5.4
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Aliases {
Alias[] value();
}

View File

@ -100,13 +100,6 @@ public @interface Document {
*/ */
boolean storeVersionInSource() default true; boolean storeVersionInSource() default true;
/**
* Aliases for the index.
*
* @since 5.4
*/
Alias[] aliases() default {};
/** /**
* @since 4.3 * @since 4.3
*/ */

View File

@ -37,8 +37,6 @@ import org.springframework.core.annotation.AliasFor;
* @author Brian Kimmig * @author Brian Kimmig
* @author Morgan Lutz * @author Morgan Lutz
* @author Sascha Woo * @author Sascha Woo
* @author Haibo Liu
* @author Andriy Redko
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@ -130,10 +128,6 @@ public @interface Field {
boolean norms() default true; boolean norms() default true;
/** /**
* NOte that null_value setting are not supported in Elasticsearch for all types. For example setting a null_value on
* a field with type text will throw an exception in the server when the mapping is written to Elasticsearch. Alas,
* the Elasticsearch documentation does not specify on which types it is allowed on which it is not.
*
* @since 4.0 * @since 4.0
*/ */
String nullValue() default ""; String nullValue() default "";
@ -201,27 +195,6 @@ public @interface Field {
*/ */
int dims() default -1; int dims() default -1;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
String elementType() default FieldElementType.DEFAULT;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
KnnSimilarity knnSimilarity() default KnnSimilarity.DEFAULT;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
KnnIndexOptions[] knnIndexOptions() default {};
/** /**
* Controls how Elasticsearch dynamically adds fields to the inner object within the document.<br> * Controls how Elasticsearch dynamically adds fields to the inner object within the document.<br>
* To be used in combination with {@link FieldType#Object} or {@link FieldType#Nested} * To be used in combination with {@link FieldType#Object} or {@link FieldType#Nested}
@ -245,11 +218,4 @@ public @interface Field {
* @since 5.1 * @since 5.1
*/ */
boolean storeEmptyValue() default true; boolean storeEmptyValue() default true;
/**
* overrides the field type in the mapping which otherwise will be taken from corresponding {@link FieldType}
*
* @since 5.4
*/
String mappedTypeName() default "";
} }

View File

@ -1,26 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
/**
* @author Haibo Liu
* @since 5.4
*/
public final class FieldElementType {
public final static String DEFAULT = "";
public final static String FLOAT = "float";
public final static String BYTE = "byte";
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import org.springframework.core.annotation.AliasFor;
/**
* Query used to limit documents.
*
* @author Youssef Aouichaoui
* @since 5.4
*/
public @interface Filter {
/**
* @return Query used to limit documents. Alias for {@link #query}.
*/
@AliasFor("query")
String value() default "";
/**
* @return Query used to limit documents. Alias for {@link #value}.
*/
@AliasFor("value")
String query() default "";
}

View File

@ -29,8 +29,6 @@ import java.lang.annotation.Target;
* @author Aleksei Arsenev * @author Aleksei Arsenev
* @author Brian Kimmig * @author Brian Kimmig
* @author Morgan Lutz * @author Morgan Lutz
* @author Haibo Liu
* @author Andriy Redko
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE) @Target(ElementType.ANNOTATION_TYPE)
@ -151,32 +149,4 @@ public @interface InnerField {
* @since 4.2 * @since 4.2
*/ */
int dims() default -1; int dims() default -1;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
String elementType() default FieldElementType.DEFAULT;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
KnnSimilarity knnSimilarity() default KnnSimilarity.DEFAULT;
/**
* to be used in combination with {@link FieldType#Dense_Vector}
*
* @since 5.4
*/
KnnIndexOptions[] knnIndexOptions() default {};
/**
* overrides the field type in the mapping which otherwise will be taken from corresponding {@link FieldType}
*
* @since 5.4
*/
String mappedTypeName() default "";
} }

View File

@ -1,38 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
/**
* @author Haibo Liu
* @since 5.4
*/
public enum KnnAlgorithmType {
HNSW("hnsw"),
INT8_HNSW("int8_hnsw"),
FLAT("flat"),
INT8_FLAT("int8_flat"),
DEFAULT("");
private final String type;
KnnAlgorithmType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
/**
* @author Haibo Liu
* @since 5.4
*/
public @interface KnnIndexOptions {
KnnAlgorithmType type() default KnnAlgorithmType.DEFAULT;
/**
* Only applicable to {@link KnnAlgorithmType#HNSW} and {@link KnnAlgorithmType#INT8_HNSW} index types.
*/
int m() default -1;
/**
* Only applicable to {@link KnnAlgorithmType#HNSW} and {@link KnnAlgorithmType#INT8_HNSW} index types.
*/
int efConstruction() default -1;
/**
* Only applicable to {@link KnnAlgorithmType#INT8_HNSW} and {@link KnnAlgorithmType#INT8_FLAT} index types.
*/
float confidenceInterval() default -1F;
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
/**
* @author Haibo Liu
* @since 5.4
*/
public enum KnnSimilarity {
L2_NORM("l2_norm"),
DOT_PRODUCT("dot_product"),
COSINE("cosine"),
MAX_INNER_PRODUCT("max_inner_product"),
DEFAULT("");
private final String similarity;
KnnSimilarity(String similarity) {
this.similarity = similarity;
}
public String getSimilarity() {
return similarity;
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import org.springframework.data.annotation.QueryAnnotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark a repository method as a search template method. The annotation defines the search template id,
* the parameters for the search template are taken from the method's arguments.
*
* @author P.J. Meisch (pj.meisch@sothawo.com)
* @since 5.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
@QueryAnnotation
public @interface SearchTemplateQuery {
/**
* The id of the search template. Must not be empt or null.
*/
String id();
}

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.annotations; package org.springframework.data.elasticsearch.annotations;

View File

@ -19,7 +19,6 @@ import static org.springframework.data.elasticsearch.aot.ElasticsearchAotPredica
import java.util.Arrays; import java.util.Arrays;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.RuntimeHintsRegistrar;
@ -33,6 +32,7 @@ import org.springframework.data.elasticsearch.core.event.ReactiveAfterConvertCal
import org.springframework.data.elasticsearch.core.event.ReactiveAfterLoadCallback; import org.springframework.data.elasticsearch.core.event.ReactiveAfterLoadCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback; import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback; import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
import org.springframework.lang.Nullable;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.aot; package org.springframework.data.elasticsearch.aot;

View File

@ -25,8 +25,8 @@ import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.support.HttpHeaders; import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
/** /**
* Configuration interface exposing common client configuration properties for Elasticsearch clients. * Configuration interface exposing common client configuration properties for Elasticsearch clients.
@ -127,16 +127,10 @@ public interface ClientConfiguration {
Optional<String> getCaFingerprint(); Optional<String> getCaFingerprint();
/** /**
* Returns the {@link HostnameVerifier} to use. Must be {@link Optional#empty()} if not configured. * Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
* Cannot be used with the Rest5Client used from Elasticsearch 9 on as the underlying Apache http components 5 does not offer a way
* to set this. Users that need a hostname verifier must integrate this in a SSLContext.
* Returning a value here is ignored in this case
* *
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured. * @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
* @deprecated since 6.0
*/ */
// todo #3117 document this
@Deprecated(since = "6.0", forRemoval=true)
Optional<HostnameVerifier> getHostNameVerifier(); Optional<HostnameVerifier> getHostNameVerifier();
/** /**

View File

@ -25,11 +25,11 @@ import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint; import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint;
import org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder; import org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder; import org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder;
import org.springframework.data.elasticsearch.support.HttpHeaders; import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -24,8 +24,9 @@ import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.jspecify.annotations.Nullable; import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.support.HttpHeaders; import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
/** /**
* Default {@link ClientConfiguration} implementation. * Default {@link ClientConfiguration} implementation.

View File

@ -17,10 +17,10 @@ package org.springframework.data.elasticsearch.client.elc;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery; import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery; import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.lang.Nullable;
/** /**
* An abstract class that serves as a base for query processors. It provides a common interface and basic functionality * An abstract class that serves as a base for query processors. It provides a common interface and basic functionality
@ -38,8 +38,8 @@ public abstract class AbstractQueryProcessor {
* @param queryConverter correct mapped field names and the values to the converted values. * @param queryConverter correct mapped field names and the values to the converted values.
* @return an Elasticsearch {@literal query}. * @return an Elasticsearch {@literal query}.
*/ */
@Nullable
static co.elastic.clients.elasticsearch._types.query_dsl.@Nullable Query getEsQuery(@Nullable Query query, static co.elastic.clients.elasticsearch._types.query_dsl.Query getEsQuery(@Nullable Query query,
@Nullable Consumer<Query> queryConverter) { @Nullable Consumer<Query> queryConverter) {
if (query == null) { if (query == null) {
return null; return null;

View File

@ -18,8 +18,6 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import java.io.IOException;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -38,10 +36,7 @@ public class AutoCloseableElasticsearchClient extends ElasticsearchClient implem
} }
@Override @Override
public void close() throws IOException { public void close() throws Exception {
// since Elasticsearch 8.16 the ElasticsearchClient implements (through ApiClient) the Closeable interface and transport.close();
// handles closing of the underlying transport. We now just call the base class, but keep this as we
// have been implementing AutoCloseable since 4.4 and won't change that to a mere Closeable
super.close();
} }
} }

View File

@ -31,13 +31,13 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.query.Criteria; import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.Field; import org.springframework.data.elasticsearch.core.query.Field;
import org.springframework.data.elasticsearch.core.query.HasChildQuery; import org.springframework.data.elasticsearch.core.query.HasChildQuery;
import org.springframework.data.elasticsearch.core.query.HasParentQuery; import org.springframework.data.elasticsearch.core.query.HasParentQuery;
import org.springframework.data.elasticsearch.core.query.InnerHitsQuery; import org.springframework.data.elasticsearch.core.query.InnerHitsQuery;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -249,54 +249,49 @@ class CriteriaQueryProcessor extends AbstractQueryProcessor {
queryBuilder.queryString(queryStringQuery(fieldName, Objects.requireNonNull(value).toString(), boost)); queryBuilder.queryString(queryStringQuery(fieldName, Objects.requireNonNull(value).toString(), boost));
break; break;
case LESS: case LESS:
queryBuilder queryBuilder //
.range(rb -> rb .range(rb -> rb //
.untyped(ut -> ut .field(fieldName) //
.field(fieldName) .lt(JsonData.of(value)) //
.lt(JsonData.of(value)) .boost(boost)); //
.boost(boost)));
break; break;
case LESS_EQUAL: case LESS_EQUAL:
queryBuilder queryBuilder //
.range(rb -> rb .range(rb -> rb //
.untyped(ut -> ut .field(fieldName) //
.field(fieldName) .lte(JsonData.of(value)) //
.lte(JsonData.of(value)) .boost(boost)); //
.boost(boost)));
break; break;
case GREATER: case GREATER:
queryBuilder queryBuilder //
.range(rb -> rb .range(rb -> rb //
.untyped(ut -> ut .field(fieldName) //
.field(fieldName) .gt(JsonData.of(value)) //
.gt(JsonData.of(value)) .boost(boost)); //
.boost(boost)));
break; break;
case GREATER_EQUAL: case GREATER_EQUAL:
queryBuilder queryBuilder //
.range(rb -> rb .range(rb -> rb //
.untyped(ut -> ut .field(fieldName) //
.field(fieldName) .gte(JsonData.of(value)) //
.gte(JsonData.of(value)) .boost(boost)); //
.boost(boost)));
break; break;
case BETWEEN: case BETWEEN:
Object[] ranges = (Object[]) value; Object[] ranges = (Object[]) value;
Assert.notNull(value, "value for a between condition must not be null"); Assert.notNull(value, "value for a between condition must not be null");
queryBuilder queryBuilder //
.range(rb -> rb .range(rb -> {
.untyped(ut -> { rb.field(fieldName);
ut.field(fieldName);
if (ranges[0] != null) { if (ranges[0] != null) {
ut.gte(JsonData.of(ranges[0])); rb.gte(JsonData.of(ranges[0]));
} }
if (ranges[1] != null) { if (ranges[1] != null) {
ut.lte(JsonData.of(ranges[1])); rb.lte(JsonData.of(ranges[1]));
} }
ut.boost(boost); // rb.boost(boost); //
return ut; return rb;
})); }); //
break; break;
case FUZZY: case FUZZY:

View File

@ -24,17 +24,6 @@ import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.NestedIdentity; import co.elastic.clients.elasticsearch.core.search.NestedIdentity;
import co.elastic.clients.json.JsonData; import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentAdapter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.util.Assert;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -43,21 +32,31 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentAdapter;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/** /**
* Utility class to adapt different Elasticsearch responses to a * Utility class to adapt different Elasticsearch responses to a
* {@link org.springframework.data.elasticsearch.core.document.Document} * {@link org.springframework.data.elasticsearch.core.document.Document}
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Haibo Liu * @author Haibo Liu
* @author Mohamed El Harrougui
* @since 4.4 * @since 4.4
*/ */
final class DocumentAdapters { final class DocumentAdapters {
private static final Log LOGGER = LogFactory.getLog(DocumentAdapters.class); private static final Log LOGGER = LogFactory.getLog(DocumentAdapters.class);
private DocumentAdapters() { private DocumentAdapters() {}
}
/** /**
* Creates a {@link SearchDocument} from a {@link Hit} returned by the Elasticsearch client. * Creates a {@link SearchDocument} from a {@link Hit} returned by the Elasticsearch client.
@ -75,7 +74,7 @@ final class DocumentAdapters {
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>(); Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
hit.innerHits().forEach((name, innerHitsResult) -> { hit.innerHits().forEach((name, innerHitsResult) -> {
// noinspection ReturnOfNull // noinspection ReturnOfNull
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, 0, null, null, innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, null, null,
searchDocument -> null, jsonpMapper)); searchDocument -> null, jsonpMapper));
}); });
@ -83,11 +82,11 @@ final class DocumentAdapters {
Explanation explanation = from(hit.explanation()); Explanation explanation = from(hit.explanation());
Map<String, Double> matchedQueries = hit.matchedQueries(); List<String> matchedQueries = hit.matchedQueries();
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> { Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{"); StringBuilder sb = new StringBuilder("{");
final boolean[] firstField = {true}; final boolean[] firstField = { true };
hit.fields().forEach((key, jsonData) -> { hit.fields().forEach((key, jsonData) -> {
if (!firstField[0]) { if (!firstField[0]) {
sb.append(','); sb.append(',');
@ -155,12 +154,12 @@ final class DocumentAdapters {
} }
float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN; float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[]{}, Collections.emptyMap(), Collections.emptyMap(), return new SearchDocumentAdapter(document, score, new Object[] {}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing()); Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
} }
@Nullable @Nullable
private static Explanation from(co.elastic.clients.elasticsearch.core.explain.@Nullable Explanation explanation) { private static Explanation from(@Nullable co.elastic.clients.elasticsearch.core.explain.Explanation explanation) {
if (explanation == null) { if (explanation == null) {
return null; return null;

View File

@ -22,8 +22,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.AggregationsContainer; import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -19,12 +19,12 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException; import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -15,38 +15,44 @@
*/ */
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients.*;
import static org.springframework.data.elasticsearch.client.elc.rest_client.RestClients.*;
import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.TransportUtils;
import co.elastic.clients.transport.Version; import co.elastic.clients.transport.Version;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import co.elastic.clients.transport.rest_client.RestClientOptions; import co.elastic.clients.transport.rest_client.RestClientOptions;
import co.elastic.clients.transport.rest_client.RestClientTransport; import co.elastic.clients.transport.rest_client.RestClientTransport;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.jspecify.annotations.Nullable; import org.elasticsearch.client.RestClientBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.support.VersionInfo; import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Utility class to create the different Elasticsearch clients. The RestClient class is the one used in Elasticsearch * Utility class to create the different Elasticsearch clients
* until version 9, it is still available, but it's use is deprecated. The Rest5Client class is the one that should be
* used from Elasticsearch 9 on.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @since 4.4 * @since 4.4
@ -113,32 +119,18 @@ public final class ElasticsearchClients {
* *
* @param restClient the underlying {@link RestClient} * @param restClient the underlying {@link RestClient}
* @return the {@link ReactiveElasticsearchClient} * @return the {@link ReactiveElasticsearchClient}
* @deprecated since 6.0, use the version with a Rest5Client.
*/ */
@Deprecated(since = "6.0", forRemoval = true)
public static ReactiveElasticsearchClient createReactive(RestClient restClient) { public static ReactiveElasticsearchClient createReactive(RestClient restClient) {
return createReactive(restClient, null, DEFAULT_JSONP_MAPPER); return createReactive(restClient, null, DEFAULT_JSONP_MAPPER);
} }
/**
* Creates a new {@link ReactiveElasticsearchClient}.
*
* @param rest5Client the underlying {@link RestClient}
* @return the {@link ReactiveElasticsearchClient}
*/
public static ReactiveElasticsearchClient createReactive(Rest5Client rest5Client) {
return createReactive(rest5Client, null, DEFAULT_JSONP_MAPPER);
}
/** /**
* Creates a new {@link ReactiveElasticsearchClient}. * Creates a new {@link ReactiveElasticsearchClient}.
* *
* @param restClient the underlying {@link RestClient} * @param restClient the underlying {@link RestClient}
* @param transportOptions options to be added to each request. * @param transportOptions options to be added to each request.
* @return the {@link ReactiveElasticsearchClient} * @return the {@link ReactiveElasticsearchClient}
* @deprecated since 6.0, use the version with a Rest5Client.
*/ */
@Deprecated(since = "6.0", forRemoval = true)
public static ReactiveElasticsearchClient createReactive(RestClient restClient, public static ReactiveElasticsearchClient createReactive(RestClient restClient,
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) { @Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
@ -147,21 +139,6 @@ public final class ElasticsearchClients {
var transport = getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions, jsonpMapper); var transport = getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions, jsonpMapper);
return createReactive(transport); return createReactive(transport);
} }
/**
* Creates a new {@link ReactiveElasticsearchClient}.
*
* @param rest5Client the underlying {@link RestClient}
* @param transportOptions options to be added to each request.
* @return the {@link ReactiveElasticsearchClient}
*/
public static ReactiveElasticsearchClient createReactive(Rest5Client rest5Client,
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
Assert.notNull(rest5Client, "restClient must not be null");
var transport = getElasticsearchTransport(rest5Client, REACTIVE_CLIENT, transportOptions, jsonpMapper);
return createReactive(transport);
}
/** /**
* Creates a new {@link ReactiveElasticsearchClient} that uses the given {@link ElasticsearchTransport}. * Creates a new {@link ReactiveElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
@ -179,21 +156,17 @@ public final class ElasticsearchClients {
// region imperative client // region imperative client
/** /**
* Creates a new imperative {@link ElasticsearchClient}. This uses a RestClient, if the old RestClient is needed, this * Creates a new imperative {@link ElasticsearchClient}
* must be created with the {@link org.springframework.data.elasticsearch.client.elc.rest_client.RestClients} class
* and passed in as parameter.
* *
* @param clientConfiguration configuration options, must not be {@literal null}. * @param clientConfiguration configuration options, must not be {@literal null}.
* @return the {@link ElasticsearchClient} * @return the {@link ElasticsearchClient}
*/ */
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration) { public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration) {
return createImperative(getRest5Client(clientConfiguration), null, DEFAULT_JSONP_MAPPER); return createImperative(getRestClient(clientConfiguration), null, DEFAULT_JSONP_MAPPER);
} }
/** /**
* Creates a new imperative {@link ElasticsearchClient}. This uses a RestClient, if the old RestClient is needed, this * Creates a new imperative {@link ElasticsearchClient}
* must be created with the {@link org.springframework.data.elasticsearch.client.elc.rest_client.RestClients} class
* and passed in as parameter.
* *
* @param clientConfiguration configuration options, must not be {@literal null}. * @param clientConfiguration configuration options, must not be {@literal null}.
* @param transportOptions options to be added to each request. * @param transportOptions options to be added to each request.
@ -201,7 +174,7 @@ public final class ElasticsearchClients {
*/ */
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration, public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration,
TransportOptions transportOptions) { TransportOptions transportOptions) {
return createImperative(getRest5Client(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER); return createImperative(getRestClient(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER);
} }
/** /**
@ -209,23 +182,11 @@ public final class ElasticsearchClients {
* *
* @param restClient the RestClient to use * @param restClient the RestClient to use
* @return the {@link ElasticsearchClient} * @return the {@link ElasticsearchClient}
* @deprecated since 6.0, use the version with a Rest5Client.
*/ */
@Deprecated(since = "6.0", forRemoval = true)
public static ElasticsearchClient createImperative(RestClient restClient) { public static ElasticsearchClient createImperative(RestClient restClient) {
return createImperative(restClient, null, DEFAULT_JSONP_MAPPER); return createImperative(restClient, null, DEFAULT_JSONP_MAPPER);
} }
/**
* Creates a new imperative {@link ElasticsearchClient}
*
* @param rest5Client the Rest5Client to use
* @return the {@link ElasticsearchClient}
*/
public static ElasticsearchClient createImperative(Rest5Client rest5Client) {
return createImperative(rest5Client, null, DEFAULT_JSONP_MAPPER);
}
/** /**
* Creates a new imperative {@link ElasticsearchClient} * Creates a new imperative {@link ElasticsearchClient}
* *
@ -233,9 +194,7 @@ public final class ElasticsearchClients {
* @param transportOptions options to be added to each request. * @param transportOptions options to be added to each request.
* @param jsonpMapper the mapper for the transport to use * @param jsonpMapper the mapper for the transport to use
* @return the {@link ElasticsearchClient} * @return the {@link ElasticsearchClient}
* @deprecated since 6.0, use the version with a Rest5Client.
*/ */
@Deprecated(since = "6.0", forRemoval = true)
public static ElasticsearchClient createImperative(RestClient restClient, @Nullable TransportOptions transportOptions, public static ElasticsearchClient createImperative(RestClient restClient, @Nullable TransportOptions transportOptions,
JsonpMapper jsonpMapper) { JsonpMapper jsonpMapper) {
@ -247,27 +206,6 @@ public final class ElasticsearchClients {
return createImperative(transport); return createImperative(transport);
} }
/**
* Creates a new imperative {@link ElasticsearchClient}
*
* @param rest5Client the Rest5Client to use
* @param transportOptions options to be added to each request.
* @param jsonpMapper the mapper for the transport to use
* @return the {@link ElasticsearchClient}
* @since 6.0
*/
public static ElasticsearchClient createImperative(Rest5Client rest5Client,
@Nullable TransportOptions transportOptions,
JsonpMapper jsonpMapper) {
Assert.notNull(rest5Client, "restClient must not be null");
ElasticsearchTransport transport = getElasticsearchTransport(rest5Client, IMPERATIVE_CLIENT, transportOptions,
jsonpMapper);
return createImperative(transport);
}
/** /**
* Creates a new {@link ElasticsearchClient} that uses the given {@link ElasticsearchTransport}. * Creates a new {@link ElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
* *
@ -282,6 +220,96 @@ public final class ElasticsearchClients {
} }
// endregion // endregion
// region low level RestClient
private static RestClientOptions.Builder getRestClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
if (transportOptions instanceof RestClientOptions restClientOptions) {
return restClientOptions.toBuilder();
}
var builder = new RestClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
if (transportOptions != null) {
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
transportOptions.queryParameters().forEach(builder::setParameter);
builder.onWarnings(transportOptions.onWarnings());
}
return builder;
}
/**
* Creates a low level {@link RestClient} for the given configuration.
*
* @param clientConfiguration must not be {@literal null}
* @return the {@link RestClient}
*/
public static RestClient getRestClient(ClientConfiguration clientConfiguration) {
return getRestClientBuilder(clientConfiguration).build();
}
private static RestClientBuilder getRestClientBuilder(ClientConfiguration clientConfiguration) {
HttpHost[] httpHosts = formattedHosts(clientConfiguration.getEndpoints(), clientConfiguration.useSsl()).stream()
.map(HttpHost::create).toArray(HttpHost[]::new);
RestClientBuilder builder = RestClient.builder(httpHosts);
if (clientConfiguration.getPathPrefix() != null) {
builder.setPathPrefix(clientConfiguration.getPathPrefix());
}
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
if (!headers.isEmpty()) {
builder.setDefaultHeaders(toHeaderArray(headers));
}
builder.setHttpClientConfigCallback(clientBuilder -> {
if (clientConfiguration.getCaFingerprint().isPresent()) {
clientBuilder
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
}
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
Duration connectTimeout = clientConfiguration.getConnectTimeout();
if (!connectTimeout.isNegative()) {
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
}
Duration socketTimeout = clientConfiguration.getSocketTimeout();
if (!socketTimeout.isNegative()) {
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
}
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof ElasticsearchHttpClientConfigurationCallback restClientConfigurationCallback) {
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
}
}
return clientBuilder;
});
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurationCallback instanceof ElasticsearchRestClientConfigurationCallback configurationCallback) {
builder = configurationCallback.configure(builder);
}
}
return builder;
}
// endregion
// region Elasticsearch transport // region Elasticsearch transport
/** /**
* Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a * Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a
@ -292,9 +320,7 @@ public final class ElasticsearchClients {
* @param transportOptions options for the transport * @param transportOptions options for the transport
* @param jsonpMapper mapper for the transport * @param jsonpMapper mapper for the transport
* @return ElasticsearchTransport * @return ElasticsearchTransport
* @deprecated since 6.0, use the version taking a Rest5Client
*/ */
@Deprecated(since = "6.0", forRemoval = true)
public static ElasticsearchTransport getElasticsearchTransport(RestClient restClient, String clientType, public static ElasticsearchTransport getElasticsearchTransport(RestClient restClient, String clientType,
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) { @Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
@ -303,7 +329,7 @@ public final class ElasticsearchClients {
Assert.notNull(jsonpMapper, "jsonpMapper must not be null"); Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder() TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
: new RestClientOptions(org.elasticsearch.client.RequestOptions.DEFAULT, false).toBuilder(); : new RestClientOptions(RequestOptions.DEFAULT).toBuilder();
RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions); RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions);
@ -327,35 +353,70 @@ public final class ElasticsearchClients {
return new RestClientTransport(restClient, jsonpMapper, restClientOptionsBuilder.build()); return new RestClientTransport(restClient, jsonpMapper, restClientOptionsBuilder.build());
} }
/**
* Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a
* header to contain the clientType
*
* @param rest5Client the client to use
* @param clientType the client type to pass in each request as header
* @param transportOptions options for the transport
* @param jsonpMapper mapper for the transport
* @return ElasticsearchTransport
*/
public static ElasticsearchTransport getElasticsearchTransport(Rest5Client rest5Client, String clientType,
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
Assert.notNull(rest5Client, "restClient must not be null");
Assert.notNull(clientType, "clientType must not be null");
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
: new Rest5ClientOptions(RequestOptions.DEFAULT, false).toBuilder();
Rest5ClientOptions.Builder rest5ClientOptionsBuilder = getRest5ClientOptionsBuilder(transportOptions);
rest5ClientOptionsBuilder.addHeader(X_SPRING_DATA_ELASTICSEARCH_CLIENT,
VersionInfo.clientVersions() + " / " + clientType);
return new Rest5ClientTransport(rest5Client, jsonpMapper, rest5ClientOptionsBuilder.build());
}
// endregion // endregion
// todo #3117 remove and document that ElasticsearchHttpClientConfigurationCallback has been move to RestClients. private static List<String> formattedHosts(List<InetSocketAddress> hosts, boolean useSsl) {
return hosts.stream().map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
.collect(Collectors.toList());
}
private static org.apache.http.Header[] toHeaderArray(HttpHeaders headers) {
return headers.entrySet().stream() //
.flatMap(entry -> entry.getValue().stream() //
.map(value -> new BasicHeader(entry.getKey(), value))) //
.toArray(org.apache.http.Header[]::new);
}
/**
* Interceptor to inject custom supplied headers.
*
* @since 4.4
*/
private record CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) implements HttpRequestInterceptor {
@Override
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
*
* @since 4.4
*/
public interface ElasticsearchHttpClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static ElasticsearchHttpClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
return httpClientBuilderCallback::apply;
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the RestClient client with a {@link RestClientBuilder}
*
* @since 5.0
*/
public interface ElasticsearchRestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
static ElasticsearchRestClientConfigurationCallback from(
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
return restClientBuilderCallback::apply;
}
}
} }

View File

@ -20,13 +20,12 @@ import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions; import co.elastic.clients.transport.rest_client.RestClientOptions;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@ -39,9 +38,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
/** /**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch * Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving * connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
* classes must provide the {@link ClientConfiguration} to use. From Version 6.0 on, this class uses the new Rest5Client * classes must provide the {@link ClientConfiguration} to use.
* from Elasticsearch 9. The old implementation using the RestClient is still available under the name
* {@link ElasticsearchLegacyRestClientConfiguration}.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @since 4.4 * @since 4.4
@ -63,27 +60,27 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
* @return RestClient * @return RestClient
*/ */
@Bean @Bean
public Rest5Client elasticsearchRest5Client(ClientConfiguration clientConfiguration) { public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null"); Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
return Rest5Clients.getRest5Client(clientConfiguration); return ElasticsearchClients.getRestClient(clientConfiguration);
} }
/** /**
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link Rest5Client} bean and * Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
* the {@link JsonpMapper} bean provided in this class. * the {@link JsonpMapper} bean provided in this class.
* *
* @return the {@link ElasticsearchTransport} * @return the {@link ElasticsearchTransport}
* @since 5.2 * @since 5.2
*/ */
@Bean @Bean
public ElasticsearchTransport elasticsearchTransport(Rest5Client rest5Client, JsonpMapper jsonpMapper) { public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
Assert.notNull(rest5Client, "restClient must not be null"); Assert.notNull(restClient, "restClient must not be null");
Assert.notNull(jsonpMapper, "jsonpMapper must not be null"); Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
return ElasticsearchClients.getElasticsearchTransport(rest5Client, ElasticsearchClients.IMPERATIVE_CLIENT, return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.IMPERATIVE_CLIENT,
transportOptions(), jsonpMapper); transportOptions(), jsonpMapper);
} }
@ -118,7 +115,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
} }
/** /**
* Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(Rest5Client, JsonpMapper)} method. * Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method.
* *
* @return the {@link JsonpMapper} to use * @return the {@link JsonpMapper} to use
* @since 5.2 * @since 5.2
@ -138,6 +135,6 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
* @return the options that should be added to every request. Must not be {@literal null} * @return the options that should be added to every request. Must not be {@literal null}
*/ */
public TransportOptions transportOptions() { public TransportOptions transportOptions() {
return new Rest5ClientOptions(RequestOptions.DEFAULT, false); return new RestClientOptions(RequestOptions.DEFAULT);
} }
} }

View File

@ -119,19 +119,14 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
String message = null; String message = null;
if (exception instanceof ResponseException responseException) { if (exception instanceof ResponseException responseException) {
// this code is for the old RestClient
status = responseException.getResponse().getStatusLine().getStatusCode(); status = responseException.getResponse().getStatusLine().getStatusCode();
message = responseException.getMessage(); message = responseException.getMessage();
} else if (exception instanceof ElasticsearchException elasticsearchException) {
// using the RestClient throws this
status = elasticsearchException.status();
message = elasticsearchException.getMessage();
} else if (exception.getCause() != null) { } else if (exception.getCause() != null) {
checkForConflictException(exception.getCause()); checkForConflictException(exception.getCause());
} }
if (status != null && message != null) { if (status != null && message != null) {
if (status == 409 && message.contains("version_conflict_engine_exception")) if (status == 409 && message.contains("type\":\"version_conflict_engine_exception"))
if (message.contains("version conflict, required seqNo")) { if (message.contains("version conflict, required seqNo")) {
throw new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", throw new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict",
exception); exception);

View File

@ -1,144 +0,0 @@
/*
* Copyright 2021-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest_client.RestClientOptions;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
* classes must provide the {@link ClientConfiguration} to use. <br/>
* This class uses the Elasticsearch RestClient which was replaced by the Rest5Client in Elasticsearch 9. It is still
* available here but deprecated.
*
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 6.0, use {@link ElasticsearchConfiguration}
*/
@Deprecated(since = "6.0", forRemoval=true)
public abstract class ElasticsearchLegacyRestClientConfiguration extends ElasticsearchConfigurationSupport {
/**
* Must be implemented by deriving classes to provide the {@link ClientConfiguration}.
*
* @return configuration, must not be {@literal null}
*/
@Bean(name = "elasticsearchClientConfiguration")
public abstract ClientConfiguration clientConfiguration();
/**
* Provides the underlying low level Elasticsearch RestClient.
*
* @param clientConfiguration configuration for the client, must not be {@literal null}
* @return RestClient
*/
@Bean
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
return RestClients.getRestClient(clientConfiguration);
}
/**
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
* the {@link JsonpMapper} bean provided in this class.
*
* @return the {@link ElasticsearchTransport}
* @since 5.2
*/
@Bean
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
Assert.notNull(restClient, "restClient must not be null");
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.IMPERATIVE_CLIENT,
transportOptions(), jsonpMapper);
}
/**
* Provides the {@link ElasticsearchClient} to be used.
*
* @param transport the {@link ElasticsearchTransport} to use
* @return ElasticsearchClient instance
*/
@Bean
public ElasticsearchClient elasticsearchClient(ElasticsearchTransport transport) {
Assert.notNull(transport, "transport must not be null");
return ElasticsearchClients.createImperative(transport);
}
/**
* Creates a {@link ElasticsearchOperations} implementation using an {@link ElasticsearchClient}.
*
* @return never {@literal null}.
*/
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
ElasticsearchClient elasticsearchClient) {
ElasticsearchTemplate template = new ElasticsearchTemplate(elasticsearchClient, elasticsearchConverter);
template.setRefreshPolicy(refreshPolicy());
return template;
}
/**
* Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method.
*
* @return the {@link JsonpMapper} to use
* @since 5.2
*/
@Bean
public JsonpMapper jsonpMapper() {
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
}
/**
* @return the options that should be added to every request. Must not be {@literal null}
*/
public TransportOptions transportOptions() {
return new RestClientOptions(RequestOptions.DEFAULT, false);
}
}

View File

@ -23,8 +23,6 @@ import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem; import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem; import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
import co.elastic.clients.elasticsearch.core.search.ResponseBody; import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.elasticsearch.sql.ElasticsearchSqlClient;
import co.elastic.clients.elasticsearch.sql.QueryResponse;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version; import co.elastic.clients.transport.Version;
@ -40,7 +38,6 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.BulkFailureException; import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation; import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate; import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
@ -59,7 +56,7 @@ import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest; import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse; import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.data.elasticsearch.core.script.Script; import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.sql.SqlResponse; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -77,7 +74,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class); private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
private final ElasticsearchClient client; private final ElasticsearchClient client;
private final ElasticsearchSqlClient sqlClient;
private final RequestConverter requestConverter; private final RequestConverter requestConverter;
private final ResponseConverter responseConverter; private final ResponseConverter responseConverter;
private final JsonpMapper jsonpMapper; private final JsonpMapper jsonpMapper;
@ -89,7 +85,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(client, "client must not be null"); Assert.notNull(client, "client must not be null");
this.client = client; this.client = client;
this.sqlClient = client.sql();
this.jsonpMapper = client._transport().jsonpMapper(); this.jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper); requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper); responseConverter = new ResponseConverter(jsonpMapper);
@ -102,7 +97,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(client, "client must not be null"); Assert.notNull(client, "client must not be null");
this.client = client; this.client = client;
this.sqlClient = client.sql();
this.jsonpMapper = client._transport().jsonpMapper(); this.jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper); requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper); responseConverter = new ResponseConverter(jsonpMapper);
@ -181,6 +175,19 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return delete(query, clazz, getIndexCoordinatesFor(clazz)); return delete(query, clazz, getIndexCoordinatesFor(clazz));
} }
@Override
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index, getRefreshPolicy());
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
return responseConverter.byQueryResponse(response);
}
@Override @Override
public ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index) { public ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null"); Assert.notNull(query, "query must not be null");
@ -649,19 +656,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
DeleteScriptRequest request = requestConverter.scriptDelete(name); DeleteScriptRequest request = requestConverter.scriptDelete(name);
return execute(client -> client.deleteScript(request)).acknowledged(); return execute(client -> client.deleteScript(request)).acknowledged();
} }
@Override
public SqlResponse search(SqlQuery query) {
Assert.notNull(query, "Query must not be null.");
try {
QueryResponse response = sqlClient.query(requestConverter.sqlQueryRequest(query));
return responseConverter.sqlResponse(response);
} catch (IOException e) {
throw exceptionTranslator.translateException(e);
}
}
// endregion // endregion
// region client callback // region client callback

View File

@ -20,7 +20,6 @@ import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight; import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
@ -28,6 +27,7 @@ import org.springframework.data.elasticsearch.core.query.highlight.HighlightFiel
import org.springframework.data.elasticsearch.core.query.highlight.HighlightFieldParameters; import org.springframework.data.elasticsearch.core.query.highlight.HighlightFieldParameters;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters; import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -43,8 +43,7 @@ class HighlightQueryBuilder {
private final RequestConverter requestConverter; private final RequestConverter requestConverter;
HighlightQueryBuilder( HighlightQueryBuilder(
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, RequestConverter requestConverter) {
RequestConverter requestConverter) {
this.mappingContext = mappingContext; this.mappingContext = mappingContext;
this.requestConverter = requestConverter; this.requestConverter = requestConverter;
} }

View File

@ -21,13 +21,13 @@ import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse; import co.elastic.clients.transport.endpoints.BooleanResponse;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
@ -46,10 +46,9 @@ import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest; import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
import org.springframework.data.elasticsearch.core.mapping.Alias;
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -61,6 +60,8 @@ import org.springframework.util.Assert;
public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchIndicesClient> public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchIndicesClient>
implements IndexOperations { implements IndexOperations {
private static final Log LOGGER = LogFactory.getLog(IndicesTemplate.class);
// we need a cluster client as well because ES has put some methods from the indices API into the cluster client // we need a cluster client as well because ES has put some methods from the indices API into the cluster client
// (component templates) // (component templates)
private final ClusterTemplate clusterTemplate; private final ClusterTemplate clusterTemplate;
@ -136,14 +137,11 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings, protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
@Nullable Document mapping) { @Nullable Document mapping) {
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
.withAliases(aliases)
.withSettings(settings)
.withMapping(mapping)
.build();
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings); Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
Assert.notNull(settings, "settings must not be null");
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexCoordinates, settings, mapping);
CreateIndexResponse createIndexResponse = execute(client -> client.create(createIndexRequest)); CreateIndexResponse createIndexResponse = execute(client -> client.create(createIndexRequest));
return Boolean.TRUE.equals(createIndexResponse.acknowledged()); return Boolean.TRUE.equals(createIndexResponse.acknowledged());
} }
@ -238,7 +236,8 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
GetMappingRequest getMappingRequest = requestConverter.indicesGetMappingRequest(indexCoordinates); GetMappingRequest getMappingRequest = requestConverter.indicesGetMappingRequest(indexCoordinates);
GetMappingResponse getMappingResponse = execute(client -> client.getMapping(getMappingRequest)); GetMappingResponse getMappingResponse = execute(client -> client.getMapping(getMappingRequest));
return responseConverter.indicesGetMapping(getMappingResponse, indexCoordinates); Document mappingResponse = responseConverter.indicesGetMapping(getMappingResponse, indexCoordinates);
return mappingResponse;
} }
@Override @Override
@ -450,14 +449,5 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) { public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
return getRequiredPersistentEntity(clazz).getIndexCoordinates(); return getRequiredPersistentEntity(clazz).getIndexCoordinates();
} }
/**
* Get the {@link Alias} of the provided class.
*
* @param clazz provided class that can be used to extract aliases.
*/
public Set<Alias> getAliasesFor(Class<?> clazz) {
return getRequiredPersistentEntity(clazz).getAliases();
}
// endregion // endregion
} }

View File

@ -23,7 +23,7 @@ import java.nio.charset.StandardCharsets;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
@ -48,7 +48,7 @@ final class JsonUtils {
} }
@Nullable @Nullable
public static String queryToJson(co.elastic.clients.elasticsearch._types.query_dsl.@Nullable Query query, public static String queryToJson(@Nullable co.elastic.clients.elasticsearch._types.query_dsl.Query query,
JsonpMapper mapper) { JsonpMapper mapper) {
if (query == null) { if (query == null) {

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.KnnQuery;
import co.elastic.clients.elasticsearch._types.KnnSearch; import co.elastic.clients.elasticsearch._types.KnnSearch;
import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
@ -28,8 +29,9 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.query.BaseQuery; import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -38,13 +40,12 @@ import org.springframework.util.Assert;
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo * @author Sascha Woo
* @author Haibo Liu
* @since 4.4 * @since 4.4
*/ */
public class NativeQuery extends BaseQuery { public class NativeQuery extends BaseQuery {
@Nullable private final Query query; @Nullable private final Query query;
private org.springframework.data.elasticsearch.core.query.@Nullable Query springDataQuery; @Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
@Nullable private Query filter; @Nullable private Query filter;
// note: the new client does not have pipeline aggs, these are just set up as normal aggs // note: the new client does not have pipeline aggs, these are just set up as normal aggs
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>(); private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@ -53,6 +54,7 @@ public class NativeQuery extends BaseQuery {
private List<SortOptions> sortOptions = Collections.emptyList(); private List<SortOptions> sortOptions = Collections.emptyList();
private Map<String, JsonData> searchExtensions = Collections.emptyMap(); private Map<String, JsonData> searchExtensions = Collections.emptyMap();
@Nullable private KnnQuery knnQuery;
@Nullable private List<KnnSearch> knnSearches = Collections.emptyList(); @Nullable private List<KnnSearch> knnSearches = Collections.emptyList();
public NativeQuery(NativeQueryBuilder builder) { public NativeQuery(NativeQueryBuilder builder) {
@ -70,6 +72,7 @@ public class NativeQuery extends BaseQuery {
"Cannot add an NativeQuery in a NativeQuery"); "Cannot add an NativeQuery in a NativeQuery");
} }
this.springDataQuery = builder.getSpringDataQuery(); this.springDataQuery = builder.getSpringDataQuery();
this.knnQuery = builder.getKnnQuery();
this.knnSearches = builder.getKnnSearches(); this.knnSearches = builder.getKnnSearches();
} }
@ -117,10 +120,18 @@ public class NativeQuery extends BaseQuery {
* @see NativeQueryBuilder#withQuery(org.springframework.data.elasticsearch.core.query.Query). * @see NativeQueryBuilder#withQuery(org.springframework.data.elasticsearch.core.query.Query).
* @since 5.1 * @since 5.1
*/ */
public void setSpringDataQuery(org.springframework.data.elasticsearch.core.query.@Nullable Query springDataQuery) { public void setSpringDataQuery(@Nullable org.springframework.data.elasticsearch.core.query.Query springDataQuery) {
this.springDataQuery = springDataQuery; this.springDataQuery = springDataQuery;
} }
/**
* @since 5.1
*/
@Nullable
public KnnQuery getKnnQuery() {
return knnQuery;
}
/** /**
* @since 5.3.1 * @since 5.3.1
*/ */
@ -129,7 +140,8 @@ public class NativeQuery extends BaseQuery {
return knnSearches; return knnSearches;
} }
public org.springframework.data.elasticsearch.core.query.@Nullable Query getSpringDataQuery() { @Nullable
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
return springDataQuery; return springDataQuery;
} }
} }

View File

@ -33,14 +33,13 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder; import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo * @author Sascha Woo
* @author Haibo Liu
* @since 4.4 * @since 4.4
*/ */
public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> { public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> {
@ -53,7 +52,7 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
private final List<SortOptions> sortOptions = new ArrayList<>(); private final List<SortOptions> sortOptions = new ArrayList<>();
private final Map<String, JsonData> searchExtensions = new LinkedHashMap<>(); private final Map<String, JsonData> searchExtensions = new LinkedHashMap<>();
private org.springframework.data.elasticsearch.core.query.@Nullable Query springDataQuery; @Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
@Nullable private KnnQuery knnQuery; @Nullable private KnnQuery knnQuery;
@Nullable private List<KnnSearch> knnSearches = Collections.emptyList(); @Nullable private List<KnnSearch> knnSearches = Collections.emptyList();
@ -104,7 +103,8 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return knnSearches; return knnSearches;
} }
public org.springframework.data.elasticsearch.core.query.@Nullable Query getSpringDataQuery() { @Nullable
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
return springDataQuery; return springDataQuery;
} }
@ -213,30 +213,13 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
} }
/** /**
* @since 5.4 * @since 5.1
*/ */
public NativeQueryBuilder withKnnSearches(List<KnnSearch> knnSearches) { public NativeQueryBuilder withKnnQuery(KnnQuery knnQuery) {
this.knnSearches = knnSearches; this.knnQuery = knnQuery;
return this; return this;
} }
/**
* @since 5.4
*/
public NativeQueryBuilder withKnnSearches(Function<KnnSearch.Builder, ObjectBuilder<KnnSearch>> fn) {
Assert.notNull(fn, "fn must not be null");
return withKnnSearches(fn.apply(new KnnSearch.Builder()).build());
}
/**
* @since 5.4
*/
public NativeQueryBuilder withKnnSearches(KnnSearch knnSearch) {
return withKnnSearches(List.of(knnSearch));
}
public NativeQuery build() { public NativeQuery build() {
Assert.isTrue(query == null || springDataQuery == null, "Cannot have both a native query and a Spring Data query"); Assert.isTrue(query == null || springDataQuery == null, "Cannot have both a native query and a Spring Data query");
return new NativeQuery(this); return new NativeQuery(this);

View File

@ -34,9 +34,9 @@ import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder; import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -27,17 +27,15 @@ import co.elastic.clients.transport.endpoints.EndpointWithResponseMapperAttr;
import co.elastic.clients.util.ObjectBuilder; import co.elastic.clients.util.ObjectBuilder;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.io.IOException;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Reactive version of {@link co.elastic.clients.elasticsearch.ElasticsearchClient}. * Reactive version of {@link co.elastic.clients.elasticsearch.ElasticsearchClient}.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author maryantocinn
* @since 4.4 * @since 4.4
*/ */
public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTransport, ReactiveElasticsearchClient> public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTransport, ReactiveElasticsearchClient>
@ -57,11 +55,8 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
} }
@Override @Override
public void close() throws IOException { public void close() throws Exception {
// since Elasticsearch 8.16 the ElasticsearchClient implements (through ApiClient) the Closeable interface and transport.close();
// handles closing of the underlying transport. We now just call the base class, but keep this as we
// have been implementing AutoCloseable since 4.4 and won't change that to a mere Closeable
super.close();
} }
// region child clients // region child clients
@ -74,10 +69,6 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
return new ReactiveElasticsearchIndicesClient(transport, transportOptions); return new ReactiveElasticsearchIndicesClient(transport, transportOptions);
} }
public ReactiveElasticsearchSqlClient sql() {
return new ReactiveElasticsearchSqlClient(transport, transportOptions);
}
// endregion // endregion
// region info // region info
@ -131,8 +122,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
// java.lang.Class<TDocument>) // java.lang.Class<TDocument>)
// noinspection unchecked // noinspection unchecked
JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse>) GetRequest._ENDPOINT; JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse>) GetRequest._ENDPOINT;
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.get.TDocument",
"co.elastic.clients:Deserializer:_global.get.Response.TDocument",
getDeserializer(tClass)); getDeserializer(tClass));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions)); return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
@ -151,7 +141,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
// noinspection unchecked // noinspection unchecked
JsonEndpoint<UpdateRequest<?, ?>, UpdateResponse<T>, ErrorResponse> endpoint = new EndpointWithResponseMapperAttr( JsonEndpoint<UpdateRequest<?, ?>, UpdateResponse<T>, ErrorResponse> endpoint = new EndpointWithResponseMapperAttr(
UpdateRequest._ENDPOINT, "co.elastic.clients:Deserializer:_global.update.Response.TDocument", UpdateRequest._ENDPOINT, "co.elastic.clients:Deserializer:_global.update.TDocument",
this.getDeserializer(clazz)); this.getDeserializer(clazz));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, this.transportOptions)); return Mono.fromFuture(transport.performRequestAsync(request, endpoint, this.transportOptions));
} }
@ -177,8 +167,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
// noinspection unchecked // noinspection unchecked
JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse>) MgetRequest._ENDPOINT; JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse>) MgetRequest._ENDPOINT;
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.mget.TDocument",
"co.elastic.clients:Deserializer:_global.mget.Response.TDocument",
this.getDeserializer(clazz)); this.getDeserializer(clazz));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions)); return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
@ -234,26 +223,6 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
return deleteByQuery(fn.apply(new DeleteByQueryRequest.Builder()).build()); return deleteByQuery(fn.apply(new DeleteByQueryRequest.Builder()).build());
} }
/**
* @since 5.4
*/
public Mono<CountResponse> count(CountRequest request) {
Assert.notNull(request, "request must not be null");
return Mono.fromFuture(transport.performRequestAsync(request, CountRequest._ENDPOINT, transportOptions));
}
/**
* @since 5.4
*/
public Mono<CountResponse> count(Function<CountRequest.Builder, ObjectBuilder<CountRequest>> fn) {
Assert.notNull(fn, "fn must not be null");
return count(fn.apply(new CountRequest.Builder()).build());
}
// endregion // endregion
// region search // region search
@ -309,7 +278,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
// noinspection unchecked // noinspection unchecked
JsonEndpoint<ScrollRequest, ScrollResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<ScrollRequest, ScrollResponse<T>, ErrorResponse>) ScrollRequest._ENDPOINT; JsonEndpoint<ScrollRequest, ScrollResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<ScrollRequest, ScrollResponse<T>, ErrorResponse>) ScrollRequest._ENDPOINT;
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, endpoint = new EndpointWithResponseMapperAttr<>(endpoint,
"co.elastic.clients:Deserializer:_global.scroll.Response.TDocument", getDeserializer(tDocumentClass)); "co.elastic.clients:Deserializer:_global.scroll.TDocument", getDeserializer(tDocumentClass));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions)); return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
} }

View File

@ -16,15 +16,7 @@
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient; import co.elastic.clients.ApiClient;
import co.elastic.clients.elasticsearch.cluster.DeleteComponentTemplateRequest; import co.elastic.clients.elasticsearch.cluster.*;
import co.elastic.clients.elasticsearch.cluster.DeleteComponentTemplateResponse;
import co.elastic.clients.elasticsearch.cluster.ExistsComponentTemplateRequest;
import co.elastic.clients.elasticsearch.cluster.GetComponentTemplateRequest;
import co.elastic.clients.elasticsearch.cluster.GetComponentTemplateResponse;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.elasticsearch.cluster.PutComponentTemplateRequest;
import co.elastic.clients.elasticsearch.cluster.PutComponentTemplateResponse;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.endpoints.BooleanResponse; import co.elastic.clients.transport.endpoints.BooleanResponse;
@ -33,7 +25,7 @@ import reactor.core.publisher.Mono;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* Reactive version of the {@link co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient} * Reactive version of the {@link co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient}

View File

@ -19,18 +19,12 @@ import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions; import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import co.elastic.clients.transport.rest_client.RestClientOptions; import co.elastic.clients.transport.rest_client.RestClientOptions;
import com.fasterxml.jackson.annotation.JsonInclude; import org.elasticsearch.client.RequestOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations; import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@ -61,11 +55,11 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
* @return RestClient * @return RestClient
*/ */
@Bean @Bean
public Rest5Client elasticsearchRestClient(ClientConfiguration clientConfiguration) { public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null"); Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
return Rest5Clients.getRest5Client(clientConfiguration); return ElasticsearchClients.getRestClient(clientConfiguration);
} }
/** /**
@ -76,12 +70,12 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
* @since 5.2 * @since 5.2
*/ */
@Bean @Bean
public ElasticsearchTransport elasticsearchTransport(Rest5Client rest5Client, JsonpMapper jsonpMapper) { public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
Assert.notNull(rest5Client, "restClient must not be null"); Assert.notNull(restClient, "restClient must not be null");
Assert.notNull(jsonpMapper, "jsonpMapper must not be null"); Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
return ElasticsearchClients.getElasticsearchTransport(rest5Client, ElasticsearchClients.REACTIVE_CLIENT, return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.REACTIVE_CLIENT,
transportOptions(), jsonpMapper); transportOptions(), jsonpMapper);
} }
@ -116,7 +110,7 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
} }
/** /**
* Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(Rest5Client, JsonpMapper)} method and * Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method and
* exposes it as a bean. * exposes it as a bean.
* *
* @return the {@link JsonpMapper} to use * @return the {@link JsonpMapper} to use
@ -124,19 +118,13 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
*/ */
@Bean @Bean
public JsonpMapper jsonpMapper() { public JsonpMapper jsonpMapper() {
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue return new JacksonJsonpMapper();
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
} }
/** /**
* @return the options that should be added to every request. Must not be {@literal null} * @return the options that should be added to every request. Must not be {@literal null}
*/ */
public TransportOptions transportOptions() { public TransportOptions transportOptions() {
return new Rest5ClientOptions(RequestOptions.DEFAULT, false); return new RestClientOptions(RequestOptions.DEFAULT).toBuilder().build();
} }
} }

View File

@ -25,7 +25,7 @@ import reactor.core.publisher.Mono;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* Reactive version of the {@link co.elastic.clients.elasticsearch.indices.ElasticsearchIndicesClient} * Reactive version of the {@link co.elastic.clients.elasticsearch.indices.ElasticsearchIndicesClient}
@ -539,6 +539,14 @@ public class ReactiveElasticsearchIndicesClient
return stats(builder -> builder); return stats(builder -> builder);
} }
public Mono<UnfreezeResponse> unfreeze(UnfreezeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, UnfreezeRequest._ENDPOINT, transportOptions));
}
public Mono<UnfreezeResponse> unfreeze(Function<UnfreezeRequest.Builder, ObjectBuilder<UnfreezeRequest>> fn) {
return unfreeze(fn.apply(new UnfreezeRequest.Builder()).build());
}
public Mono<UpdateAliasesResponse> updateAliases(UpdateAliasesRequest request) { public Mono<UpdateAliasesResponse> updateAliases(UpdateAliasesRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, UpdateAliasesRequest._ENDPOINT, transportOptions)); return Mono.fromFuture(transport.performRequestAsync(request, UpdateAliasesRequest._ENDPOINT, transportOptions));
} }

View File

@ -1,144 +0,0 @@
/*
* Copyright 2021-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest_client.RestClientOptions;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.util.Assert;
/**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the {@link ReactiveElasticsearchClient}. This class exposes different parts of the setup as Spring
* beans. Deriving * classes must provide the {@link ClientConfiguration} to use. <br/>
* This class uses the Elasticsearch RestClient which was replaced b y the Rest5Client in Elasticsearch 9. It is still
* available here but deprecated. *
*
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 6.0 use {@link ReactiveElasticsearchConfiguration}
*/
@Deprecated(since = "6.0", forRemoval=true)
public abstract class ReactiveElasticsearchLegacyRestClientConfiguration extends ElasticsearchConfigurationSupport {
/**
* Must be implemented by deriving classes to provide the {@link ClientConfiguration}.
*
* @return configuration, must not be {@literal null}
*/
@Bean(name = "elasticsearchClientConfiguration")
public abstract ClientConfiguration clientConfiguration();
/**
* Provides the underlying low level RestClient.
*
* @param clientConfiguration configuration for the client, must not be {@literal null}
* @return RestClient
*/
@Bean
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
return RestClients.getRestClient(clientConfiguration);
}
/**
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
* the {@link JsonpMapper} bean provided in this class.
*
* @return the {@link ElasticsearchTransport}
* @since 5.2
*/
@Bean
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
Assert.notNull(restClient, "restClient must not be null");
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.REACTIVE_CLIENT,
transportOptions(), jsonpMapper);
}
/**
* Provides the {@link ReactiveElasticsearchClient} instance used.
*
* @param transport the ElasticsearchTransport to use
* @return ReactiveElasticsearchClient instance.
*/
@Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient(ElasticsearchTransport transport) {
Assert.notNull(transport, "transport must not be null");
return ElasticsearchClients.createReactive(transport);
}
/**
* Creates {@link ReactiveElasticsearchOperations}.
*
* @return never {@literal null}.
*/
@Bean(name = { "reactiveElasticsearchOperations", "reactiveElasticsearchTemplate" })
public ReactiveElasticsearchOperations reactiveElasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
ReactiveElasticsearchClient reactiveElasticsearchClient) {
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient,
elasticsearchConverter);
template.setRefreshPolicy(refreshPolicy());
return template;
}
/**
* Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method and
* exposes it as a bean.
*
* @return the {@link JsonpMapper} to use
* @since 5.2
*/
@Bean
public JsonpMapper jsonpMapper() {
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
}
/**
* @return the options that should be added to every request. Must not be {@literal null}
*/
public TransportOptions transportOptions() {
return new RestClientOptions(RequestOptions.DEFAULT, false).toBuilder().build();
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch.sql.QueryRequest;
import co.elastic.clients.elasticsearch.sql.QueryResponse;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.util.ObjectBuilder;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
/**
* Reactive version of {@link co.elastic.clients.elasticsearch.sql.ElasticsearchSqlClient}.
*
* @author Aouichaoui Youssef
* @since 5.4
*/
public class ReactiveElasticsearchSqlClient extends ApiClient<ElasticsearchTransport, ReactiveElasticsearchSqlClient> {
public ReactiveElasticsearchSqlClient(ElasticsearchTransport transport, @Nullable TransportOptions transportOptions) {
super(transport, transportOptions);
}
@Override
public ReactiveElasticsearchSqlClient withTransportOptions(@Nullable TransportOptions transportOptions) {
return new ReactiveElasticsearchSqlClient(transport, transportOptions);
}
/**
* Executes a SQL request
*
* @param fn a function that initializes a builder to create the {@link QueryRequest}.
*/
public final Mono<QueryResponse> query(Function<QueryRequest.Builder, ObjectBuilder<QueryRequest>> fn)
throws IOException, ElasticsearchException {
return query(fn.apply(new QueryRequest.Builder()).build());
}
/**
* Executes a SQL request.
*/
public Mono<QueryResponse> query(QueryRequest query) {
return Mono.fromFuture(transport.performRequestAsync(query, QueryRequest._ENDPOINT, transportOptions));
}
/**
* Executes a SQL request.
*/
public Mono<QueryResponse> query() {
return Mono.fromFuture(
transport.performRequestAsync(new QueryRequest.Builder().build(), QueryRequest._ENDPOINT, transportOptions));
}
}

View File

@ -40,7 +40,6 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.BulkFailureException; import org.springframework.data.elasticsearch.BulkFailureException;
@ -58,12 +57,19 @@ import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocument; import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse; import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*; import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse; import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest; import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse; import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.data.elasticsearch.core.script.Script; import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.sql.SqlResponse; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -82,7 +88,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private static final Log LOGGER = LogFactory.getLog(ReactiveElasticsearchTemplate.class); private static final Log LOGGER = LogFactory.getLog(ReactiveElasticsearchTemplate.class);
private final ReactiveElasticsearchClient client; private final ReactiveElasticsearchClient client;
private final ReactiveElasticsearchSqlClient sqlClient;
private final RequestConverter requestConverter; private final RequestConverter requestConverter;
private final ResponseConverter responseConverter; private final ResponseConverter responseConverter;
private final JsonpMapper jsonpMapper; private final JsonpMapper jsonpMapper;
@ -94,7 +99,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(client, "client must not be null"); Assert.notNull(client, "client must not be null");
this.client = client; this.client = client;
this.sqlClient = client.sql();
this.jsonpMapper = client._transport().jsonpMapper(); this.jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(converter, jsonpMapper); requestConverter = new RequestConverter(converter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper); responseConverter = new ResponseConverter(jsonpMapper);
@ -167,6 +171,16 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
.onErrorReturn(NoSuchIndexException.class, false); .onErrorReturn(NoSuchIndexException.class, false);
} }
@Override
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index, getRefreshPolicy());
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
}
@Override @Override
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index) { public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "query must not be null"); Assert.notNull(query, "query must not be null");
@ -632,14 +646,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return NativeQuery.builder().withIds(ids); return NativeQuery.builder().withIds(ids);
} }
@Override
public Mono<SqlResponse> search(SqlQuery query) {
Assert.notNull(query, "Query must not be null.");
co.elastic.clients.elasticsearch.sql.QueryRequest request = requestConverter.sqlQueryRequest(query);
return sqlClient.query(request).onErrorMap(this::translateException).map(responseConverter::sqlResponse);
}
/** /**
* Callback interface to be used with {@link #execute(ReactiveElasticsearchTemplate.ClientCallback<>)} for operating * Callback interface to be used with {@link #execute(ReactiveElasticsearchTemplate.ClientCallback<>)} for operating
* directly on {@link ReactiveElasticsearchClient}. * directly on {@link ReactiveElasticsearchClient}.

View File

@ -15,7 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.util.StringUtils.*; import static org.springframework.util.StringUtils.hasText;
import co.elastic.clients.elasticsearch._types.AcknowledgedResponseBase; import co.elastic.clients.elasticsearch._types.AcknowledgedResponseBase;
import co.elastic.clients.elasticsearch.indices.*; import co.elastic.clients.elasticsearch.indices.*;
@ -24,12 +24,10 @@ import co.elastic.clients.transport.endpoints.BooleanResponse;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.elasticsearch.NoSuchIndexException; import org.springframework.data.elasticsearch.NoSuchIndexException;
@ -48,10 +46,9 @@ import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest; import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
import org.springframework.data.elasticsearch.core.mapping.Alias;
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -133,14 +130,8 @@ public class ReactiveIndicesTemplate
private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings, private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
@Nullable Document mapping) { @Nullable Document mapping) {
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
.withAliases(aliases)
.withSettings(settings)
.withMapping(mapping)
.build();
CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexSettings); CreateIndexRequest createIndexRequest = requestConverter.indicesCreateRequest(indexCoordinates, settings, mapping);
Mono<CreateIndexResponse> createIndexResponse = Mono.from(execute(client -> client.create(createIndexRequest))); Mono<CreateIndexResponse> createIndexResponse = Mono.from(execute(client -> client.create(createIndexRequest)));
return createIndexResponse.map(CreateIndexResponse::acknowledged); return createIndexResponse.map(CreateIndexResponse::acknowledged);
} }
@ -444,15 +435,6 @@ public class ReactiveIndicesTemplate
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getIndexCoordinates(); return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getIndexCoordinates();
} }
/**
* Get the {@link Alias} of the provided class.
*
* @param clazz provided class that can be used to extract aliases.
*/
private Set<Alias> getAliasesFor(Class<?> clazz) {
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz).getAliases();
}
private Class<?> checkForBoundClass() { private Class<?> checkForBoundClass() {
if (boundClass == null) { if (boundClass == null) {
throw new InvalidDataAccessApiUsageException("IndexOperations are not bound"); throw new InvalidDataAccessApiUsageException("IndexOperations are not bound");

View File

@ -20,6 +20,7 @@ import static org.springframework.util.CollectionUtils.*;
import co.elastic.clients.elasticsearch._types.Conflicts; import co.elastic.clients.elasticsearch._types.Conflicts;
import co.elastic.clients.elasticsearch._types.ExpandWildcard; import co.elastic.clients.elasticsearch._types.ExpandWildcard;
import co.elastic.clients.elasticsearch._types.InlineScript;
import co.elastic.clients.elasticsearch._types.NestedSortValue; import co.elastic.clients.elasticsearch._types.NestedSortValue;
import co.elastic.clients.elasticsearch._types.OpType; import co.elastic.clients.elasticsearch._types.OpType;
import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOptions;
@ -42,20 +43,18 @@ import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation; import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation; import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation;
import co.elastic.clients.elasticsearch.core.mget.MultiGetOperation; import co.elastic.clients.elasticsearch.core.mget.MultiGetOperation;
import co.elastic.clients.elasticsearch.core.msearch.MultisearchBody;
import co.elastic.clients.elasticsearch.core.msearch.MultisearchHeader; import co.elastic.clients.elasticsearch.core.msearch.MultisearchHeader;
import co.elastic.clients.elasticsearch.core.search.Highlight; import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Rescore; import co.elastic.clients.elasticsearch.core.search.Rescore;
import co.elastic.clients.elasticsearch.core.search.SearchRequestBody;
import co.elastic.clients.elasticsearch.core.search.SourceConfig; import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import co.elastic.clients.elasticsearch.indices.*; import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.ExistsIndexTemplateRequest; import co.elastic.clients.elasticsearch.indices.ExistsIndexTemplateRequest;
import co.elastic.clients.elasticsearch.indices.ExistsRequest; import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.elasticsearch.indices.update_aliases.Action; import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
import co.elastic.clients.elasticsearch.sql.query.SqlFormat;
import co.elastic.clients.json.JsonData; import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpDeserializer; import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.util.NamedValue;
import co.elastic.clients.util.ObjectBuilder; import co.elastic.clients.util.ObjectBuilder;
import jakarta.json.stream.JsonParser; import jakarta.json.stream.JsonParser;
@ -69,16 +68,13 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy; import org.springframework.data.elasticsearch.core.RefreshPolicy;
@ -92,8 +88,6 @@ import org.springframework.data.elasticsearch.core.index.GetIndexTemplateRequest
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest; import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest; import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
import org.springframework.data.elasticsearch.core.mapping.Alias;
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@ -102,6 +96,7 @@ import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.Remote; import org.springframework.data.elasticsearch.core.reindex.Remote;
import org.springframework.data.elasticsearch.core.script.Script; import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap; import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -116,6 +111,7 @@ import org.springframework.util.StringUtils;
* @author Haibo Liu * @author Haibo Liu
* @since 4.4 * @since 4.4
*/ */
@SuppressWarnings("ClassCanBeRecord")
class RequestConverter extends AbstractQueryProcessor { class RequestConverter extends AbstractQueryProcessor {
private static final Log LOGGER = LogFactory.getLog(RequestConverter.class); private static final Log LOGGER = LogFactory.getLog(RequestConverter.class);
@ -174,8 +170,7 @@ class RequestConverter extends AbstractQueryProcessor {
})); }));
} }
private co.elastic.clients.elasticsearch.indices.Alias.Builder buildAlias(AliasActionParameters parameters, private Alias.Builder buildAlias(AliasActionParameters parameters, Alias.Builder aliasBuilder) {
co.elastic.clients.elasticsearch.indices.Alias.Builder aliasBuilder) {
if (parameters.getRouting() != null) { if (parameters.getRouting() != null) {
aliasBuilder.routing(parameters.getRouting()); aliasBuilder.routing(parameters.getRouting());
@ -239,25 +234,17 @@ class RequestConverter extends AbstractQueryProcessor {
return new ExistsRequest.Builder().index(Arrays.asList(indexCoordinates.getIndexNames())).build(); return new ExistsRequest.Builder().index(Arrays.asList(indexCoordinates.getIndexNames())).build();
} }
public CreateIndexRequest indicesCreateRequest(CreateIndexSettings indexSettings) { public CreateIndexRequest indicesCreateRequest(IndexCoordinates indexCoordinates, Map<String, Object> settings,
Map<String, co.elastic.clients.elasticsearch.indices.Alias> aliases = new HashMap<>(); @Nullable Document mapping) {
for (Alias alias : indexSettings.getAliases()) {
co.elastic.clients.elasticsearch.indices.Alias esAlias = co.elastic.clients.elasticsearch.indices.Alias Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
.of(ab -> ab.filter(getQuery(alias.getFilter(), null)) Assert.notNull(settings, "settings must not be null");
.routing(alias.getRouting())
.indexRouting(alias.getIndexRouting())
.searchRouting(alias.getSearchRouting())
.isHidden(alias.getHidden())
.isWriteIndex(alias.getWriteIndex()));
aliases.put(alias.getAlias(), esAlias);
}
// note: the new client does not support the index.storeType anymore // note: the new client does not support the index.storeType anymore
return new CreateIndexRequest.Builder() // return new CreateIndexRequest.Builder() //
.index(indexSettings.getIndexCoordinates().getIndexName()) // .index(indexCoordinates.getIndexName()) //
.aliases(aliases) .settings(indexSettings(settings)) //
.settings(indexSettings(indexSettings.getSettings())) // .mappings(typeMapping(mapping)) //
.mappings(typeMapping(indexSettings.getMapping())) //
.build(); .build();
} }
@ -412,7 +399,7 @@ class RequestConverter extends AbstractQueryProcessor {
if (putTemplateRequest.getSettings() != null) { if (putTemplateRequest.getSettings() != null) {
Map<String, JsonData> settings = getTemplateParams(putTemplateRequest.getSettings().entrySet()); Map<String, JsonData> settings = getTemplateParams(putTemplateRequest.getSettings().entrySet());
builder.settings(sb -> sb.otherSettings(settings)); builder.settings(settings);
} }
if (putTemplateRequest.getMappings() != null) { if (putTemplateRequest.getMappings() != null) {
@ -533,27 +520,6 @@ class RequestConverter extends AbstractQueryProcessor {
.of(gtr -> gtr.name(getTemplateRequest.getTemplateName()).flatSettings(true)); .of(gtr -> gtr.name(getTemplateRequest.getTemplateName()).flatSettings(true));
} }
public co.elastic.clients.elasticsearch.sql.QueryRequest sqlQueryRequest(SqlQuery query) {
Assert.notNull(query, "Query must not be null.");
return co.elastic.clients.elasticsearch.sql.QueryRequest.of(sqb -> sqb
.query(query.getQuery())
.catalog(query.getCatalog())
.columnar(query.getColumnar())
.cursor(query.getCursor())
.fetchSize(query.getFetchSize())
.fieldMultiValueLeniency(query.getFieldMultiValueLeniency())
.indexUsingFrozen(query.getIndexIncludeFrozen())
.keepAlive(time(query.getKeepAlive()))
.keepOnCompletion(query.getKeepOnCompletion())
.pageTimeout(time(query.getPageTimeout()))
.requestTimeout(time(query.getRequestTimeout()))
.waitForCompletionTimeout(time(query.getWaitForCompletionTimeout()))
.filter(getQuery(query.getFilter(), null))
.timeZone(Objects.toString(query.getTimeZone(), null))
.format(SqlFormat.Json));
}
// endregion // endregion
// region documents // region documents
@ -738,7 +704,8 @@ class RequestConverter extends AbstractQueryProcessor {
return uob.build(); return uob.build();
} }
private co.elastic.clients.elasticsearch._types.@Nullable Script getScript(@Nullable ScriptData scriptData) { @Nullable
private co.elastic.clients.elasticsearch._types.Script getScript(@Nullable ScriptData scriptData) {
if (scriptData == null) { if (scriptData == null) {
return null; return null;
@ -750,11 +717,16 @@ class RequestConverter extends AbstractQueryProcessor {
scriptData.params().forEach((key, value) -> params.put(key, JsonData.of(value, jsonpMapper))); scriptData.params().forEach((key, value) -> params.put(key, JsonData.of(value, jsonpMapper)));
} }
return co.elastic.clients.elasticsearch._types.Script.of(sb -> { return co.elastic.clients.elasticsearch._types.Script.of(sb -> {
sb.lang(scriptData.language()) if (scriptData.type() == ScriptType.INLINE) {
.params(params) sb.inline(is -> is //
.id(scriptData.scriptName()); .lang(scriptData.language()) //
if (scriptData.script() != null){ .source(scriptData.script()) //
sb.source(s -> s.scriptString(scriptData.script())); .params(params)); //
} else if (scriptData.type() == ScriptType.STORED) {
sb.stored(ss -> ss //
.id(scriptData.script()) //
.params(params) //
);
} }
return sb; return sb;
}); });
@ -926,13 +898,7 @@ class RequestConverter extends AbstractQueryProcessor {
ReindexRequest.Script script = reindexRequest.getScript(); ReindexRequest.Script script = reindexRequest.getScript();
if (script != null) { if (script != null) {
builder.script(sb -> { builder.script(s -> s.inline(InlineScript.of(i -> i.lang(script.getLang()).source(script.getSource()))));
if (script.getSource() != null){
sb.source(s -> s.scriptString(script.getSource()));
}
sb.lang(script.getLang());
return sb;
});
} }
builder.timeout(time(reindexRequest.getTimeout())) // builder.timeout(time(reindexRequest.getTimeout())) //
@ -1050,7 +1016,7 @@ class RequestConverter extends AbstractQueryProcessor {
order = sortField.order().jsonValue(); order = sortField.order().jsonValue();
} }
return sortField.field() + ':' + order; return sortField.field() + ":" + order;
}) })
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -1088,14 +1054,21 @@ class RequestConverter extends AbstractQueryProcessor {
} }
uqb.script(sb -> { uqb.script(sb -> {
sb.lang(query.getLang()).params(params); if (query.getScriptType() == ScriptType.INLINE) {
if (query.getScript() != null){ sb.inline(is -> is //
sb.source(s -> s.scriptString(query.getScript())); .lang(query.getLang()) //
.source(query.getScript()) //
.params(params)); //
} else if (query.getScriptType() == ScriptType.STORED) {
sb.stored(ss -> ss //
.id(query.getScript()) //
.params(params) //
);
} }
sb.id(query.getId());
return sb; return sb;
}); }
);
} }
uqb // uqb //
@ -1256,11 +1229,11 @@ class RequestConverter extends AbstractQueryProcessor {
mtrb.searchTemplates(stb -> stb mtrb.searchTemplates(stb -> stb
.header(msearchHeaderBuilder(query, param.index(), routing)) .header(msearchHeaderBuilder(query, param.index(), routing))
.body(bb -> { .body(bb -> {
bb.explain(query.getExplain()) // bb //
.id(query.getId()); // .explain(query.getExplain()) //
if (query.getSource() != null){ .id(query.getId()) //
bb.source(s -> s.scriptString(query.getSource())); .source(query.getSource()) //
} ;
if (!CollectionUtils.isEmpty(query.getParams())) { if (!CollectionUtils.isEmpty(query.getParams())) {
Map<String, JsonData> params = getTemplateParams(query.getParams().entrySet()); Map<String, JsonData> params = getTemplateParams(query.getParams().entrySet());
@ -1296,15 +1269,11 @@ class RequestConverter extends AbstractQueryProcessor {
.timeout(timeStringMs(query.getTimeout())) // .timeout(timeStringMs(query.getTimeout())) //
; ;
var offset = query.getPageable().isPaged() ? query.getPageable().getOffset() : 0; if (query.getPageable().isPaged()) {
var pageSize = query.getPageable().isPaged() ? query.getPageable().getPageSize() bb //
: INDEX_MAX_RESULT_WINDOW; .from((int) query.getPageable().getOffset()) //
// if we have both a page size and a max results, we take the min, this is necessary for .size(query.getPageable().getPageSize());
// searchForStream to work correctly (#3098) as there the page size defines what is }
// returned in a single request, and the max result determines the total number of
// documents returned
var size = query.isLimiting() ? Math.min(pageSize, query.getMaxResults()) : pageSize;
bb.from((int) offset).size(size);
if (!isEmpty(query.getFields())) { if (!isEmpty(query.getFields())) {
bb.fields(fb -> { bb.fields(fb -> {
@ -1317,6 +1286,10 @@ class RequestConverter extends AbstractQueryProcessor {
bb.storedFields(query.getStoredFields()); bb.storedFields(query.getStoredFields());
} }
if (query.isLimiting()) {
bb.size(query.getMaxResults());
}
if (query.getMinScore() > 0) { if (query.getMinScore() > 0) {
bb.minScore((double) query.getMinScore()); bb.minScore((double) query.getMinScore());
} }
@ -1350,18 +1323,17 @@ class RequestConverter extends AbstractQueryProcessor {
String script = runtimeField.getScript(); String script = runtimeField.getScript();
if (script != null) { if (script != null) {
rfb.script(s -> { rfb
if (script != null) { .script(s -> s
s.source(so -> so.scriptString(script)); .inline(is -> {
} is.source(script);
if (runtimeField.getParams() != null) { if (runtimeField.getParams() != null) {
s.params(TypeUtils.paramsMap(runtimeField.getParams())); is.params(TypeUtils.paramsMap(runtimeField.getParams()));
} }
return s; return is;
}); }));
} }
return rfb; return rfb;
}); });
runtimeMappings.put(runtimeField.getName(), esRuntimeField); runtimeMappings.put(runtimeField.getName(), esRuntimeField);
@ -1370,14 +1342,9 @@ class RequestConverter extends AbstractQueryProcessor {
} }
if (!isEmpty(query.getIndicesBoost())) { if (!isEmpty(query.getIndicesBoost())) {
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream() bb.indicesBoost(query.getIndicesBoost().stream()
.map(indexBoost -> { .map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
var namedValue = new NamedValue(indexBoost.getIndexName(), .collect(Collectors.toList()));
Float.valueOf(indexBoost.getBoost()).doubleValue());
return namedValue;
});
List<NamedValue<Double>> namedValueList = namedValueStream.collect(Collectors.toList());
bb.indicesBoost(namedValueList);
} }
query.getScriptedFields().forEach(scriptedField -> bb.scriptFields(scriptedField.getFieldName(), query.getScriptedFields().forEach(scriptedField -> bb.scriptFields(scriptedField.getFieldName(),
@ -1403,7 +1370,7 @@ class RequestConverter extends AbstractQueryProcessor {
private Function<MultisearchHeader.Builder, ObjectBuilder<MultisearchHeader>> msearchHeaderBuilder(Query query, private Function<MultisearchHeader.Builder, ObjectBuilder<MultisearchHeader>> msearchHeaderBuilder(Query query,
IndexCoordinates index, @Nullable String routing) { IndexCoordinates index, @Nullable String routing) {
return h -> { return h -> {
var searchType = (query instanceof NativeQuery nativeQuery && !isEmpty(nativeQuery.getKnnSearches())) ? null var searchType = (query instanceof NativeQuery nativeQuery && nativeQuery.getKnnQuery() != null) ? null
: searchType(query.getSearchType()); : searchType(query.getSearchType());
h // h //
@ -1435,7 +1402,7 @@ class RequestConverter extends AbstractQueryProcessor {
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(clazz); ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(clazz);
var searchType = (query instanceof NativeQuery nativeQuery && !isEmpty(nativeQuery.getKnnSearches())) ? null var searchType = (query instanceof NativeQuery nativeQuery && nativeQuery.getKnnQuery() != null) ? null
: searchType(query.getSearchType()); : searchType(query.getSearchType());
builder // builder //
@ -1476,14 +1443,13 @@ class RequestConverter extends AbstractQueryProcessor {
builder.seqNoPrimaryTerm(true); builder.seqNoPrimaryTerm(true);
} }
var offset = query.getPageable().isPaged() ? query.getPageable().getOffset() : 0; if (query.getPageable().isPaged()) {
var pageSize = query.getPageable().isPaged() ? query.getPageable().getPageSize() : INDEX_MAX_RESULT_WINDOW; builder //
// if we have both a page size and a max results, we take the min, this is necessary for .from((int) query.getPageable().getOffset()) //
// searchForStream to work correctly (#3098) as there the page size defines what is .size(query.getPageable().getPageSize());
// returned in a single request, and the max result determines the total number of } else {
// documents returned builder.from(0).size(INDEX_MAX_RESULT_WINDOW);
var size = query.isLimiting() ? Math.min(pageSize, query.getMaxResults()) : pageSize; }
builder.from((int) offset).size(size);
if (!isEmpty(query.getFields())) { if (!isEmpty(query.getFields())) {
var fieldAndFormats = query.getFields().stream().map(field -> FieldAndFormat.of(b -> b.field(field))).toList(); var fieldAndFormats = query.getFields().stream().map(field -> FieldAndFormat.of(b -> b.field(field))).toList();
@ -1498,6 +1464,10 @@ class RequestConverter extends AbstractQueryProcessor {
addIndicesOptions(builder, query.getIndicesOptions()); addIndicesOptions(builder, query.getIndicesOptions());
} }
if (query.isLimiting()) {
builder.size(query.getMaxResults());
}
if (query.getMinScore() > 0) { if (query.getMinScore() > 0) {
builder.minScore((double) query.getMinScore()); builder.minScore((double) query.getMinScore());
} }
@ -1554,16 +1524,16 @@ class RequestConverter extends AbstractQueryProcessor {
rfb.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType())); rfb.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType()));
String script = runtimeField.getScript(); String script = runtimeField.getScript();
if (script != null) { if (script != null) {
rfb.script(s -> { rfb
if (script != null) { .script(s -> s
s.source(so -> so.scriptString(script)); .inline(is -> {
} is.source(script);
if (runtimeField.getParams() != null) { if (runtimeField.getParams() != null) {
s.params(TypeUtils.paramsMap(runtimeField.getParams())); is.params(TypeUtils.paramsMap(runtimeField.getParams()));
} }
return s; return is;
}); }));
} }
return rfb; return rfb;
@ -1585,14 +1555,9 @@ class RequestConverter extends AbstractQueryProcessor {
} }
if (!isEmpty(query.getIndicesBoost())) { if (!isEmpty(query.getIndicesBoost())) {
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream() builder.indicesBoost(query.getIndicesBoost().stream()
.map(indexBoost -> { .map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
var namedValue = new NamedValue(indexBoost.getIndexName(), .collect(Collectors.toList()));
Float.valueOf(indexBoost.getBoost()).doubleValue());
return namedValue;
});
List<NamedValue<Double>> namedValueList = namedValueStream.collect(Collectors.toList());
builder.indicesBoost(namedValueList);
} }
if (!isEmpty(query.getDocValueFields())) { if (!isEmpty(query.getDocValueFields())) {
@ -1654,7 +1619,7 @@ class RequestConverter extends AbstractQueryProcessor {
builder.highlight(highlight); builder.highlight(highlight);
} }
private void addHighlight(Query query, SearchRequestBody.Builder builder) { private void addHighlight(Query query, MultisearchBody.Builder builder) {
Highlight highlight = query.getHighlightQuery() Highlight highlight = query.getHighlightQuery()
.map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext(), this) .map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext(), this)
@ -1734,7 +1699,7 @@ class RequestConverter extends AbstractQueryProcessor {
} }
@Nullable @Nullable
private NestedSortValue getNestedSort(Order.@Nullable Nested nested, private NestedSortValue getNestedSort(@Nullable Order.Nested nested,
@Nullable ElasticsearchPersistentEntity<?> persistentEntity) { @Nullable ElasticsearchPersistentEntity<?> persistentEntity) {
return (nested == null || persistentEntity == null) ? null return (nested == null || persistentEntity == null) ? null
: NestedSortValue.of(b -> b // : NestedSortValue.of(b -> b //
@ -1764,6 +1729,17 @@ class RequestConverter extends AbstractQueryProcessor {
.sort(query.getSortOptions()) // .sort(query.getSortOptions()) //
; ;
if (query.getKnnQuery() != null) {
var kq = query.getKnnQuery();
builder.knn(ksb -> ksb
.field(kq.field())
.queryVector(kq.queryVector())
.numCandidates(kq.numCandidates())
.filter(kq.filter())
.similarity(kq.similarity()));
}
if (!isEmpty(query.getKnnSearches())) { if (!isEmpty(query.getKnnSearches())) {
builder.knn(query.getKnnSearches()); builder.knn(query.getKnnSearches());
} }
@ -1778,13 +1754,24 @@ class RequestConverter extends AbstractQueryProcessor {
} }
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
private void prepareNativeSearch(NativeQuery query, SearchRequestBody.Builder builder) { private void prepareNativeSearch(NativeQuery query, MultisearchBody.Builder builder) {
builder // builder //
.suggest(query.getSuggester()) // .suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) // .collapse(query.getFieldCollapse()) //
.sort(query.getSortOptions()); .sort(query.getSortOptions());
if (query.getKnnQuery() != null) {
var kq = query.getKnnQuery();
builder.knn(ksb -> ksb
.field(kq.field())
.queryVector(kq.queryVector())
.numCandidates(kq.numCandidates())
.filter(kq.filter())
.similarity(kq.similarity()));
}
if (!isEmpty(query.getKnnSearches())) { if (!isEmpty(query.getKnnSearches())) {
builder.knn(query.getKnnSearches()); builder.knn(query.getKnnSearches());
} }
@ -1798,11 +1785,13 @@ class RequestConverter extends AbstractQueryProcessor {
} }
} }
co.elastic.clients.elasticsearch._types.query_dsl.@Nullable Query getQuery(@Nullable Query query, @Nullable
co.elastic.clients.elasticsearch._types.query_dsl.Query getQuery(@Nullable Query query,
@Nullable Class<?> clazz) { @Nullable Class<?> clazz) {
return getEsQuery(query, (q) -> elasticsearchConverter.updateQuery(q, clazz)); return getEsQuery(query, (q) -> elasticsearchConverter.updateQuery(q, clazz));
} }
@SuppressWarnings("StatementWithEmptyBody")
private void addPostFilter(Query query, SearchRequest.Builder builder) { private void addPostFilter(Query query, SearchRequest.Builder builder) {
// we only need to handle NativeQuery here. filter from a CriteriaQuery are added into the query and not as post // we only need to handle NativeQuery here. filter from a CriteriaQuery are added into the query and not as post
@ -1897,11 +1886,10 @@ class RequestConverter extends AbstractQueryProcessor {
.id(query.getId()) // .id(query.getId()) //
.index(Arrays.asList(index.getIndexNames())) // .index(Arrays.asList(index.getIndexNames())) //
.preference(query.getPreference()) // .preference(query.getPreference()) //
.searchType(searchType(query.getSearchType())); // .searchType(searchType(query.getSearchType())) //
.source(query.getSource()) //
;
if (query.getSource() != null) {
builder.source(so -> so.scriptString(query.getSource()));
}
if (query.getRoute() != null) { if (query.getRoute() != null) {
builder.routing(query.getRoute()); builder.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) { } else if (StringUtils.hasText(routing)) {
@ -1944,7 +1932,7 @@ class RequestConverter extends AbstractQueryProcessor {
.id(script.id()) // .id(script.id()) //
.script(sb -> sb // .script(sb -> sb //
.lang(script.language()) // .lang(script.language()) //
.source(s -> s.scriptString(script.source())))); .source(script.source())));
} }
public GetScriptRequest scriptGet(String name) { public GetScriptRequest scriptGet(String name) {
@ -2034,12 +2022,9 @@ class RequestConverter extends AbstractQueryProcessor {
private SourceConfig getSourceConfig(Query query) { private SourceConfig getSourceConfig(Query query) {
if (query.getSourceFilter() != null) { if (query.getSourceFilter() != null) {
return SourceConfig.of(s -> { return SourceConfig.of(s -> s //
.filter(sfb -> {
SourceFilter sourceFilter = query.getSourceFilter(); SourceFilter sourceFilter = query.getSourceFilter();
if (sourceFilter.fetchSource() != null) {
s.fetch(sourceFilter.fetchSource());
} else {
s.filter(sfb -> {
String[] includes = sourceFilter.getIncludes(); String[] includes = sourceFilter.getIncludes();
String[] excludes = sourceFilter.getExcludes(); String[] excludes = sourceFilter.getExcludes();
@ -2052,10 +2037,7 @@ class RequestConverter extends AbstractQueryProcessor {
} }
return sfb; return sfb;
}); }));
}
return s;
});
} else { } else {
return null; return null;
} }

View File

@ -33,8 +33,6 @@ import co.elastic.clients.elasticsearch.core.mget.MultiGetResponseItem;
import co.elastic.clients.elasticsearch.indices.*; import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.get_index_template.IndexTemplateItem; import co.elastic.clients.elasticsearch.indices.get_index_template.IndexTemplateItem;
import co.elastic.clients.elasticsearch.indices.get_mapping.IndexMappingRecord; import co.elastic.clients.elasticsearch.indices.get_mapping.IndexMappingRecord;
import co.elastic.clients.elasticsearch.sql.QueryResponse;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import java.util.ArrayList; import java.util.ArrayList;
@ -48,7 +46,6 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.ElasticsearchErrorCause; import org.springframework.data.elasticsearch.ElasticsearchErrorCause;
import org.springframework.data.elasticsearch.core.IndexInformation; import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem; import org.springframework.data.elasticsearch.core.MultiGetItem;
@ -64,8 +61,8 @@ import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.StringQuery; import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse; import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.data.elasticsearch.core.script.Script; import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.sql.SqlResponse;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap; import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -92,7 +89,7 @@ class ResponseConverter {
return ClusterHealth.builder() // return ClusterHealth.builder() //
.withActivePrimaryShards(healthResponse.activePrimaryShards()) // .withActivePrimaryShards(healthResponse.activePrimaryShards()) //
.withActiveShards(healthResponse.activeShards()) // .withActiveShards(healthResponse.activeShards()) //
.withActiveShardsPercent(healthResponse.activeShardsPercentAsNumber())// .withActiveShardsPercent(Double.parseDouble(healthResponse.activeShardsPercentAsNumber()))//
.withClusterName(healthResponse.clusterName()) // .withClusterName(healthResponse.clusterName()) //
.withDelayedUnassignedShards(healthResponse.delayedUnassignedShards()) // .withDelayedUnassignedShards(healthResponse.delayedUnassignedShards()) //
.withInitializingShards(healthResponse.initializingShards()) // .withInitializingShards(healthResponse.initializingShards()) //
@ -191,7 +188,7 @@ class ResponseConverter {
Assert.notNull(getMappingResponse, "getMappingResponse must not be null"); Assert.notNull(getMappingResponse, "getMappingResponse must not be null");
Assert.notNull(indexCoordinates, "indexCoordinates must not be null"); Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
Map<String, IndexMappingRecord> mappings = getMappingResponse.mappings(); Map<String, IndexMappingRecord> mappings = getMappingResponse.result();
if (mappings == null || mappings.isEmpty()) { if (mappings == null || mappings.isEmpty()) {
return Document.create(); return Document.create();
@ -219,7 +216,7 @@ class ResponseConverter {
List<IndexInformation> indexInformationList = new ArrayList<>(); List<IndexInformation> indexInformationList = new ArrayList<>();
getIndexResponse.indices().forEach((indexName, indexState) -> { getIndexResponse.result().forEach((indexName, indexState) -> {
Settings settings = indexState.settings() != null ? Settings.parse(toJson(indexState.settings(), jsonpMapper)) Settings settings = indexState.settings() != null ? Settings.parse(toJson(indexState.settings(), jsonpMapper))
: new Settings(); : new Settings();
Document mappings = indexState.mappings() != null ? Document.parse(toJson(indexState.mappings(), jsonpMapper)) Document mappings = indexState.mappings() != null ? Document.parse(toJson(indexState.mappings(), jsonpMapper))
@ -239,7 +236,7 @@ class ResponseConverter {
Assert.notNull(getAliasResponse, "getAliasResponse must not be null"); Assert.notNull(getAliasResponse, "getAliasResponse must not be null");
Map<String, Set<AliasData>> aliasDataMap = new HashMap<>(); Map<String, Set<AliasData>> aliasDataMap = new HashMap<>();
getAliasResponse.aliases().forEach((indexName, alias) -> { getAliasResponse.result().forEach((indexName, alias) -> {
Set<AliasData> aliasDataSet = new HashSet<>(); Set<AliasData> aliasDataSet = new HashSet<>();
alias.aliases() alias.aliases()
.forEach((aliasName, aliasDefinition) -> aliasDataSet.add(indicesGetAliasData(aliasName, aliasDefinition))); .forEach((aliasName, aliasDefinition) -> aliasDataSet.add(indicesGetAliasData(aliasName, aliasDefinition)));
@ -400,6 +397,7 @@ class ResponseConverter {
private ReindexResponse.Failure reindexResponseFailureOf(BulkIndexByScrollFailure failure) { private ReindexResponse.Failure reindexResponseFailureOf(BulkIndexByScrollFailure failure) {
return ReindexResponse.Failure.builder() // return ReindexResponse.Failure.builder() //
.withIndex(failure.index()) // .withIndex(failure.index()) //
.withType(failure.type()) //
.withId(failure.id()) // .withId(failure.id()) //
.withStatus(failure.status())// .withStatus(failure.status())//
.withErrorCause(toErrorCause(failure.cause())) // .withErrorCause(toErrorCause(failure.cause())) //
@ -410,12 +408,14 @@ class ResponseConverter {
private ByQueryResponse.Failure byQueryResponseFailureOf(BulkIndexByScrollFailure failure) { private ByQueryResponse.Failure byQueryResponseFailureOf(BulkIndexByScrollFailure failure) {
return ByQueryResponse.Failure.builder() // return ByQueryResponse.Failure.builder() //
.withIndex(failure.index()) // .withIndex(failure.index()) //
.withType(failure.type()) //
.withId(failure.id()) // .withId(failure.id()) //
.withStatus(failure.status())// .withStatus(failure.status())//
.withErrorCause(toErrorCause(failure.cause())).build(); .withErrorCause(toErrorCause(failure.cause())).build();
} }
public static MultiGetItem.@Nullable Failure getFailure(MultiGetResponseItem<EntityAsMap> itemResponse) { @Nullable
public static MultiGetItem.Failure getFailure(MultiGetResponseItem<EntityAsMap> itemResponse) {
MultiGetError responseFailure = itemResponse.isFailure() ? itemResponse.failure() : null; MultiGetError responseFailure = itemResponse.isFailure() ? itemResponse.failure() : null;
@ -497,10 +497,6 @@ class ResponseConverter {
builder.withDeleted(response.deleted()); builder.withDeleted(response.deleted());
} }
if(response.updated() != null) {
builder.withUpdated(response.updated());
}
if (response.batches() != null) { if (response.batches() != null) {
builder.withBatches(Math.toIntExact(response.batches())); builder.withBatches(Math.toIntExact(response.batches()));
} }
@ -535,34 +531,11 @@ class ResponseConverter {
? Script.builder() // ? Script.builder() //
.withId(response.id()) // .withId(response.id()) //
.withLanguage(response.script().lang()) // .withLanguage(response.script().lang()) //
.withSource(response.script().source().scriptString()).build() // .withSource(response.script().source()).build() //
: null; : null;
} }
// endregion // endregion
// region sql
public SqlResponse sqlResponse(QueryResponse response) {
SqlResponse.Builder builder = SqlResponse.builder();
builder.withRunning(Boolean.TRUE.equals(response.isRunning()))
.withPartial(Boolean.TRUE.equals(response.isPartial())).withCursor(response.cursor());
final List<SqlResponse.Column> columns = response.columns().stream()
.map(column -> new SqlResponse.Column(column.name(), column.type())).toList();
builder.withColumns(columns);
for (List<JsonData> rowValues : response.rows()) {
SqlResponse.Row.Builder rowBuilder = SqlResponse.Row.builder();
for (int idx = 0; idx < rowValues.size(); idx++) {
rowBuilder.withValue(columns.get(idx), rowValues.get(idx).toJson());
}
builder.withRow(rowBuilder.build());
}
return builder.build();
}
// end region
// region helper functions // region helper functions
private long timeToLong(Time time) { private long timeToLong(Time time) {

View File

@ -29,7 +29,6 @@ import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.core.search.TotalHits; import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.JsonpMapper;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -39,7 +38,6 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.SearchShardStatistics; import org.springframework.data.elasticsearch.core.SearchShardStatistics;
import org.springframework.data.elasticsearch.core.TotalHitsRelation; import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.document.SearchDocument; import org.springframework.data.elasticsearch.core.document.SearchDocument;
@ -49,6 +47,7 @@ import org.springframework.data.elasticsearch.core.suggest.response.PhraseSugges
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion; import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion;
import org.springframework.data.elasticsearch.support.ScoreDoc; import org.springframework.data.elasticsearch.support.ScoreDoc;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -57,7 +56,6 @@ import org.springframework.util.CollectionUtils;
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Haibo Liu * @author Haibo Liu
* @author Mohamed El Harrougui
* @since 4.4 * @since 4.4
*/ */
class SearchDocumentResponseBuilder { class SearchDocumentResponseBuilder {
@ -85,10 +83,8 @@ class SearchDocumentResponseBuilder {
Map<String, List<Suggestion<EntityAsMap>>> suggest = responseBody.suggest(); Map<String, List<Suggestion<EntityAsMap>>> suggest = responseBody.suggest();
var pointInTimeId = responseBody.pitId(); var pointInTimeId = responseBody.pitId();
var shards = responseBody.shards(); var shards = responseBody.shards();
var executionDurationInMillis = responseBody.took();
return from(hitsMetadata, shards, scrollId, pointInTimeId, executionDurationInMillis, aggregations, suggest, return from(hitsMetadata, shards, scrollId, pointInTimeId, aggregations, suggest, entityCreator, jsonpMapper);
entityCreator, jsonpMapper);
} }
/** /**
@ -113,10 +109,8 @@ class SearchDocumentResponseBuilder {
var aggregations = response.aggregations(); var aggregations = response.aggregations();
var suggest = response.suggest(); var suggest = response.suggest();
var pointInTimeId = response.pitId(); var pointInTimeId = response.pitId();
var executionDurationInMillis = response.took();
return from(hitsMetadata, shards, scrollId, pointInTimeId, executionDurationInMillis, aggregations, suggest, return from(hitsMetadata, shards, scrollId, pointInTimeId, aggregations, suggest, entityCreator, jsonpMapper);
entityCreator, jsonpMapper);
} }
/** /**
@ -133,8 +127,7 @@ class SearchDocumentResponseBuilder {
* @return the {@link SearchDocumentResponse} * @return the {@link SearchDocumentResponse}
*/ */
public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nullable ShardStatistics shards, public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nullable ShardStatistics shards,
@Nullable String scrollId, @Nullable String pointInTimeId, long executionDurationInMillis, @Nullable String scrollId, @Nullable String pointInTimeId, @Nullable Map<String, Aggregate> aggregations,
@Nullable Map<String, Aggregate> aggregations,
Map<String, List<Suggestion<EntityAsMap>>> suggestES, SearchDocumentResponse.EntityCreator<T> entityCreator, Map<String, List<Suggestion<EntityAsMap>>> suggestES, SearchDocumentResponse.EntityCreator<T> entityCreator,
JsonpMapper jsonpMapper) { JsonpMapper jsonpMapper) {
@ -158,8 +151,6 @@ class SearchDocumentResponseBuilder {
float maxScore = hitsMetadata.maxScore() != null ? hitsMetadata.maxScore().floatValue() : Float.NaN; float maxScore = hitsMetadata.maxScore() != null ? hitsMetadata.maxScore().floatValue() : Float.NaN;
Duration executionDuration = Duration.ofMillis(executionDurationInMillis);
List<SearchDocument> searchDocuments = new ArrayList<>(); List<SearchDocument> searchDocuments = new ArrayList<>();
for (Hit<?> hit : hitsMetadata.hits()) { for (Hit<?> hit : hitsMetadata.hits()) {
searchDocuments.add(DocumentAdapters.from(hit, jsonpMapper)); searchDocuments.add(DocumentAdapters.from(hit, jsonpMapper));
@ -172,8 +163,7 @@ class SearchDocumentResponseBuilder {
SearchShardStatistics shardStatistics = shards != null ? shardsFrom(shards) : null; SearchShardStatistics shardStatistics = shards != null ? shardsFrom(shards) : null;
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, executionDuration, scrollId, return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, pointInTimeId, searchDocuments,
pointInTimeId, searchDocuments,
aggregationsContainer, suggest, shardStatistics); aggregationsContainer, suggest, shardStatistics);
} }

View File

@ -38,7 +38,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy; import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
@ -53,6 +52,7 @@ import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.query.types.ConflictsType; import org.springframework.data.elasticsearch.core.query.types.ConflictsType;
import org.springframework.data.elasticsearch.core.query.types.OperatorType; import org.springframework.data.elasticsearch.core.query.types.OperatorType;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest; import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -220,7 +220,7 @@ final class TypeUtils {
} }
@Nullable @Nullable
static SortOrder sortOrder(Sort.@Nullable Direction direction) { static SortOrder sortOrder(@Nullable Sort.Direction direction) {
if (direction == null) { if (direction == null) {
return null; return null;
@ -301,7 +301,7 @@ final class TypeUtils {
} }
@Nullable @Nullable
static OpType opType(IndexQuery.@Nullable OpType opType) { static OpType opType(@Nullable IndexQuery.OpType opType) {
if (opType != null) { if (opType != null) {
return switch (opType) { return switch (opType) {
@ -325,7 +325,8 @@ final class TypeUtils {
}; };
} }
static UpdateResponse.@Nullable Result result(@Nullable Result result) { @Nullable
static UpdateResponse.Result result(@Nullable Result result) {
if (result == null) { if (result == null) {
return null; return null;
@ -342,7 +343,7 @@ final class TypeUtils {
} }
@Nullable @Nullable
static ScoreMode scoreMode(RescorerQuery.@Nullable ScoreMode scoreMode) { static ScoreMode scoreMode(@Nullable RescorerQuery.ScoreMode scoreMode) {
if (scoreMode == null) { if (scoreMode == null) {
return null; return null;
@ -360,7 +361,7 @@ final class TypeUtils {
} }
@Nullable @Nullable
static SearchType searchType(Query.@Nullable SearchType searchType) { static SearchType searchType(@Nullable Query.SearchType searchType) {
if (searchType == null) { if (searchType == null) {
return null; return null;
@ -417,7 +418,7 @@ final class TypeUtils {
@Nullable @Nullable
static VersionType versionType( static VersionType versionType(
org.springframework.data.elasticsearch.annotations.Document.@Nullable VersionType versionType) { @Nullable org.springframework.data.elasticsearch.annotations.Document.VersionType versionType) {
if (versionType != null) { if (versionType != null) {
return switch (versionType) { return switch (versionType) {
@ -535,7 +536,7 @@ final class TypeUtils {
* @param scoreMode spring-data-elasticsearch {@literal scoreMode}. * @param scoreMode spring-data-elasticsearch {@literal scoreMode}.
* @return an Elasticsearch {@literal scoreMode}. * @return an Elasticsearch {@literal scoreMode}.
*/ */
static ChildScoreMode scoreMode(HasChildQuery.@Nullable ScoreMode scoreMode) { static ChildScoreMode scoreMode(@Nullable HasChildQuery.ScoreMode scoreMode) {
if (scoreMode == null) { if (scoreMode == null) {
return ChildScoreMode.None; return ChildScoreMode.None;
} }

View File

@ -19,11 +19,11 @@ import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.IndexSettings; import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.elasticsearch.indices.PutMappingRequest; import co.elastic.clients.elasticsearch.indices.PutMappingRequest;
import org.springframework.aot.hint.MemberCategory;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.lang.Nullable;
/** /**
* runtime hints for the Elasticsearch client libraries, as these do not provide any of their own. * runtime hints for the Elasticsearch client libraries, as these do not provide any of their own.

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client.elc.aot; package org.springframework.data.elasticsearch.client.elc.aot;

View File

@ -18,5 +18,6 @@
* This package contains classes that use the new Elasticsearch client library (co.elastic.clients:elasticsearch-java) * This package contains classes that use the new Elasticsearch client library (co.elastic.clients:elasticsearch-java)
* to access Elasticsearch. * to access Elasticsearch.
*/ */
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;

View File

@ -1,274 +0,0 @@
package org.springframework.data.elasticsearch.client.elc.rest5_client;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.TransportUtils;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import co.elastic.clients.transport.rest5_client.low_level.Rest5ClientBuilder;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy;
import org.apache.hc.core5.util.Timeout;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.util.Assert;
/**
* Utility class containing the functions to create the Elasticsearch Rest5Client used from Elasticsearch 9 on.
*
* @since 6.0
*/
public final class Rest5Clients {
// values copied from Rest5ClientBuilder
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 1000;
public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 30000;
public static final int DEFAULT_RESPONSE_TIMEOUT_MILLIS = 0; // meaning infinite
public static final int DEFAULT_MAX_CONN_PER_ROUTE = 10;
public static final int DEFAULT_MAX_CONN_TOTAL = 30;
private Rest5Clients() {}
/**
* Creates a low level {@link Rest5Client} for the given configuration.
*
* @param clientConfiguration must not be {@literal null}
* @return the {@link Rest5Client}
*/
public static Rest5Client getRest5Client(ClientConfiguration clientConfiguration) {
return getRest5ClientBuilder(clientConfiguration).build();
}
private static Rest5ClientBuilder getRest5ClientBuilder(ClientConfiguration clientConfiguration) {
HttpHost[] httpHosts = getHttpHosts(clientConfiguration);
Rest5ClientBuilder builder = Rest5Client.builder(httpHosts);
if (clientConfiguration.getPathPrefix() != null) {
builder.setPathPrefix(clientConfiguration.getPathPrefix());
}
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
if (!headers.isEmpty()) {
builder.setDefaultHeaders(toHeaderArray(headers));
}
// we need to provide our own HttpClient, as the Rest5ClientBuilder
// does not provide a callback for configuration the http client as the old RestClientBuilder.
var httpClient = createHttpClient(clientConfiguration);
builder.setHttpClient(httpClient);
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurationCallback instanceof ElasticsearchRest5ClientConfigurationCallback configurationCallback) {
builder = configurationCallback.configure(builder);
}
}
return builder;
}
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
boolean useSsl = clientConfiguration.useSsl();
return hosts.stream()
.map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
.map(URI::create)
.map(HttpHost::create)
.toArray(HttpHost[]::new);
}
private static Header[] toHeaderArray(HttpHeaders headers) {
return headers.entrySet().stream() //
.flatMap(entry -> entry.getValue().stream() //
.map(value -> new BasicHeader(entry.getKey(), value))) //
.toList().toArray(new Header[0]);
}
// the basic logic to create the http client is copied from the Rest5ClientBuilder class, this is taken from the
// Elasticsearch code, as there is no public usable instance in that
private static CloseableHttpAsyncClient createHttpClient(ClientConfiguration clientConfiguration) {
var requestConfigBuilder = RequestConfig.custom();
var connectionConfigBuilder = ConnectionConfig.custom();
Duration connectTimeout = clientConfiguration.getConnectTimeout();
if (!connectTimeout.isNegative()) {
connectionConfigBuilder.setConnectTimeout(
Timeout.of(Math.toIntExact(connectTimeout.toMillis()), TimeUnit.MILLISECONDS));
}
Duration socketTimeout = clientConfiguration.getSocketTimeout();
if (!socketTimeout.isNegative()) {
var soTimeout = Timeout.of(Math.toIntExact(socketTimeout.toMillis()), TimeUnit.MILLISECONDS);
connectionConfigBuilder.setSocketTimeout(soTimeout);
requestConfigBuilder.setConnectionRequestTimeout(soTimeout);
} else {
connectionConfigBuilder.setSocketTimeout(Timeout.of(DEFAULT_SOCKET_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
requestConfigBuilder
.setConnectionRequestTimeout(Timeout.of(DEFAULT_RESPONSE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
}
try {
SSLContext sslContext = clientConfiguration.getCaFingerprint().isPresent()
? TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get())
: (clientConfiguration.getSslContext().isPresent()
? clientConfiguration.getSslContext().get()
: SSLContext.getDefault());
ConnectionConfig connectionConfig = connectionConfigBuilder.build();
PoolingAsyncClientConnectionManager defaultConnectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultConnectionConfig(connectionConfig)
.setMaxConnPerRoute(DEFAULT_MAX_CONN_PER_ROUTE)
.setMaxConnTotal(DEFAULT_MAX_CONN_TOTAL)
.setTlsStrategy(new BasicClientTlsStrategy(sslContext))
.build();
var requestConfig = requestConfigBuilder.build();
var immutableRefToHttpClientBuilder = new Object() {
HttpAsyncClientBuilder httpClientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(defaultConnectionManager)
.setUserAgent(VersionInfo.clientVersions())
.setTargetAuthenticationStrategy(new DefaultAuthenticationStrategy())
.setThreadFactory(new RestClientThreadFactory());
};
clientConfiguration.getProxy().ifPresent(proxy -> {
try {
var proxyRoutePlanner = new DefaultProxyRoutePlanner(HttpHost.create(proxy));
immutableRefToHttpClientBuilder.httpClientBuilder.setRoutePlanner(proxyRoutePlanner);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
});
immutableRefToHttpClientBuilder.httpClientBuilder.addRequestInterceptorFirst((request, entity, context) -> {
clientConfiguration.getHeadersSupplier().get().forEach((header, values) -> {
// The accept and content-type headers are already put on the request, despite this being the first
// interceptor.
if ("Accept".equalsIgnoreCase(header) || " Content-Type".equalsIgnoreCase(header)) {
request.removeHeaders(header);
}
values.forEach(value -> request.addHeader(header, value));
});
});
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof ElasticsearchHttpClientConfigurationCallback httpClientConfigurer) {
immutableRefToHttpClientBuilder.httpClientBuilder = httpClientConfigurer.configure(immutableRefToHttpClientBuilder.httpClientBuilder);
}
}
return immutableRefToHttpClientBuilder.httpClientBuilder.build();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("could not create the default ssl context", e);
}
}
/*
* Copied from the Elasticsearch code as this class is not public there.
*/
private static class RestClientThreadFactory implements ThreadFactory {
private static final AtomicLong CLIENT_THREAD_POOL_ID_GENERATOR = new AtomicLong();
private final long clientThreadPoolId;
private final AtomicLong clientThreadId;
private RestClientThreadFactory() {
this.clientThreadPoolId = CLIENT_THREAD_POOL_ID_GENERATOR.getAndIncrement();
this.clientThreadId = new AtomicLong();
}
public Thread newThread(Runnable runnable) {
return new Thread(runnable, String.format(Locale.ROOT, "elasticsearch-rest-client-%d-thread-%d",
this.clientThreadPoolId, this.clientThreadId.incrementAndGet()));
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the Elasticsearch Rest5Client's Http client with a {@link HttpAsyncClientBuilder}
*
* @since 6.0
*/
public interface ElasticsearchHttpClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static Rest5Clients.ElasticsearchHttpClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
return httpClientBuilderCallback::apply;
}
}
/**
* {@link ClientConfiguration.ClientConfigurationCallback} to configure the Rest5Client client with a
* {@link Rest5ClientBuilder}
*
* @since 6.0
*/
public interface ElasticsearchRest5ClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<Rest5ClientBuilder> {
static ElasticsearchRest5ClientConfigurationCallback from(
Function<Rest5ClientBuilder, Rest5ClientBuilder> rest5ClientBuilderCallback) {
Assert.notNull(rest5ClientBuilderCallback, "rest5ClientBuilderCallback must not be null");
return rest5ClientBuilderCallback::apply;
}
}
public static Rest5ClientOptions.Builder getRest5ClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
if (transportOptions instanceof Rest5ClientOptions rest5ClientOptions) {
return rest5ClientOptions.toBuilder();
}
var builder = new Rest5ClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
if (transportOptions != null) {
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
transportOptions.queryParameters().forEach(builder::setParameter);
builder.onWarnings(transportOptions.onWarnings());
}
return builder;
}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This package contains related to the new (from Elasticsearch 9 on) Rest5Client. There are also classes copied over from Elasticsearch in order to have a Rest5ClientBuilder that allows to configure the http client.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.client.elc.rest5_client;

View File

@ -1,195 +0,0 @@
package org.springframework.data.elasticsearch.client.elc.rest_client;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.TransportUtils;
import co.elastic.clients.transport.rest_client.RestClientOptions;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
/**
* Utility class containing the functions to create the Elasticsearch RestClient used up to Elasticsearch 9.
*
* @since 6.0
* @deprecated since 6.0, use the new Rest5Client the code for that is in the package ../rest_client.
*/
@Deprecated(since = "6.0", forRemoval = true)
public final class RestClients {
/**
* Creates a low level {@link RestClient} for the given configuration.
*
* @param clientConfiguration must not be {@literal null}
* @return the {@link RestClient}
*/
public static RestClient getRestClient(ClientConfiguration clientConfiguration) {
return getRestClientBuilder(clientConfiguration).build();
}
private static RestClientBuilder getRestClientBuilder(ClientConfiguration clientConfiguration) {
HttpHost[] httpHosts = getHttpHosts(clientConfiguration);
RestClientBuilder builder = RestClient.builder(httpHosts);
if (clientConfiguration.getPathPrefix() != null) {
builder.setPathPrefix(clientConfiguration.getPathPrefix());
}
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
if (!headers.isEmpty()) {
builder.setDefaultHeaders(toHeaderArray(headers));
}
builder.setHttpClientConfigCallback(clientBuilder -> {
if (clientConfiguration.getCaFingerprint().isPresent()) {
clientBuilder
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
}
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
Duration connectTimeout = clientConfiguration.getConnectTimeout();
if (!connectTimeout.isNegative()) {
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
}
Duration socketTimeout = clientConfiguration.getSocketTimeout();
if (!socketTimeout.isNegative()) {
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
}
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof RestClients.ElasticsearchHttpClientConfigurationCallback restClientConfigurationCallback) {
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
}
}
return clientBuilder;
});
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurationCallback instanceof ElasticsearchRestClientConfigurationCallback configurationCallback) {
builder = configurationCallback.configure(builder);
}
}
return builder;
}
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
boolean useSsl = clientConfiguration.useSsl();
return hosts.stream()
.map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
.map(HttpHost::create).toArray(HttpHost[]::new);
}
private static org.apache.http.Header[] toHeaderArray(HttpHeaders headers) {
return headers.entrySet().stream() //
.flatMap(entry -> entry.getValue().stream() //
.map(value -> new BasicHeader(entry.getKey(), value))) //
.toArray(org.apache.http.Header[]::new);
}
/**
* Interceptor to inject custom supplied headers.
*
* @since 4.4
*/
record CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) implements HttpRequestInterceptor {
@Override
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
*
* @since 4.4
*/
public interface ElasticsearchHttpClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static RestClients.ElasticsearchHttpClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
return httpClientBuilderCallback::apply;
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the RestClient client with a {@link RestClientBuilder}
*
* @since 5.0
*/
public interface ElasticsearchRestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
static ElasticsearchRestClientConfigurationCallback from(
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
return restClientBuilderCallback::apply;
}
}
public static RestClientOptions.Builder getRestClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
if (transportOptions instanceof RestClientOptions restClientOptions) {
return restClientOptions.toBuilder();
}
var builder = new RestClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
if (transportOptions != null) {
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
transportOptions.queryParameters().forEach(builder::setParameter);
builder.onWarnings(transportOptions.onWarnings());
}
return builder;
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2022-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This package contains related to the old (up to Elasticsearch 9) RestClient.
*/
@Deprecated(since = "6.0", forRemoval=true)
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.client.elc.rest_client;

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client; package org.springframework.data.elasticsearch.client;

View File

@ -19,7 +19,7 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.client.util; package org.springframework.data.elasticsearch.client.util;

View File

@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.config;
import static org.springframework.data.config.ParsingUtils.*; import static org.springframework.data.config.ParsingUtils.*;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@ -29,6 +28,7 @@ import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDef
import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback; import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback;
import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback; import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.w3c.dom.Element; import org.w3c.dom.Element;

View File

@ -20,7 +20,6 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@ -35,6 +34,7 @@ import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchC
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;

View File

@ -1,2 +1,3 @@
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.config; package org.springframework.data.elasticsearch.config;

View File

@ -24,7 +24,6 @@ import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
@ -58,6 +57,7 @@ import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.Streamable; import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -298,6 +298,12 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return this.delete(id, getIndexCoordinatesFor(entityType)); return this.delete(id, getIndexCoordinatesFor(entityType));
} }
@Override
@Deprecated
public ByQueryResponse delete(Query query, Class<?> clazz) {
return delete(query, clazz, getIndexCoordinatesFor(clazz));
}
@Override @Override
public String delete(Object entity) { public String delete(Object entity) {
return delete(entity, getIndexCoordinatesFor(entity.getClass())); return delete(entity, getIndexCoordinatesFor(entity.getClass()));

View File

@ -26,7 +26,6 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Subscriber; import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription; import org.reactivestreams.Subscription;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -57,6 +56,7 @@ import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.support.VersionInfo; import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -233,7 +233,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
.subscribe(new Subscriber<>() { .subscribe(new Subscriber<>() {
@Nullable private Subscription subscription = null; @Nullable private Subscription subscription = null;
private final AtomicBoolean upstreamComplete = new AtomicBoolean(false); private final AtomicBoolean upstreamComplete = new AtomicBoolean(false);
private final AtomicBoolean onNextHasBeenCalled = new AtomicBoolean(false);
@Override @Override
public void onSubscribe(Subscription subscription) { public void onSubscribe(Subscription subscription) {
@ -243,7 +242,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
@Override @Override
public void onNext(List<T> entityList) { public void onNext(List<T> entityList) {
onNextHasBeenCalled.set(true);
saveAll(entityList, index) saveAll(entityList, index)
.map(sink::tryEmitNext) .map(sink::tryEmitNext)
.doOnComplete(() -> { .doOnComplete(() -> {
@ -269,10 +267,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
@Override @Override
public void onComplete() { public void onComplete() {
upstreamComplete.set(true); upstreamComplete.set(true);
if (!onNextHasBeenCalled.get()) {
// this happens when an empty flux is saved
sink.tryEmitComplete();
}
} }
}); });
return sink.asFlux(); return sink.asFlux();
@ -414,6 +408,12 @@ abstract public class AbstractReactiveElasticsearchTemplate
abstract protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index); abstract protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index);
@Override
@Deprecated
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType) {
return delete(query, entityType, getIndexCoordinatesFor(entityType));
}
@Override @Override
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType) { public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType) {
return delete(query, entityType, getIndexCoordinatesFor(entityType)); return delete(query, entityType, getIndexCoordinatesFor(entityType));

View File

@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.core;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions; import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse; import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
@ -29,6 +28,7 @@ import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse; import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest; import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse; import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.lang.Nullable;
/** /**
* The operations for the * The operations for the
@ -272,6 +272,19 @@ public interface DocumentOperations {
*/ */
String delete(Object entity, IndexCoordinates index); String delete(Object entity, IndexCoordinates index);
/**
* Delete all records matching the query.
*
* @param query query defining the objects
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @return response with detailed information
* @since 4.1
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
*/
@Deprecated
ByQueryResponse delete(Query query, Class<?> clazz);
/** /**
* Delete all records matching the query. * Delete all records matching the query.
* *
@ -283,6 +296,19 @@ public interface DocumentOperations {
*/ */
ByQueryResponse delete(DeleteQuery query, Class<?> clazz); ByQueryResponse delete(DeleteQuery query, Class<?> clazz);
/**
* Delete all records matching the query.
*
* @param query query defining the objects
* @param clazz The entity class, must be annotated with
* {@link org.springframework.data.elasticsearch.annotations.Document}
* @param index the index from which to delete
* @return response with detailed information
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
*/
@Deprecated
ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index);
/** /**
* Delete all records matching the query. * Delete all records matching the query.
* *

View File

@ -15,13 +15,12 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations; import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.routing.RoutingResolver; import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
import org.springframework.data.elasticsearch.core.script.ScriptOperations; import org.springframework.data.elasticsearch.core.script.ScriptOperations;
import org.springframework.data.elasticsearch.core.sql.SqlOperations; import org.springframework.lang.Nullable;
/** /**
* ElasticsearchOperations. Since 4.0 this interface only contains common helper functions, the other methods have been * ElasticsearchOperations. Since 4.0 this interface only contains common helper functions, the other methods have been
@ -36,7 +35,7 @@ import org.springframework.data.elasticsearch.core.sql.SqlOperations;
* @author Dmitriy Yakovlev * @author Dmitriy Yakovlev
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
*/ */
public interface ElasticsearchOperations extends DocumentOperations, SearchOperations, ScriptOperations, SqlOperations { public interface ElasticsearchOperations extends DocumentOperations, SearchOperations, ScriptOperations {
/** /**
* get an {@link IndexOperations} that is bound to the given class * get an {@link IndexOperations} that is bound to the given class

View File

@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.join.JoinField; import org.springframework.data.elasticsearch.core.join.JoinField;
@ -30,6 +29,7 @@ import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -18,10 +18,10 @@ package org.springframework.data.elasticsearch.core;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasData; import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.index.Settings; import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.lang.Nullable;
/** /**
* Immutable object that holds information(name, settings, mappings, aliases) about an Index * Immutable object that holds information(name, settings, mappings, aliases) about an Index

View File

@ -19,10 +19,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*; import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
/** /**
* The operations for the * The operations for the

View File

@ -22,10 +22,10 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*; import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -15,7 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* Value class capturing information about a newly indexed document in Elasticsearch. * Value class capturing information about a newly indexed document in Elasticsearch.

View File

@ -15,8 +15,8 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.ElasticsearchErrorCause; import org.springframework.data.elasticsearch.ElasticsearchErrorCause;
import org.springframework.lang.Nullable;
/** /**
* Response object for items returned from multiget requests, encapsulating the returned data and potential error * Response object for items returned from multiget requests, encapsulating the returned data and potential error

View File

@ -326,6 +326,17 @@ public interface ReactiveDocumentOperations {
*/ */
Mono<String> delete(String id, Class<?> entityType); Mono<String> delete(String id, Class<?> entityType);
/**
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @return a {@link Mono} emitting the number of the removed documents.
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
*/
@Deprecated
Mono<ByQueryResponse> delete(Query query, Class<?> entityType);
/** /**
* Delete the documents matching the given {@link Query} extracting index from entity metadata. * Delete the documents matching the given {@link Query} extracting index from entity metadata.
* *
@ -336,6 +347,18 @@ public interface ReactiveDocumentOperations {
*/ */
Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType); Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType);
/**
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
*
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
* @return a {@link Mono} emitting the number of the removed documents.
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
*/
@Deprecated
Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index);
/** /**
* Delete the documents matching the given {@link Query} extracting index from entity metadata. * Delete the documents matching the given {@link Query} extracting index from entity metadata.
* *

View File

@ -15,14 +15,13 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations; import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.routing.RoutingResolver; import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
import org.springframework.data.elasticsearch.core.script.ReactiveScriptOperations; import org.springframework.data.elasticsearch.core.script.ReactiveScriptOperations;
import org.springframework.data.elasticsearch.core.sql.ReactiveSqlOperations; import org.springframework.lang.Nullable;
/** /**
* Interface that specifies a basic set of Elasticsearch operations executed in a reactive way. * Interface that specifies a basic set of Elasticsearch operations executed in a reactive way.
@ -32,7 +31,7 @@ import org.springframework.data.elasticsearch.core.sql.ReactiveSqlOperations;
* @since 3.2 * @since 3.2
*/ */
public interface ReactiveElasticsearchOperations public interface ReactiveElasticsearchOperations
extends ReactiveDocumentOperations, ReactiveSearchOperations, ReactiveScriptOperations, ReactiveSqlOperations { extends ReactiveDocumentOperations, ReactiveSearchOperations, ReactiveScriptOperations {
/** /**
* Get the {@link ElasticsearchConverter} used. * Get the {@link ElasticsearchConverter} used.

View File

@ -17,17 +17,14 @@ package org.springframework.data.elasticsearch.core;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import java.time.Duration;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.lang.Nullable;
/** /**
* Encapsulates a Flux of {@link SearchHit}s with additional information from the search. * Encapsulates a Flux of {@link SearchHit}s with additional information from the search.
* *
* @param <T> the result data class. * @param <T> the result data class.
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Mohamed El Harrougui
* @since 4.4 * @since 4.4
*/ */
public interface ReactiveSearchHits<T> { public interface ReactiveSearchHits<T> {
@ -40,11 +37,6 @@ public interface ReactiveSearchHits<T> {
float getMaxScore(); float getMaxScore();
/**
* @return the execution duration it took to complete the request
*/
Duration getExecutionDuration();
/** /**
* @return the {@link SearchHit}s from the search result. * @return the {@link SearchHit}s from the search result.
*/ */

View File

@ -17,14 +17,11 @@ package org.springframework.data.elasticsearch.core;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import java.time.Duration;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.lang.Nullable;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Mohamed El Harrougui
* @since 4.4 * @since 4.4
*/ */
public class ReactiveSearchHitsImpl<T> implements ReactiveSearchHits<T> { public class ReactiveSearchHitsImpl<T> implements ReactiveSearchHits<T> {
@ -61,11 +58,6 @@ public class ReactiveSearchHitsImpl<T> implements ReactiveSearchHits<T> {
return delegate.getMaxScore(); return delegate.getMaxScore();
} }
@Override
public Duration getExecutionDuration() {
return delegate.getExecutionDuration();
}
@Override @Override
public boolean hasSearchHits() { public boolean hasSearchHits() {
return delegate.hasSearchHits(); return delegate.hasSearchHits();

View File

@ -23,9 +23,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.document.Explanation; import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData; import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -48,12 +48,12 @@ public class SearchHit<T> {
@Nullable private final NestedMetaData nestedMetaData; @Nullable private final NestedMetaData nestedMetaData;
@Nullable private final String routing; @Nullable private final String routing;
@Nullable private final Explanation explanation; @Nullable private final Explanation explanation;
private final Map<String, Double> matchedQueries = new LinkedHashMap<>(); private final List<String> matchedQueries = new ArrayList<>();
public SearchHit(@Nullable String index, @Nullable String id, @Nullable String routing, float score, public SearchHit(@Nullable String index, @Nullable String id, @Nullable String routing, float score,
@Nullable Object[] sortValues, @Nullable Map<String, List<String>> highlightFields, @Nullable Object[] sortValues, @Nullable Map<String, List<String>> highlightFields,
@Nullable Map<String, SearchHits<?>> innerHits, @Nullable NestedMetaData nestedMetaData, @Nullable Map<String, SearchHits<?>> innerHits, @Nullable NestedMetaData nestedMetaData,
@Nullable Explanation explanation, @Nullable Map<String, Double> matchedQueries, T content) { @Nullable Explanation explanation, @Nullable List<String> matchedQueries, T content) {
this.index = index; this.index = index;
this.id = id; this.id = id;
this.routing = routing; this.routing = routing;
@ -73,7 +73,7 @@ public class SearchHit<T> {
this.content = content; this.content = content;
if (matchedQueries != null) { if (matchedQueries != null) {
this.matchedQueries.putAll(matchedQueries); this.matchedQueries.addAll(matchedQueries);
} }
} }
@ -194,7 +194,7 @@ public class SearchHit<T> {
* @return the matched queries for this SearchHit. * @return the matched queries for this SearchHit.
*/ */
@Nullable @Nullable
public Map<String, Double> getMatchedQueries() { public List<String> getMatchedQueries() {
return matchedQueries; return matchedQueries;
} }
} }

View File

@ -15,7 +15,6 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
@ -23,7 +22,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
@ -35,6 +33,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion; import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -48,7 +47,6 @@ import org.springframework.util.Assert;
* @author Sascha Woo * @author Sascha Woo
* @author Jakob Hoeper * @author Jakob Hoeper
* @author Haibo Liu * @author Haibo Liu
* @author Mohamed El Harrougui
* @since 4.0 * @since 4.0
*/ */
public class SearchHitMapping<T> { public class SearchHitMapping<T> {
@ -89,7 +87,6 @@ public class SearchHitMapping<T> {
long totalHits = searchDocumentResponse.getTotalHits(); long totalHits = searchDocumentResponse.getTotalHits();
SearchShardStatistics shardStatistics = searchDocumentResponse.getSearchShardStatistics(); SearchShardStatistics shardStatistics = searchDocumentResponse.getSearchShardStatistics();
float maxScore = searchDocumentResponse.getMaxScore(); float maxScore = searchDocumentResponse.getMaxScore();
Duration executionDuration = searchDocumentResponse.getExecutionDuration();
String scrollId = searchDocumentResponse.getScrollId(); String scrollId = searchDocumentResponse.getScrollId();
String pointInTimeId = searchDocumentResponse.getPointInTimeId(); String pointInTimeId = searchDocumentResponse.getPointInTimeId();
@ -107,8 +104,8 @@ public class SearchHitMapping<T> {
Suggest suggest = searchDocumentResponse.getSuggest(); Suggest suggest = searchDocumentResponse.getSuggest();
mapHitsInCompletionSuggestion(suggest); mapHitsInCompletionSuggestion(suggest);
return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, executionDuration, scrollId, pointInTimeId, return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, scrollId, pointInTimeId, searchHits,
searchHits, aggregations, suggest, shardStatistics); aggregations, suggest, shardStatistics);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -241,7 +238,6 @@ public class SearchHitMapping<T> {
return new SearchHitsImpl<>(searchHits.getTotalHits(), return new SearchHitsImpl<>(searchHits.getTotalHits(),
searchHits.getTotalHitsRelation(), searchHits.getTotalHitsRelation(),
searchHits.getMaxScore(), searchHits.getMaxScore(),
searchHits.getExecutionDuration(),
scrollId, scrollId,
searchHits.getPointInTimeId(), searchHits.getPointInTimeId(),
convertedSearchHits, convertedSearchHits,

View File

@ -21,11 +21,11 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.ReactiveWrappers; import org.springframework.data.util.ReactiveWrappers;
import org.springframework.lang.Nullable;
/** /**
* Utility class with helper methods for working with {@link SearchHit}. * Utility class with helper methods for working with {@link SearchHit}.

View File

@ -15,13 +15,12 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import java.time.Duration;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.util.Streamable; import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
/** /**
* Encapsulates a list of {@link SearchHit}s with additional information from the search. * Encapsulates a list of {@link SearchHit}s with additional information from the search.
@ -29,7 +28,6 @@ import org.springframework.data.util.Streamable;
* @param <T> the result data class. * @param <T> the result data class.
* @author Sascha Woo * @author Sascha Woo
* @author Haibo Liu * @author Haibo Liu
* @author Mohamed El Harrougui
* @since 4.0 * @since 4.0
*/ */
public interface SearchHits<T> extends Streamable<SearchHit<T>> { public interface SearchHits<T> extends Streamable<SearchHit<T>> {
@ -45,11 +43,6 @@ public interface SearchHits<T> extends Streamable<SearchHit<T>> {
*/ */
float getMaxScore(); float getMaxScore();
/**
* @return the execution duration it took to complete the request
*/
Duration getExecutionDuration();
/** /**
* @param index position in List. * @param index position in List.
* @return the {@link SearchHit} at position {index} * @return the {@link SearchHit} at position {index}

View File

@ -15,13 +15,12 @@
*/ */
package org.springframework.data.elasticsearch.core; package org.springframework.data.elasticsearch.core;
import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.util.Lazy; import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -31,7 +30,6 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo * @author Sascha Woo
* @author Haibo Liu * @author Haibo Liu
* @author Mohamed El Harrougui
* @since 4.0 * @since 4.0
*/ */
public class SearchHitsImpl<T> implements SearchScrollHits<T> { public class SearchHitsImpl<T> implements SearchScrollHits<T> {
@ -39,7 +37,6 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
private final long totalHits; private final long totalHits;
private final TotalHitsRelation totalHitsRelation; private final TotalHitsRelation totalHitsRelation;
private final float maxScore; private final float maxScore;
private final Duration executionDuration;
@Nullable private final String scrollId; @Nullable private final String scrollId;
private final List<? extends SearchHit<T>> searchHits; private final List<? extends SearchHit<T>> searchHits;
private final Lazy<List<SearchHit<T>>> unmodifiableSearchHits; private final Lazy<List<SearchHit<T>>> unmodifiableSearchHits;
@ -52,13 +49,12 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
* @param totalHits the number of total hits for the search * @param totalHits the number of total hits for the search
* @param totalHitsRelation the relation {@see TotalHitsRelation}, must not be {@literal null} * @param totalHitsRelation the relation {@see TotalHitsRelation}, must not be {@literal null}
* @param maxScore the maximum score * @param maxScore the maximum score
* @param executionDuration the execution duration it took to complete the request
* @param scrollId the scroll id if available * @param scrollId the scroll id if available
* @param searchHits must not be {@literal null} * @param searchHits must not be {@literal null}
* @param aggregations the aggregations if available * @param aggregations the aggregations if available
*/ */
public SearchHitsImpl(long totalHits, TotalHitsRelation totalHitsRelation, float maxScore, Duration executionDuration, public SearchHitsImpl(long totalHits, TotalHitsRelation totalHitsRelation, float maxScore, @Nullable String scrollId,
@Nullable String scrollId, @Nullable String pointInTimeId, List<? extends SearchHit<T>> searchHits, @Nullable String pointInTimeId, List<? extends SearchHit<T>> searchHits,
@Nullable AggregationsContainer<?> aggregations, @Nullable Suggest suggest, @Nullable AggregationsContainer<?> aggregations, @Nullable Suggest suggest,
@Nullable SearchShardStatistics searchShardStatistics) { @Nullable SearchShardStatistics searchShardStatistics) {
@ -67,7 +63,6 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
this.totalHits = totalHits; this.totalHits = totalHits;
this.totalHitsRelation = totalHitsRelation; this.totalHitsRelation = totalHitsRelation;
this.maxScore = maxScore; this.maxScore = maxScore;
this.executionDuration = executionDuration;
this.scrollId = scrollId; this.scrollId = scrollId;
this.pointInTimeId = pointInTimeId; this.pointInTimeId = pointInTimeId;
this.searchHits = searchHits; this.searchHits = searchHits;
@ -93,11 +88,6 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
return maxScore; return maxScore;
} }
@Override
public Duration getExecutionDuration() {
return executionDuration;
}
@Override @Override
@Nullable @Nullable
public String getScrollId() { public String getScrollId() {
@ -143,7 +133,6 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
"totalHits=" + totalHits + // "totalHits=" + totalHits + //
", totalHitsRelation=" + totalHitsRelation + // ", totalHitsRelation=" + totalHitsRelation + //
", maxScore=" + maxScore + // ", maxScore=" + maxScore + //
", executionDuration=" + executionDuration + //
", scrollId='" + scrollId + '\'' + // ", scrollId='" + scrollId + '\'' + //
", pointInTimeId='" + pointInTimeId + '\'' + // ", pointInTimeId='" + pointInTimeId + '\'' + //
", searchHits={" + searchHits.size() + " elements}" + // ", searchHits={" + searchHits.size() + " elements}" + //

Some files were not shown because too many files have changed in this diff Show More