Compare commits

..

12 Commits
main ... 5.4.2

Author SHA1 Message Date
Mark Paluch
60b71dcb11
Release version 5.4.2 (2024.1.2).
See #3024
2025-01-17 11:37:36 +01:00
Mark Paluch
33a149ae2d
Prepare 5.4.2 (2024.1.2).
See #3024
2025-01-17 11:37:19 +01:00
Peter-Josef Meisch
29158f9097
Update copyright notices to 2025.
Original Pull Request #3035
Closes #3033
2025-01-03 08:44:05 +01:00
Alfonso
5d52918a5c
fix: use scripted field name to populate entity.
Original Pull Request: #3023
Closes: #3022

(cherry picked from commit 944e7e81ddf0482ea996be32253c9cbfd65d1e4f)
2024-12-14 18:51:27 +01:00
Christoph Strobl
457c9b71b3
After release cleanups.
See #3004
2024-12-13 09:38:13 +01:00
Christoph Strobl
18b91d5da2
Prepare next development iteration.
See #3004
2024-12-13 09:38:11 +01:00
Christoph Strobl
62091be997
Release version 5.4.1 (2024.1.1).
See #3004
2024-12-13 09:34:47 +01:00
Christoph Strobl
fdc7893817
Prepare 5.4.1 (2024.1.1).
See #3004
2024-12-13 09:34:27 +01:00
Peter-Josef Meisch
535d76faf0
Upgrade Elasticsearch to 8.15.5.
Original Pull Request #3018
Closes #3016
2024-12-01 11:59:53 +01:00
Peter-Josef Meisch
26bd770b8c
Update versions documentation.
Original Pull request #3011 
Closes #3010
2024-11-23 22:46:46 +01:00
Mark Paluch
aec03a3529
After release cleanups.
See #2990
2024-11-15 14:13:26 +01:00
Mark Paluch
e3b26b2268
Prepare next development iteration.
See #2990
2024-11-15 14:13:25 +01:00
348 changed files with 1760 additions and 3355 deletions

1
.gitignore vendored
View File

@ -33,4 +33,3 @@ node
package-lock.json package-lock.json
.mvn/.develocity .mvn/.develocity
/src/test/resources/testcontainers-local.properties

View File

@ -3,6 +3,6 @@
<extension> <extension>
<groupId>io.spring.develocity.conventions</groupId> <groupId>io.spring.develocity.conventions</groupId>
<artifactId>develocity-conventions-maven-extension</artifactId> <artifactId>develocity-conventions-maven-extension</artifactId>
<version>0.0.22</version> <version>0.0.19</version>
</extension> </extension>
</extensions> </extensions>

View File

@ -1,14 +0,0 @@
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=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.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

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.4.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,25 @@
# 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=23.0.1_11-jdk-noble
# 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
docker.mongodb.8.0.version=8.0.0
# 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

43
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.4.2</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.4.2</version>
</parent> </parent>
<name>Spring Data Elasticsearch</name> <name>Spring Data Elasticsearch</name>
@ -18,10 +18,10 @@
<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.4.2</springdata.commons>
<!-- version of the ElasticsearchClient --> <!-- version of the ElasticsearchClient -->
<elasticsearch-java>9.0.1</elasticsearch-java> <elasticsearch-java>8.15.5</elasticsearch-java>
<hoverfly>0.19.0</hoverfly> <hoverfly>0.19.0</hoverfly>
<log4j>2.23.1</log4j> <log4j>2.23.1</log4j>
@ -132,25 +132,6 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>${elasticsearch-java}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>${querydsl}</version>
<optional>true</optional>
</dependency>
<!-- Jackson JSON Mapper --> <!-- Jackson JSON Mapper -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@ -469,20 +450,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

@ -11,8 +11,6 @@
*** 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.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[]

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
@ -81,5 +81,5 @@ class MyConfiguration {
} }
---- ----
If your code contains more than one `AuditorAware` bean for different types, you must provide the name of the bean to use as an argument to the `auditorAwareRef` parameter of the If your code contains more than one `AuditorAware` bean for different types, you must provide the name of the bean to use as an argument to the `auditorAwareRef` parameter of the
`@EnableElasticsearchAuditing` annotation. `@EnableElasticsearchAuditing` annotation.

View File

@ -1,20 +1,9 @@
[[new-features]] [[new-features]]
= What's new = What's new
[[new-features.6-0-0]] [[new-features.5-4-1]]
== New in Spring Data Elasticsearch 6.6 == New in Spring Data Elasticsearch 5.4.1
* Upgrade to Elasticsearch 8.15.5.
* Upgarde to Spring 7
* Switch to jspecify nullability annotations
* Upgrade to Elasticsearch 9.0.1
[[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-features.5-4-0]]
== New in Spring Data Elasticsearch 5.4 == New in Spring Data Elasticsearch 5.4

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

@ -6,11 +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.1 | 7.0.x | 2024.1 | 5.4.x | 8.15.5 | 6.2.x
| 2025.0 | 5.5.x | 8.18.1 | 6.2.x | 2024.0 | 5.3.1 | 8.13.4 | 6.1.x
| 2024.1 | 5.4.x | 8.15.5 | 6.1.x | 2023.1 (Vaughan) | 5.2.xfootnote:oom[Out of maintenance] | 8.11.1 | 6.1.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 | 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

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,21 +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
[[elasticsearch-migration-guide-5.5-6.0.deprecations]]
== Deprecations
=== 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,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.

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;
/** /**

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,6 +32,18 @@ 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}
@ -54,188 +55,187 @@ import java.util.stream.Collectors;
*/ */
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.
* *
* @param hit the hit object * @param hit the hit object
* @param jsonpMapper to map JsonData objects * @param jsonpMapper to map JsonData objects
* @return the created {@link SearchDocument} * @return the created {@link SearchDocument}
*/ */
public static SearchDocument from(Hit<?> hit, JsonpMapper jsonpMapper) { public static SearchDocument from(Hit<?> hit, JsonpMapper jsonpMapper) {
Assert.notNull(hit, "hit must not be null"); Assert.notNull(hit, "hit must not be null");
Map<String, List<String>> highlightFields = hit.highlight(); Map<String, List<String>> highlightFields = hit.highlight();
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, 0, null, null,
searchDocument -> null, jsonpMapper)); searchDocument -> null, jsonpMapper));
}); });
NestedMetaData nestedMetaData = from(hit.nested()); NestedMetaData nestedMetaData = from(hit.nested());
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(',');
} }
sb.append('"').append(key).append("\":") // sb.append('"').append(key).append("\":") //
.append(jsonData.toJson(jsonpMapper).toString()); .append(jsonData.toJson(jsonpMapper).toString());
firstField[0] = false; firstField[0] = false;
}); });
sb.append('}'); sb.append('}');
return new EntityAsMap().fromJson(sb.toString()); return new EntityAsMap().fromJson(sb.toString());
}; };
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields()); EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
Map<String, List<Object>> documentFields = new LinkedHashMap<>(); Map<String, List<Object>> documentFields = new LinkedHashMap<>();
hitFieldsAsMap.forEach((key, value) -> { hitFieldsAsMap.forEach((key, value) -> {
if (value instanceof List) { if (value instanceof List) {
// noinspection unchecked // noinspection unchecked
documentFields.put(key, (List<Object>) value); documentFields.put(key, (List<Object>) value);
} else { } else {
documentFields.put(key, Collections.singletonList(value)); documentFields.put(key, Collections.singletonList(value));
} }
}); });
Document document; Document document;
Object source = hit.source(); Object source = hit.source();
if (source == null) { if (source == null) {
document = Document.from(hitFieldsAsMap); document = Document.from(hitFieldsAsMap);
} else { } else {
if (source instanceof EntityAsMap entityAsMap) { if (source instanceof EntityAsMap entityAsMap) {
document = Document.from(entityAsMap); document = Document.from(entityAsMap);
} else if (source instanceof JsonData jsonData) { } else if (source instanceof JsonData jsonData) {
document = Document.from(jsonData.to(EntityAsMap.class)); document = Document.from(jsonData.to(EntityAsMap.class));
} else { } else {
if (LOGGER.isWarnEnabled()) { if (LOGGER.isWarnEnabled()) {
LOGGER.warn(String.format("Cannot map from type " + source.getClass().getName())); LOGGER.warn(String.format("Cannot map from type " + source.getClass().getName()));
} }
document = Document.create(); document = Document.create();
} }
} }
document.setIndex(hit.index()); document.setIndex(hit.index());
document.setId(hit.id()); document.setId(hit.id());
if (hit.version() != null) { if (hit.version() != null) {
document.setVersion(hit.version()); document.setVersion(hit.version());
} }
document.setSeqNo(hit.seqNo() != null && hit.seqNo() >= 0 ? hit.seqNo() : -2); // -2 was the default value in the document.setSeqNo(hit.seqNo() != null && hit.seqNo() >= 0 ? hit.seqNo() : -2); // -2 was the default value in the
// old client // old client
document.setPrimaryTerm(hit.primaryTerm() != null && hit.primaryTerm() > 0 ? hit.primaryTerm() : 0); document.setPrimaryTerm(hit.primaryTerm() != null && hit.primaryTerm() > 0 ? hit.primaryTerm() : 0);
float score = hit.score() != null ? hit.score().floatValue() : Float.NaN; float score = hit.score() != null ? hit.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, hit.sort().stream().map(TypeUtils::toObject).toArray(), return new SearchDocumentAdapter(document, score, hit.sort().stream().map(TypeUtils::toObject).toArray(),
documentFields, highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing()); documentFields, highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
} }
public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) { public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {
Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source()) Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create(); : Document.create();
document.setIndex(completionSuggestOption.index()); document.setIndex(completionSuggestOption.index());
if (completionSuggestOption.id() != null) { if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id()); document.setId(completionSuggestOption.id());
} }
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;
} }
List<Explanation> details = explanation.details().stream().map(DocumentAdapters::from).collect(Collectors.toList()); List<Explanation> details = explanation.details().stream().map(DocumentAdapters::from).collect(Collectors.toList());
return new Explanation(true, (double) explanation.value(), explanation.description(), details); return new Explanation(true, (double) explanation.value(), explanation.description(), details);
} }
private static Explanation from(ExplanationDetail explanationDetail) { private static Explanation from(ExplanationDetail explanationDetail) {
List<Explanation> details = explanationDetail.details().stream().map(DocumentAdapters::from) List<Explanation> details = explanationDetail.details().stream().map(DocumentAdapters::from)
.collect(Collectors.toList()); .collect(Collectors.toList());
return new Explanation(null, (double) explanationDetail.value(), explanationDetail.description(), details); return new Explanation(null, (double) explanationDetail.value(), explanationDetail.description(), details);
} }
@Nullable @Nullable
private static NestedMetaData from(@Nullable NestedIdentity nestedIdentity) { private static NestedMetaData from(@Nullable NestedIdentity nestedIdentity) {
if (nestedIdentity == null) { if (nestedIdentity == null) {
return null; return null;
} }
NestedMetaData child = from(nestedIdentity.nested()); NestedMetaData child = from(nestedIdentity.nested());
return NestedMetaData.of(nestedIdentity.field(), nestedIdentity.offset(), child); return NestedMetaData.of(nestedIdentity.field(), nestedIdentity.offset(), child);
} }
/** /**
* Creates a {@link Document} from a {@link GetResponse} where the found document is contained as {@link EntityAsMap}. * Creates a {@link Document} from a {@link GetResponse} where the found document is contained as {@link EntityAsMap}.
* *
* @param getResponse the response instance * @param getResponse the response instance
* @return the Document * @return the Document
*/ */
@Nullable @Nullable
public static Document from(GetResult<EntityAsMap> getResponse) { public static Document from(GetResult<EntityAsMap> getResponse) {
Assert.notNull(getResponse, "getResponse must not be null"); Assert.notNull(getResponse, "getResponse must not be null");
if (!getResponse.found()) { if (!getResponse.found()) {
return null; return null;
} }
Document document = getResponse.source() != null ? Document.from(getResponse.source()) : Document.create(); Document document = getResponse.source() != null ? Document.from(getResponse.source()) : Document.create();
document.setIndex(getResponse.index()); document.setIndex(getResponse.index());
document.setId(getResponse.id()); document.setId(getResponse.id());
if (getResponse.version() != null) { if (getResponse.version() != null) {
document.setVersion(getResponse.version()); document.setVersion(getResponse.version());
} }
if (getResponse.seqNo() != null) { if (getResponse.seqNo() != null) {
document.setSeqNo(getResponse.seqNo()); document.setSeqNo(getResponse.seqNo());
} }
if (getResponse.primaryTerm() != null) { if (getResponse.primaryTerm() != null) {
document.setPrimaryTerm(getResponse.primaryTerm()); document.setPrimaryTerm(getResponse.primaryTerm());
} }
return document; return document;
} }
/** /**
* Creates a list of {@link MultiGetItem}s from a {@link MgetResponse} where the data is contained as * Creates a list of {@link MultiGetItem}s from a {@link MgetResponse} where the data is contained as
* {@link EntityAsMap} instances. * {@link EntityAsMap} instances.
* *
* @param mgetResponse the response instance * @param mgetResponse the response instance
* @return list of multiget items * @return list of multiget items
*/ */
public static List<MultiGetItem<Document>> from(MgetResponse<EntityAsMap> mgetResponse) { public static List<MultiGetItem<Document>> from(MgetResponse<EntityAsMap> mgetResponse) {
Assert.notNull(mgetResponse, "mgetResponse must not be null"); Assert.notNull(mgetResponse, "mgetResponse must not be null");
return mgetResponse.docs().stream() // return mgetResponse.docs().stream() //
.map(itemResponse -> MultiGetItem.of( // .map(itemResponse -> MultiGetItem.of( //
itemResponse.isFailure() ? null : from(itemResponse.result()), // itemResponse.isFailure() ? null : from(itemResponse.result()), //
ResponseConverter.getFailure(itemResponse))) ResponseConverter.getFailure(itemResponse)))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
} }

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

@ -46,9 +46,9 @@ import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestClientBuilder;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.ClientConfiguration;
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;
/** /**
@ -329,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(RequestOptions.DEFAULT, false).toBuilder(); : new RestClientOptions(RequestOptions.DEFAULT).toBuilder();
RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions); RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions);

View File

@ -135,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 RestClientOptions(RequestOptions.DEFAULT, false); return new RestClientOptions(RequestOptions.DEFAULT);
} }
} }

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.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;
@ -60,6 +59,7 @@ 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.data.elasticsearch.core.sql.SqlResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -181,6 +181,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");

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

@ -27,7 +27,6 @@ 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.UncategorizedElasticsearchException; import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
@ -50,6 +49,7 @@ import org.springframework.data.elasticsearch.core.mapping.Alias;
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings; 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;
/** /**

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

@ -28,8 +28,8 @@ 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.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -44,7 +44,7 @@ import org.springframework.util.Assert;
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<>();
@ -117,7 +117,7 @@ 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;
} }
@ -129,7 +129,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,8 +33,8 @@ 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;
/** /**
@ -53,7 +53,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 +104,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;
} }

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,10 +27,9 @@ 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;
/** /**
@ -57,11 +56,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
@ -131,8 +127,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.Response.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));
@ -177,8 +172,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.Response.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));

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

@ -125,6 +125,6 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
* @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 RestClientOptions(RequestOptions.DEFAULT, false).toBuilder().build(); 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

@ -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;
@ -64,6 +63,7 @@ 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.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;
@ -167,6 +167,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");

View File

@ -29,7 +29,6 @@ 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;
@ -52,6 +51,7 @@ import org.springframework.data.elasticsearch.core.mapping.Alias;
import org.springframework.data.elasticsearch.core.mapping.CreateIndexSettings; 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;
/** /**

View File

@ -42,20 +42,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;
@ -73,12 +71,10 @@ 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;
@ -102,6 +98,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;
@ -536,22 +533,17 @@ class RequestConverter extends AbstractQueryProcessor {
public co.elastic.clients.elasticsearch.sql.QueryRequest sqlQueryRequest(SqlQuery query) { public co.elastic.clients.elasticsearch.sql.QueryRequest sqlQueryRequest(SqlQuery query) {
Assert.notNull(query, "Query must not be null."); Assert.notNull(query, "Query must not be null.");
return co.elastic.clients.elasticsearch.sql.QueryRequest.of(sqb -> sqb return co.elastic.clients.elasticsearch.sql.QueryRequest.of(sqb -> {
.query(query.getQuery()) sqb.query(query.getQuery()).catalog(query.getCatalog()).columnar(query.getColumnar()).cursor(query.getCursor())
.catalog(query.getCatalog()) .fetchSize(query.getFetchSize()).fieldMultiValueLeniency(query.getFieldMultiValueLeniency())
.columnar(query.getColumnar()) .indexUsingFrozen(query.getIndexIncludeFrozen()).keepAlive(time(query.getKeepAlive()))
.cursor(query.getCursor()) .keepOnCompletion(query.getKeepOnCompletion()).pageTimeout(time(query.getPageTimeout()))
.fetchSize(query.getFetchSize()) .requestTimeout(time(query.getRequestTimeout()))
.fieldMultiValueLeniency(query.getFieldMultiValueLeniency()) .waitForCompletionTimeout(time(query.getWaitForCompletionTimeout())).filter(getQuery(query.getFilter(), null))
.indexUsingFrozen(query.getIndexIncludeFrozen()) .timeZone(Objects.toString(query.getTimeZone(), null)).format("json");
.keepAlive(time(query.getKeepAlive()))
.keepOnCompletion(query.getKeepOnCompletion()) return sqb;
.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
@ -738,7 +730,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;
@ -751,10 +744,11 @@ class RequestConverter extends AbstractQueryProcessor {
} }
return co.elastic.clients.elasticsearch._types.Script.of(sb -> { return co.elastic.clients.elasticsearch._types.Script.of(sb -> {
sb.lang(scriptData.language()) sb.lang(scriptData.language())
.params(params) .params(params);
.id(scriptData.scriptName()); if (scriptData.type() == ScriptType.INLINE) {
if (scriptData.script() != null){ sb.source(scriptData.script());
sb.source(s -> s.scriptString(scriptData.script())); } else if (scriptData.type() == ScriptType.STORED) {
sb.id(scriptData.script());
} }
return sb; return sb;
}); });
@ -926,13 +920,9 @@ 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(sb -> sb
if (script.getSource() != null){ .lang(script.getLang())
sb.source(s -> s.scriptString(script.getSource())); .source(script.getSource()));
}
sb.lang(script.getLang());
return sb;
});
} }
builder.timeout(time(reindexRequest.getTimeout())) // builder.timeout(time(reindexRequest.getTimeout())) //
@ -1089,11 +1079,12 @@ class RequestConverter extends AbstractQueryProcessor {
uqb.script(sb -> { uqb.script(sb -> {
sb.lang(query.getLang()).params(params); sb.lang(query.getLang()).params(params);
if (query.getScript() != null){
sb.source(s -> s.scriptString(query.getScript()));
}
sb.id(query.getId());
if (query.getScriptType() == ScriptType.INLINE) {
sb.source(query.getScript()); //
} else if (query.getScriptType() == ScriptType.STORED) {
sb.id(query.getScript());
}
return sb; return sb;
}); });
} }
@ -1256,11 +1247,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 +1287,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 +1304,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());
} }
@ -1351,9 +1342,7 @@ class RequestConverter extends AbstractQueryProcessor {
if (script != null) { if (script != null) {
rfb.script(s -> { rfb.script(s -> {
if (script != null) { s.source(script);
s.source(so -> so.scriptString(script));
}
if (runtimeField.getParams() != null) { if (runtimeField.getParams() != null) {
s.params(TypeUtils.paramsMap(runtimeField.getParams())); s.params(TypeUtils.paramsMap(runtimeField.getParams()));
@ -1370,14 +1359,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(),
@ -1476,14 +1460,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 +1481,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());
} }
@ -1555,9 +1542,7 @@ class RequestConverter extends AbstractQueryProcessor {
String script = runtimeField.getScript(); String script = runtimeField.getScript();
if (script != null) { if (script != null) {
rfb.script(s -> { rfb.script(s -> {
if (script != null) { s.source(script);
s.source(so -> so.scriptString(script));
}
if (runtimeField.getParams() != null) { if (runtimeField.getParams() != null) {
s.params(TypeUtils.paramsMap(runtimeField.getParams())); s.params(TypeUtils.paramsMap(runtimeField.getParams()));
@ -1585,14 +1570,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 +1634,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 +1714,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 //
@ -1778,7 +1758,7 @@ 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()) //
@ -1798,7 +1778,8 @@ 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));
} }
@ -1897,11 +1878,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)) {
@ -1943,8 +1923,8 @@ class RequestConverter extends AbstractQueryProcessor {
return PutScriptRequest.of(b -> b // return PutScriptRequest.of(b -> b //
.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 +2014,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 //
SourceFilter sourceFilter = query.getSourceFilter(); .filter(sfb -> {
if (sourceFilter.fetchSource() != null) { SourceFilter sourceFilter = query.getSourceFilter();
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 +2029,7 @@ class RequestConverter extends AbstractQueryProcessor {
} }
return sfb; return sfb;
}); }));
}
return s;
});
} else { } else {
return null; return null;
} }

View File

@ -48,7 +48,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;
@ -66,6 +65,7 @@ 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.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 +92,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 +191,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 +219,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 +239,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 +400,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 +411,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 +500,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,7 +534,7 @@ 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

View File

@ -39,7 +39,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 +48,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;
@ -133,8 +133,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, long executionDurationInMillis, @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) {
@ -172,8 +171,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, executionDuration, 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,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,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.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.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

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;
/** /**
@ -123,7 +123,7 @@ public class EntityOperations {
// Only deal with text because ES generated Ids are strings! // Only deal with text because ES generated Ids are strings!
if (indexedObjectInformation.id() != null && idProperty != null if (indexedObjectInformation.id() != null && idProperty != null
// isReadable from the base class is false in case of records // isReadable from the base class is false in case of records
&& (idProperty.isReadable() || idProperty.getOwner().getType().isRecord()) && (idProperty.isReadable() || idProperty.getOwner().getType().isRecord())
&& idProperty.getType().isAssignableFrom(String.class)) { && idProperty.getType().isAssignableFrom(String.class)) {
propertyAccessor.setProperty(idProperty, indexedObjectInformation.id()); propertyAccessor.setProperty(idProperty, indexedObjectInformation.id());

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,7 +15,6 @@
*/ */
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;
@ -23,6 +22,7 @@ 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.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.

View File

@ -19,8 +19,8 @@ import reactor.core.publisher.Flux;
import java.time.Duration; 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.

View File

@ -19,8 +19,8 @@ import reactor.core.publisher.Flux;
import java.time.Duration; 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

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

@ -23,7 +23,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 +34,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;
/** /**

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

@ -19,9 +19,9 @@ 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.

View File

@ -19,9 +19,9 @@ 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;
/** /**

View File

@ -17,8 +17,8 @@ package org.springframework.data.elasticsearch.core;
import java.time.Duration; import java.time.Duration;
import org.jspecify.annotations.Nullable;
import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Nullable;
/** /**
* A {@link SearchHitsIterator} encapsulates {@link SearchHit} results that can be wrapped in a Java 8 * A {@link SearchHitsIterator} encapsulates {@link SearchHit} results that can be wrapped in a Java 8

View File

@ -18,11 +18,11 @@ package org.springframework.data.elasticsearch.core;
import java.time.Duration; import java.time.Duration;
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.BaseQueryBuilder; import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery; import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.lang.Nullable;
/** /**
* The operations for the * The operations for the
@ -233,8 +233,7 @@ public interface SearchOperations {
Query idsQuery(List<String> ids); Query idsQuery(List<String> ids);
/** /**
* Creates a {@link BaseQueryBuilder} that has the given ids setto the parameter value. No other properties of the * Creates a {@link BaseQueryBuilder} that has the given ids setto the parameter value. No other properties of the bulder are set.
* bulder are set.
* *
* @param ids the list of ids must not be {@literal null} * @param ids the list of ids must not be {@literal null}
* @return query returning the documents with the given ids * @return query returning the documents with the given ids

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;
/** /**
* This interface is used to expose the current {@code scrollId} from the underlying scroll context. * This interface is used to expose the current {@code scrollId} from the underlying scroll context.

View File

@ -17,8 +17,8 @@ package org.springframework.data.elasticsearch.core;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.ElasticsearchErrorCause; import org.springframework.data.elasticsearch.ElasticsearchErrorCause;
import org.springframework.lang.Nullable;
/** /**
* @author Haibo Liu * @author Haibo Liu

View File

@ -23,8 +23,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.util.ScrollState; import org.springframework.data.elasticsearch.client.util.ScrollState;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -1,5 +1,6 @@
/** /**
* Interfaces and classes related to Elasticsearch cluster information and management. * Interfaces and classes related to Elasticsearch cluster information and management.
*/ */
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.core.cluster; package org.springframework.data.elasticsearch.core.cluster;

View File

@ -19,14 +19,14 @@ import java.time.temporal.TemporalAccessor;
/** /**
* Interface to convert from and to {@link TemporalAccessor}s. * Interface to convert from and to {@link TemporalAccessor}s.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @since 4.2 * @since 4.2
*/ */
public interface DateFormatter { public interface DateFormatter {
/** /**
* Formats a {@link TemporalAccessor} into a String. * Formats a {@link TemporalAccessor} into a String.
* *
* @param accessor must not be {@literal null} * @param accessor must not be {@literal null}
* @return the formatted String * @return the formatted String
*/ */
@ -34,7 +34,7 @@ public interface DateFormatter {
/** /**
* Parses a String into a {@link TemporalAccessor}. * Parses a String into a {@link TemporalAccessor}.
* *
* @param input the String to parse, must not be {@literal null} * @param input the String to parse, must not be {@literal null}
* @param type the class of T * @param type the class of T
* @param <T> the {@link TemporalAccessor} implementation * @param <T> the {@link TemporalAccessor} implementation

View File

@ -19,7 +19,6 @@ import java.util.Collections;
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.convert.DefaultTypeMapper; import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.SimpleTypeInformationMapper; import org.springframework.data.convert.SimpleTypeInformationMapper;
import org.springframework.data.convert.TypeAliasAccessor; import org.springframework.data.convert.TypeAliasAccessor;
@ -28,6 +27,7 @@ import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/** /**
* Elasticsearch specific {@link org.springframework.data.convert.TypeMapper} implementation. * Elasticsearch specific {@link org.springframework.data.convert.TypeMapper} implementation.

View File

@ -15,7 +15,6 @@
*/ */
package org.springframework.data.elasticsearch.core.convert; package org.springframework.data.elasticsearch.core.convert;
import org.jspecify.annotations.Nullable;
import org.springframework.data.convert.EntityConverter; import org.springframework.data.convert.EntityConverter;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
@ -24,6 +23,7 @@ import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverte
import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -17,11 +17,11 @@ package org.springframework.data.elasticsearch.core.convert;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.data.convert.TypeMapper; import org.springframework.data.convert.TypeMapper;
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.mapping.context.MappingContext; import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;
/** /**
* Elasticsearch specific {@link TypeMapper} definition. * Elasticsearch specific {@link TypeMapper} definition.

View File

@ -15,7 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.core.convert; package org.springframework.data.elasticsearch.core.convert;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* @since 5.3 * @since 5.3

View File

@ -24,7 +24,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.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -39,7 +38,6 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.CustomConversions;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.FieldType; import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.ScriptedField; import org.springframework.data.elasticsearch.annotations.ScriptedField;
import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.data.elasticsearch.core.document.Document;
@ -52,7 +50,6 @@ import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery; import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter; import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.Field; import org.springframework.data.elasticsearch.core.query.Field;
import org.springframework.data.elasticsearch.core.query.Order;
import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm; import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.elasticsearch.core.query.SourceFilter; import org.springframework.data.elasticsearch.core.query.SourceFilter;
@ -66,6 +63,7 @@ import org.springframework.data.mapping.model.*;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.format.datetime.DateFormatterRegistrar; import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -394,7 +392,7 @@ public class MappingElasticsearchConverter
} }
if (source instanceof SearchDocument searchDocument) { if (source instanceof SearchDocument searchDocument) {
populateScriptedFields(targetEntity, result, searchDocument); populateScriptFields(targetEntity, result, searchDocument);
} }
return result; return result;
} catch (ConversionException e) { } catch (ConversionException e) {
@ -652,16 +650,7 @@ public class MappingElasticsearchConverter
return conversionService.convert(value, target); return conversionService.convert(value, target);
} }
/** private <T> void populateScriptFields(ElasticsearchPersistentEntity<?> entity, T result,
* Checks if any of the properties of the entity is annotated with
*
* @{@link ScriptedField}. If so, the value of this property is set from the returned fields in the document.
* @param entity the entity to defining the persistent property
* @param result the rsult to populate
* @param searchDocument the search result caontaining the fields
* @param <T> the result type
*/
private <T> void populateScriptedFields(ElasticsearchPersistentEntity<?> entity, T result,
SearchDocument searchDocument) { SearchDocument searchDocument) {
Map<String, List<Object>> fields = searchDocument.getFields(); Map<String, List<Object>> fields = searchDocument.getFields();
entity.doWithProperties((SimplePropertyHandler) property -> { entity.doWithProperties((SimplePropertyHandler) property -> {
@ -670,13 +659,8 @@ public class MappingElasticsearchConverter
// noinspection ConstantConditions // noinspection ConstantConditions
String name = scriptedField.name().isEmpty() ? property.getName() : scriptedField.name(); String name = scriptedField.name().isEmpty() ? property.getName() : scriptedField.name();
if (fields.containsKey(name)) { if (fields.containsKey(name)) {
if (property.isCollectionLike()) { Object value = searchDocument.getFieldValue(name);
List<Object> values = searchDocument.getFieldValues(name); entity.getPropertyAccessor(result).setProperty(property, value);
entity.getPropertyAccessor(result).setProperty(property, values);
} else {
Object value = searchDocument.getFieldValue(name);
entity.getPropertyAccessor(result).setProperty(property, value);
}
} }
} }
}); });
@ -1247,7 +1231,7 @@ public class MappingElasticsearchConverter
return; return;
} }
updatePropertiesInFieldsSortAndSourceFilter(query, domainClass); updatePropertiesInFieldsAndSourceFilter(query, domainClass);
if (query instanceof CriteriaQuery criteriaQuery) { if (query instanceof CriteriaQuery criteriaQuery) {
updatePropertiesInCriteriaQuery(criteriaQuery, domainClass); updatePropertiesInCriteriaQuery(criteriaQuery, domainClass);
@ -1258,14 +1242,7 @@ public class MappingElasticsearchConverter
} }
} }
/** private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
* replaces the names of fields in the query, the sort or soucre filters with the field names used in Elasticsearch
* when they are defined on the ElasticsearchProperties
*
* @param query the query to process
* @param domainClass the domain class (persistent entity)
*/
private void updatePropertiesInFieldsSortAndSourceFilter(Query query, Class<?> domainClass) {
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass); ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
@ -1273,12 +1250,12 @@ public class MappingElasticsearchConverter
List<String> fields = query.getFields(); List<String> fields = query.getFields();
if (!fields.isEmpty()) { if (!fields.isEmpty()) {
query.setFields(propertyToFieldNames(fields, persistentEntity)); query.setFields(updateFieldNames(fields, persistentEntity));
} }
List<String> storedFields = query.getStoredFields(); List<String> storedFields = query.getStoredFields();
if (!CollectionUtils.isEmpty(storedFields)) { if (!CollectionUtils.isEmpty(storedFields)) {
query.setStoredFields(propertyToFieldNames(storedFields, persistentEntity)); query.setStoredFields(updateFieldNames(storedFields, persistentEntity));
} }
SourceFilter sourceFilter = query.getSourceFilter(); SourceFilter sourceFilter = query.getSourceFilter();
@ -1289,60 +1266,37 @@ public class MappingElasticsearchConverter
String[] excludes = null; String[] excludes = null;
if (sourceFilter.getIncludes() != null) { if (sourceFilter.getIncludes() != null) {
includes = propertyToFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity) includes = updateFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
.toArray(new String[] {}); .toArray(new String[] {});
} }
if (sourceFilter.getExcludes() != null) { if (sourceFilter.getExcludes() != null) {
excludes = propertyToFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity) excludes = updateFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
.toArray(new String[] {}); .toArray(new String[] {});
} }
query.addSourceFilter(new FetchSourceFilter(sourceFilter.fetchSource(), includes, excludes)); query.addSourceFilter(new FetchSourceFilter(includes, excludes));
}
if (query.getSort() != null) {
var sort = query.getSort();
// stream the orders and map them to a new order with the changed names,
// then replace the existing sort with a new sort containing the new orders.
var newOrders = sort.stream().map(order -> {
var fieldNames = updateFieldNames(order.getProperty(), persistentEntity);
if (order instanceof Order springDataElasticsearchOrder) {
return springDataElasticsearchOrder.withProperty(fieldNames);
} else {
return new Sort.Order(order.getDirection(),
fieldNames,
order.isIgnoreCase(),
order.getNullHandling());
}
}).toList();
if (query instanceof BaseQuery baseQuery) {
baseQuery.setSort(Sort.by(newOrders));
}
} }
} }
} }
/** /**
* replaces property name of a property of the persistentEntity with the corresponding fieldname. If no such property * relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
* exists, the original fieldName is kept. * fieldname. If no such property exists, the original fieldName is kept.
* *
* @param propertyNames list of fieldnames * @param fieldNames list of fieldnames
* @param persistentEntity the persistent entity to check * @param persistentEntity the persistent entity to check
* @return an updated list of field names * @return an updated list of field names
*/ */
private List<String> propertyToFieldNames(List<String> propertyNames, private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
ElasticsearchPersistentEntity<?> persistentEntity) { return fieldNames.stream().map(fieldName -> updateFieldName(persistentEntity, fieldName))
return propertyNames.stream().map(propertyName -> propertyToFieldName(persistentEntity, propertyName))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@NotNull @NotNull
private String propertyToFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String propertyName) { private String updateFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String fieldName) {
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(propertyName); ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
return persistentProperty != null ? persistentProperty.getFieldName() : propertyName; return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
} }
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) { private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
@ -1372,85 +1326,15 @@ public class MappingElasticsearchConverter
return; return;
} }
var propertyNamesUpdate = updatePropertyNames(persistentEntity, field.getName()); String[] fieldNames = field.getName().split("\\.");
var fieldNames = propertyNamesUpdate.names();
field.setName(String.join(".", fieldNames));
if (propertyNamesUpdate.propertyCount() > 1 && propertyNamesUpdate.nestedProperty()) {
List<String> propertyNames = Arrays.asList(fieldNames);
field.setPath(String.join(".", propertyNames.subList(0, propertyNamesUpdate.propertyCount - 1)));
}
if (propertyNamesUpdate.persistentProperty != null) {
if (propertyNamesUpdate.persistentProperty.hasPropertyValueConverter()) {
PropertyValueConverter propertyValueConverter = Objects
.requireNonNull(propertyNamesUpdate.persistentProperty.getPropertyValueConverter());
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
if (criteriaEntry.getKey().hasValue()) {
Object value = criteriaEntry.getValue();
if (value.getClass().isArray()) {
Object[] objects = (Object[]) value;
for (int i = 0; i < objects.length; i++) {
objects[i] = propertyValueConverter.write(objects[i]);
}
} else {
criteriaEntry.setValue(propertyValueConverter.write(value));
}
}
});
}
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = propertyNamesUpdate.persistentProperty
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
if (fieldAnnotation != null) {
field.setFieldType(fieldAnnotation.type());
}
}
}
static record PropertyNamesUpdate(
String[] names,
Boolean nestedProperty,
Integer propertyCount,
ElasticsearchPersistentProperty persistentProperty) {
}
@Override
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
Assert.notNull(propertyPath, "propertyPath must not be null");
Assert.notNull(persistentEntity, "persistentEntity must not be null");
var propertyNamesUpdate = updatePropertyNames(persistentEntity, propertyPath);
return String.join(".", propertyNamesUpdate.names());
}
/**
* Parse a propertyPath and replace the path values with the field names from a persistentEntity. path entries not
* found in the entity are kept as they are.
*
* @return the eventually modified names, a flag if a nested entity was encountered the number of processed
* propertiesand the last processed PersistentProperty.
*/
PropertyNamesUpdate updatePropertyNames(ElasticsearchPersistentEntity<?> persistentEntity, String propertyPath) {
String[] propertyNames = propertyPath.split("\\.");
String[] fieldNames = Arrays.copyOf(propertyNames, propertyNames.length);
ElasticsearchPersistentEntity<?> currentEntity = persistentEntity; ElasticsearchPersistentEntity<?> currentEntity = persistentEntity;
ElasticsearchPersistentProperty persistentProperty = null; ElasticsearchPersistentProperty persistentProperty = null;
int propertyCount = 0; int propertyCount = 0;
boolean isNested = false; boolean isNested = false;
for (int i = 0; i < propertyNames.length; i++) { for (int i = 0; i < fieldNames.length; i++) {
persistentProperty = currentEntity.getPersistentProperty(propertyNames[i]); persistentProperty = currentEntity.getPersistentProperty(fieldNames[i]);
if (persistentProperty != null) { if (persistentProperty != null) {
propertyCount++; propertyCount++;
@ -1477,8 +1361,77 @@ public class MappingElasticsearchConverter
} }
} }
return new PropertyNamesUpdate(fieldNames, isNested, propertyCount, persistentProperty); field.setName(String.join(".", fieldNames));
if (propertyCount > 1 && isNested) {
List<String> propertyNames = Arrays.asList(fieldNames);
field.setPath(String.join(".", propertyNames.subList(0, propertyCount - 1)));
}
if (persistentProperty != null) {
if (persistentProperty.hasPropertyValueConverter()) {
PropertyValueConverter propertyValueConverter = Objects
.requireNonNull(persistentProperty.getPropertyValueConverter());
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
if (criteriaEntry.getKey().hasValue()) {
Object value = criteriaEntry.getValue();
if (value.getClass().isArray()) {
Object[] objects = (Object[]) value;
for (int i = 0; i < objects.length; i++) {
objects[i] = propertyValueConverter.write(objects[i]);
}
} else {
criteriaEntry.setValue(propertyValueConverter.write(value));
}
}
});
}
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
if (fieldAnnotation != null) {
field.setFieldType(fieldAnnotation.type());
}
}
} }
@Override
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
Assert.notNull(propertyPath, "propertyPath must not be null");
Assert.notNull(persistentEntity, "persistentEntity must not be null");
var properties = propertyPath.split("\\.", 2);
if (properties.length > 0) {
var propertyName = properties[0];
var fieldName = updateFieldName(persistentEntity, propertyName);
if (properties.length > 1) {
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
if (persistentProperty != null) {
ElasticsearchPersistentEntity<?> nestedPersistentEntity = mappingContext
.getPersistentEntity(persistentProperty);
if (nestedPersistentEntity != null) {
return fieldName + '.' + updateFieldNames(properties[1], nestedPersistentEntity);
} else {
return fieldName;
}
}
}
return fieldName;
} else {
return propertyPath;
}
}
// endregion // endregion
@SuppressWarnings("ClassCanBeRecord") @SuppressWarnings("ClassCanBeRecord")

View File

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

View File

@ -20,9 +20,9 @@ import java.util.LinkedHashMap;
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.convert.ConversionException; import org.springframework.data.elasticsearch.core.convert.ConversionException;
import org.springframework.data.elasticsearch.support.StringObjectMap; import org.springframework.data.elasticsearch.support.StringObjectMap;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -18,7 +18,7 @@ package org.springframework.data.elasticsearch.core.document;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**

View File

@ -21,9 +21,10 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap; import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.data.elasticsearch.support.StringObjectMap;
import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -15,7 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.core.document; package org.springframework.data.elasticsearch.core.document;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* meta data returned for nested inner hits. * meta data returned for nested inner hits.

View File

@ -18,7 +18,7 @@ package org.springframework.data.elasticsearch.core.document;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* Extension to {@link Document} exposing search response related data. * Extension to {@link Document} exposing search response related data.
@ -57,20 +57,6 @@ public interface SearchDocument extends Document {
return (V) values.get(0); return (V) values.get(0);
} }
/**
* @param name the field name
* @param <V> the type of elements
* @return the values of the given field.
*/
@Nullable
default <V> List<V> getFieldValues(final String name) {
List<Object> values = getFields().get(name);
if (values == null) {
return null;
}
return (List<V>) values;
}
/** /**
* @return the sort values for the search hit * @return the sort values for the search hit
*/ */
@ -125,5 +111,5 @@ public interface SearchDocument extends Document {
* @return the matched queries for the SearchHit. * @return the matched queries for the SearchHit.
*/ */
@Nullable @Nullable
Map<String, Double> getMatchedQueries(); List<String> getMatchedQueries();
} }

View File

@ -22,7 +22,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.jspecify.annotations.Nullable; import org.springframework.lang.Nullable;
/** /**
* {@link SearchDocument} implementation using a {@link Document} delegate. * {@link SearchDocument} implementation using a {@link Document} delegate.
@ -41,12 +41,12 @@ public class SearchDocumentAdapter implements SearchDocument {
private final Map<String, SearchDocumentResponse> innerHits = new HashMap<>(); private final Map<String, SearchDocumentResponse> innerHits = new HashMap<>();
@Nullable private final NestedMetaData nestedMetaData; @Nullable private final NestedMetaData nestedMetaData;
@Nullable private final Explanation explanation; @Nullable private final Explanation explanation;
@Nullable private final Map<String, Double> matchedQueries; @Nullable private final List<String> matchedQueries;
@Nullable private final String routing; @Nullable private final String routing;
public SearchDocumentAdapter(Document delegate, float score, Object[] sortValues, Map<String, List<Object>> fields, public SearchDocumentAdapter(Document delegate, float score, Object[] sortValues, Map<String, List<Object>> fields,
Map<String, List<String>> highlightFields, Map<String, SearchDocumentResponse> innerHits, Map<String, List<String>> highlightFields, Map<String, SearchDocumentResponse> innerHits,
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation, @Nullable Map<String, Double> matchedQueries, @Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation, @Nullable List<String> matchedQueries,
@Nullable String routing) { @Nullable String routing) {
this.delegate = delegate; this.delegate = delegate;
@ -249,7 +249,7 @@ public class SearchDocumentAdapter implements SearchDocument {
@Override @Override
@Nullable @Nullable
public Map<String, Double> getMatchedQueries() { public List<String> getMatchedQueries() {
return matchedQueries; return matchedQueries;
} }

View File

@ -20,10 +20,10 @@ import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Function; import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.AggregationsContainer; import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.data.elasticsearch.core.SearchShardStatistics; import org.springframework.data.elasticsearch.core.SearchShardStatistics;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest; import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.lang.Nullable;
/** /**
* This represents the complete search response from Elasticsearch, including the returned documents. * This represents the complete search response from Elasticsearch, including the returned documents.

View File

@ -1,5 +1,6 @@
/** /**
* Classes related to the Document structure of Elasticsearch documents and search responses. * Classes related to the Document structure of Elasticsearch documents and search responses.
*/ */
@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.elasticsearch.core.document; package org.springframework.data.elasticsearch.core.document;

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