mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-08-10 11:33:35 +00:00
Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6697755b45 | ||
|
e49bb63df4 | ||
|
006cda6de6 | ||
|
f51efa2cad | ||
|
6e30801a59 | ||
|
e64d0ada62 | ||
|
7f7cf3f52e | ||
|
55d470fc91 | ||
|
b7290b5d9c | ||
|
40414f0bde | ||
|
cb71caa504 | ||
|
ed9abce7af | ||
|
12ddb74fae | ||
|
6324b72707 | ||
|
6e49980c7c | ||
|
f9509f2696 | ||
|
7f53944e1b | ||
|
a9d2aaa93d | ||
|
158f5fc342 | ||
|
6268133506 | ||
|
923787c2d1 | ||
|
62fcbd44fa | ||
|
77ba620fc6 | ||
|
06704d974d | ||
|
897cb0a957 | ||
|
a8557a36dc | ||
|
2678cdc7b6 | ||
|
eb42312ebe | ||
|
af13fe0247 | ||
|
e81810c0d7 | ||
|
e9c7c0ee95 | ||
|
3a4425053e | ||
|
09984f86e6 | ||
|
710526c5f6 | ||
|
1acd392af7 | ||
|
76fe240a24 | ||
|
e298bc9f7a | ||
|
49d5dee5aa | ||
|
df6a127629 | ||
|
08a1ef3a28 | ||
|
62a34cf09c | ||
|
cc5f149c5a | ||
|
0728c8e4aa | ||
|
ebbe242a72 | ||
|
0ce9a1c400 | ||
|
22763d17a7 | ||
|
9870de1e77 | ||
|
8c9d9ae1e7 | ||
|
945179e4eb | ||
|
ea38ef1d41 | ||
|
acbfba94ac | ||
|
5a0f556a3b | ||
|
a07ac3c93d | ||
|
9d025dd469 | ||
|
925921f174 | ||
|
2f0a259045 | ||
|
9ffcb092db |
@ -8,3 +8,7 @@
|
|||||||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||||
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||||
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
|
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.util=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.text=ALL-UNNAMED
|
||||||
|
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED
|
||||||
|
4
.mvn/wrapper/maven-wrapper.properties
vendored
4
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,3 +1,3 @@
|
|||||||
#Thu Nov 07 09:47:28 CET 2024
|
#Thu Jul 17 13:59:56 CEST 2025
|
||||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||||
|
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -9,7 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
triggers {
|
triggers {
|
||||||
pollSCM 'H/10 * * * *'
|
pollSCM 'H/10 * * * *'
|
||||||
upstream(upstreamProjects: "spring-data-commons/4.0.x", threshold: hudson.model.Result.SUCCESS)
|
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
options {
|
options {
|
||||||
|
@ -1,25 +1,20 @@
|
|||||||
# Java versions
|
# Java versions
|
||||||
java.main.tag=17.0.13_11-jdk-focal
|
java.main.tag=24.0.1_9-jdk-noble
|
||||||
java.next.tag=23.0.1_11-jdk-noble
|
java.next.tag=24.0.1_9-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.4.4.version=4.4.25
|
docker.mongodb.6.0.version=6.0.23
|
||||||
docker.mongodb.5.0.version=5.0.21
|
docker.mongodb.7.0.version=7.0.20
|
||||||
docker.mongodb.6.0.version=6.0.10
|
docker.mongodb.8.0.version=8.0.9
|
||||||
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
|
||||||
|
47
pom.xml
47
pom.xml
@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-elasticsearch</artifactId>
|
<artifactId>spring-data-elasticsearch</artifactId>
|
||||||
<version>6.0.0-M2</version>
|
<version>6.0.0-SNAPSHOT</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-M2</version>
|
<version>4.0.0-SNAPSHOT</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-M2</springdata.commons>
|
<springdata.commons>4.0.0-SNAPSHOT</springdata.commons>
|
||||||
|
|
||||||
<!-- version of the ElasticsearchClient -->
|
<!-- version of the ElasticsearchClient -->
|
||||||
<elasticsearch-java>8.17.4</elasticsearch-java>
|
<elasticsearch-java>9.1.0</elasticsearch-java>
|
||||||
|
|
||||||
<hoverfly>0.19.0</hoverfly>
|
<hoverfly>0.19.0</hoverfly>
|
||||||
<log4j>2.23.1</log4j>
|
<log4j>2.23.1</log4j>
|
||||||
@ -132,6 +132,27 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- the old RestCLient is an optional dependency for user that still want to use it-->
|
||||||
|
<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>
|
||||||
|
<optional>true</optional>
|
||||||
|
</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>
|
||||||
@ -450,8 +471,20 @@
|
|||||||
</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>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*** 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.4-5.5.adoc[]
|
||||||
|
*** xref:migration-guides/migration-guide-5.5-6.0.adoc[]
|
||||||
|
|
||||||
|
|
||||||
* xref:elasticsearch.adoc[]
|
* xref:elasticsearch.adoc[]
|
||||||
|
@ -6,10 +6,10 @@ This chapter illustrates configuration and usage of supported Elasticsearch clie
|
|||||||
Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is connected to a single Elasticsearch node or a cluster.
|
Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is connected to a single Elasticsearch node or a cluster.
|
||||||
Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of xref:elasticsearch/template.adoc[Elasticsearch Operations] and xref:elasticsearch/repositories/elasticsearch-repositories.adoc[Elasticsearch Repositories].
|
Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of xref:elasticsearch/template.adoc[Elasticsearch Operations] and xref:elasticsearch/repositories/elasticsearch-repositories.adoc[Elasticsearch Repositories].
|
||||||
|
|
||||||
[[elasticsearch.clients.restclient]]
|
[[elasticsearch.clients.rest5client]]
|
||||||
== Imperative Rest Client
|
== Imperative Rest5Client
|
||||||
|
|
||||||
To use the imperative (non-reactive) client, a configuration bean must be configured like this:
|
To use the imperative (non-reactive) Rest5Client, a configuration bean must be configured like this:
|
||||||
|
|
||||||
====
|
====
|
||||||
[source,java]
|
[source,java]
|
||||||
@ -31,7 +31,7 @@ public class MyClientConfig extends ElasticsearchConfiguration {
|
|||||||
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
|
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
|
||||||
====
|
====
|
||||||
|
|
||||||
The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[]] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
|
The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
|
||||||
|
|
||||||
|
|
||||||
The following beans can then be injected in other Spring components:
|
The following beans can then be injected in other Spring components:
|
||||||
@ -46,7 +46,81 @@ ElasticsearchOperations operations; <.>
|
|||||||
ElasticsearchClient elasticsearchClient; <.>
|
ElasticsearchClient elasticsearchClient; <.>
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RestClient restClient; <.>
|
Rest5Client rest5Client; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
JsonpMapper jsonpMapper; <.>
|
||||||
|
----
|
||||||
|
|
||||||
|
<.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[]
|
||||||
|
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
|
||||||
|
<.> the low level `Rest5Client` from the Elasticsearch libraries
|
||||||
|
<.> the `JsonpMapper` user by the Elasticsearch `Transport`
|
||||||
|
====
|
||||||
|
|
||||||
|
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
|
||||||
|
When using repositories, this instance is used under the hood as well.
|
||||||
|
|
||||||
|
[[elasticsearch.clients.restclient]]
|
||||||
|
== Deprecated Imperative RestClient
|
||||||
|
|
||||||
|
To use the imperative (non-reactive) RestClient - deprecated since version 6 - , the following dependency needs to be added, adapt the correct version. The exclusion is needed in a Spring Boot application:
|
||||||
|
====
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
|
<artifactId>elasticsearch-rest-client</artifactId>
|
||||||
|
<version>${elasticsearch-client.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
The configuration bean must be configured like this:
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchLegacyRestClientConfiguration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MyClientConfig extends ElasticsearchLegacyRestClientConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientConfiguration clientConfiguration() {
|
||||||
|
return ClientConfiguration.builder() <.>
|
||||||
|
.connectedTo("localhost:9200")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
|
||||||
|
====
|
||||||
|
|
||||||
|
The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
|
||||||
|
|
||||||
|
|
||||||
|
The following beans can then be injected in other Spring components:
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;@Autowired
|
||||||
|
ElasticsearchOperations operations; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ElasticsearchClient elasticsearchClient; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RestClient restClient; <.>
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
JsonpMapper jsonpMapper; <.>
|
JsonpMapper jsonpMapper; <.>
|
||||||
@ -61,8 +135,8 @@ JsonpMapper jsonpMapper; <.>
|
|||||||
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
|
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
|
||||||
When using repositories, this instance is used under the hood as well.
|
When using repositories, this instance is used under the hood as well.
|
||||||
|
|
||||||
[[elasticsearch.clients.reactiverestclient]]
|
[[elasticsearch.clients.reactiverest5client]]
|
||||||
== Reactive Rest Client
|
== Reactive Rest5Client
|
||||||
|
|
||||||
When working with the reactive stack, the configuration must be derived from a different class:
|
When working with the reactive stack, the configuration must be derived from a different class:
|
||||||
|
|
||||||
@ -99,6 +173,65 @@ ReactiveElasticsearchOperations operations; <.>
|
|||||||
@Autowired
|
@Autowired
|
||||||
ReactiveElasticsearchClient elasticsearchClient; <.>
|
ReactiveElasticsearchClient elasticsearchClient; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
Rest5Client rest5Client; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
JsonpMapper jsonpMapper; <.>
|
||||||
|
----
|
||||||
|
|
||||||
|
the following can be injected:
|
||||||
|
|
||||||
|
<.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[]
|
||||||
|
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
|
||||||
|
This is a reactive implementation based on the Elasticsearch client implementation.
|
||||||
|
<.> the low level `RestClient` from the Elasticsearch libraries
|
||||||
|
<.> the `JsonpMapper` user by the Elasticsearch `Transport`
|
||||||
|
====
|
||||||
|
|
||||||
|
Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[] to interact with the Elasticsearch cluster.
|
||||||
|
When using repositories, this instance is used under the hood as well.
|
||||||
|
|
||||||
|
[[elasticsearch.clients.reactiverestclient]]
|
||||||
|
== Deprecated Reactive RestClient
|
||||||
|
|
||||||
|
See the section above for the imperative code to use the deprecated RestClient for the necessary dependencies to include.
|
||||||
|
|
||||||
|
When working with the reactive stack, the configuration must be derived from a different class:
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchLegacyRestClientConfiguration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MyClientConfig extends ReactiveElasticsearchLegacyRestClientConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientConfiguration clientConfiguration() {
|
||||||
|
return ClientConfiguration.builder() <.>
|
||||||
|
.connectedTo("localhost:9200")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
|
||||||
|
====
|
||||||
|
|
||||||
|
The javadoc:org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
|
||||||
|
|
||||||
|
The following beans can then be injected in other Spring components:
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
@Autowired
|
||||||
|
ReactiveElasticsearchOperations operations; <.>
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ReactiveElasticsearchClient elasticsearchClient; <.>
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RestClient restClient; <.>
|
RestClient restClient; <.>
|
||||||
|
|
||||||
@ -183,8 +316,8 @@ In the case this is not enough, the user can add callback functions by using the
|
|||||||
|
|
||||||
The following callbacks are provided:
|
The following callbacks are provided:
|
||||||
|
|
||||||
[[elasticsearch.clients.configuration.callbacks.rest]]
|
[[elasticsearch.clients.configuration.callbacks.rest5]]
|
||||||
==== Configuration of the low level Elasticsearch `RestClient`:
|
==== Configuration of the low level Elasticsearch `Rest5Client`:
|
||||||
|
|
||||||
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
|
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
|
||||||
`RestClient`:
|
`RestClient`:
|
||||||
@ -193,7 +326,24 @@ This callback provides a `org.elasticsearch.client.RestClientBuilder` that can b
|
|||||||
----
|
----
|
||||||
ClientConfiguration.builder()
|
ClientConfiguration.builder()
|
||||||
.connectedTo("localhost:9200", "localhost:9291")
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
.withClientConfigurer(ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
.withClientConfigurer(Rest5Clients.ElasticsearchRest5ClientConfigurationCallback.from(restClientBuilder -> {
|
||||||
|
// configure the Elasticsearch Rest5Client
|
||||||
|
return restClientBuilder;
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
====
|
||||||
|
[[elasticsearch.clients.configuration.callbacks.rest]]
|
||||||
|
==== Configuration of the deprecated low level Elasticsearch `RestClient`:
|
||||||
|
|
||||||
|
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
|
||||||
|
`RestClient`:
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
|
.withClientConfigurer(RestClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
||||||
// configure the Elasticsearch RestClient
|
// configure the Elasticsearch RestClient
|
||||||
return restClientBuilder;
|
return restClientBuilder;
|
||||||
}))
|
}))
|
||||||
@ -201,10 +351,29 @@ ClientConfiguration.builder()
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
[[elasticsearch.clients.configurationcallbacks.httpasync]]
|
[[elasticsearch.clients.configurationcallbacks.httpasync5]]
|
||||||
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `RestClient`:
|
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `Rest5Client`:
|
||||||
|
|
||||||
This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpCLient that is
|
This callback provides a `org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder` to configure the HttpClient that is
|
||||||
|
used by the `Rest5Client`.
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
|
.withClientConfigurer(Rest5Clients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
|
||||||
|
// configure the HttpAsyncClient
|
||||||
|
return httpAsyncClientBuilder;
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[[elasticsearch.clients.configurationcallbacks.httpasync]]
|
||||||
|
==== Configuration of the HttpAsyncClient used by the deprecated low level Elasticsearch `RestClient`:
|
||||||
|
|
||||||
|
This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpClient that is
|
||||||
used by the `RestClient`.
|
used by the `RestClient`.
|
||||||
|
|
||||||
====
|
====
|
||||||
@ -212,7 +381,7 @@ used by the `RestClient`.
|
|||||||
----
|
----
|
||||||
ClientConfiguration.builder()
|
ClientConfiguration.builder()
|
||||||
.connectedTo("localhost:9200", "localhost:9291")
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
.withClientConfigurer(ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
|
.withClientConfigurer(RestClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
|
||||||
// configure the HttpAsyncClient
|
// configure the HttpAsyncClient
|
||||||
return httpAsyncClientBuilder;
|
return httpAsyncClientBuilder;
|
||||||
}))
|
}))
|
||||||
@ -220,6 +389,63 @@ ClientConfiguration.builder()
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[elasticsearch.clients.configurationcallbacks.connectionconfig]]
|
||||||
|
==== Configuration of the ConnectionConfig used by the low level Elasticsearch `Rest5Client`:
|
||||||
|
|
||||||
|
This callback provides a `org.apache.hc.client5.http.config.ConnectionConfig` to configure the connection that is
|
||||||
|
used by the `Rest5Client`.
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
|
.withClientConfigurer(Rest5Clients.ElasticsearchConnectionConfigurationCallback.from(connectionConfigBuilder -> {
|
||||||
|
// configure the connection
|
||||||
|
return connectionConfigBuilder;
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[[elasticsearch.clients.configurationcallbacks.connectioncmanager]]
|
||||||
|
==== Configuration of the ConnectionManager used by the low level Elasticsearch `Rest5Client`:
|
||||||
|
|
||||||
|
This callback provides a `org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder` to configure the connection manager that is
|
||||||
|
used by the `Rest5Client`.
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
|
.withClientConfigurer(Rest5Clients.ElasticsearchConnectionManagerCallback.from(connectionManagerBuilder -> {
|
||||||
|
// configure the connection manager
|
||||||
|
return connectionManagerBuilder;
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[[elasticsearch.clients.configurationcallbacks.requestconfig]]
|
||||||
|
==== Configuration of the RequestConfig used by the low level Elasticsearch `Rest5Client`:
|
||||||
|
|
||||||
|
This callback provides a `org.apache.hc.client5.http.config.RequestConfig` to configure the RequestConfig that is
|
||||||
|
used by the `Rest5Client`.
|
||||||
|
|
||||||
|
====
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:9200", "localhost:9291")
|
||||||
|
.withClientConfigurer(Rest5Clients.ElasticsearchRequestConfigCallback.from(requestConfigBuilder -> {
|
||||||
|
// configure the request config
|
||||||
|
return requestConfigBuilder;
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
[[elasticsearch.clients.logging]]
|
[[elasticsearch.clients.logging]]
|
||||||
== Client Logging
|
== Client Logging
|
||||||
|
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
[[new-features]]
|
[[new-features]]
|
||||||
= What's new
|
= What's new
|
||||||
|
|
||||||
|
[[new-features.6-0-0]]
|
||||||
|
== New in Spring Data Elasticsearch 6.0
|
||||||
|
|
||||||
|
* Upgarde to Spring 7
|
||||||
|
* Switch to jspecify nullability annotations
|
||||||
|
* Upgrade to Elasticsearch 9.1.0
|
||||||
|
* Use the new Elasticsearch Rest5Client as default
|
||||||
|
|
||||||
|
|
||||||
[[new-features.5-5-0]]
|
[[new-features.5-5-0]]
|
||||||
== New in Spring Data Elasticsearch 5.5
|
== New in Spring Data Elasticsearch 5.5
|
||||||
|
|
||||||
* Upgrade to Elasticsearch 8.17.4.
|
* Upgrade to Elasticsearch 8.18.1.
|
||||||
* Add support for the `@SearchTemplateQuery` annotation on repository methods.
|
* Add support for the `@SearchTemplateQuery` annotation on repository methods.
|
||||||
* Scripted field properties of type collection can be populated from scripts returning arrays.
|
* Scripted field properties of type collection can be populated from scripts returning arrays.
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ 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 | 8.17.2 | 6.2.x
|
| 2025.1 (in development) | 6.0.x | 9.1.0 | 7.0.x
|
||||||
| 2025.0 (in development) | 5.5.x | 8.17.2 | 6.2.x
|
| 2025.0 | 5.5.x | 8.18.1 | 6.2.x
|
||||||
| 2024.1 | 5.4.x | 8.15.5 | 6.1.x
|
| 2024.1 | 5.4.x | 8.15.5 | 6.1.x
|
||||||
| 2024.0 | 5.3.x | 8.13.4 | 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[Out of maintenance] | 8.11.1 | 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
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
[[elasticsearch-migration-guide-5.5-6.0]]
|
||||||
|
= Upgrading from 5.5.x to 6.0.x
|
||||||
|
|
||||||
|
This section describes breaking changes from version 5.5.x to 6.0.x and how removed features can be replaced by new introduced features.
|
||||||
|
|
||||||
|
[[elasticsearch-migration-guide-5.5-6.0.breaking-changes]]
|
||||||
|
== Breaking Changes
|
||||||
|
|
||||||
|
From version 6.0 on, Spring Data Elasticsearch uses the Elasticsearch 9 libraries and as default the new `Rest5Client` provided by these libraries. It is still possible to use the old `RestClient`, check xref:elasticsearch/clients.adoc[Elasticsearch clients] for information. The configuration callbacks for this `RestClient` have been moved from `org.springframework.data.elasticsearch.client.elc.ElasticsearchClients` to the `org.springframework.data.elasticsearch.client.elc.rest_client.RestClients` class.
|
||||||
|
|
||||||
|
In the `org.springframework.data.elasticsearch.core.query.UpdateQuery` class the type of the two fields `ifSeqNo` and `ifPrimaryTerm` has changed from `Integer` to `Long` to align with the normal query and the underlying Elasticsearch client.
|
||||||
|
|
||||||
|
[[elasticsearch-migration-guide-5.5-6.0.deprecations]]
|
||||||
|
== Deprecations
|
||||||
|
|
||||||
|
All the code using the old `RestClient` has been moved to the `org.springframework.data.elasticsearch.client.elc.rest_client` package and has been deprecated. Users should switch to the classes from the `org.springframework.data.elasticsearch.client.elc.rest5_client` package.
|
||||||
|
|
||||||
|
|
||||||
|
=== Removals
|
||||||
|
|
||||||
|
The `org.springframework.data.elasticsearch.core.query.ScriptType` enum has been removed. To distinguish between an inline and a stored script set the appropriate values in the `org.springframework.data.elasticsearch.core.query.ScriptData` record.
|
||||||
|
|
||||||
|
These methods have been removed because the Elasticsearch Client 9 does not support them anymore:
|
||||||
|
```
|
||||||
|
org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchIndicesClient.unfreeze(UnfreezeRequest)
|
||||||
|
org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchIndicesClient.unfreeze(Function<UnfreezeRequest.Builder, ObjectBuilder<UnfreezeRequest>>)
|
||||||
|
```
|
@ -127,10 +127,16 @@ public interface ClientConfiguration {
|
|||||||
Optional<String> getCaFingerprint();
|
Optional<String> getCaFingerprint();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
|
* Returns the {@link HostnameVerifier} to use. Must be {@link Optional#empty()} if not configured.
|
||||||
|
* Cannot be used with the Rest5Client used from Elasticsearch 9 on as the underlying Apache http components 5 does not offer a way
|
||||||
|
* to set this. Users that need a hostname verifier must integrate this in a SSLContext.
|
||||||
|
* Returning a value here is ignored in this case
|
||||||
*
|
*
|
||||||
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
|
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
|
||||||
|
* @deprecated since 6.0
|
||||||
*/
|
*/
|
||||||
|
// todo #3117 document this
|
||||||
|
@Deprecated(since = "6.0", forRemoval=true)
|
||||||
Optional<HostnameVerifier> getHostNameVerifier();
|
Optional<HostnameVerifier> getHostNameVerifier();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,14 +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 java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
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.jspecify.annotations.Nullable;
|
||||||
@ -44,6 +36,13 @@ import org.springframework.data.elasticsearch.core.document.SearchDocumentAdapte
|
|||||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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}
|
||||||
@ -55,187 +54,188 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
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());
|
||||||
|
|
||||||
List<String> matchedQueries = hit.matchedQueries();
|
Map<String, Double> 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(co.elastic.clients.elasticsearch.core.explain.@Nullable 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,44 +15,38 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.client.elc;
|
package org.springframework.data.elasticsearch.client.elc;
|
||||||
|
|
||||||
|
import static org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients.*;
|
||||||
|
import static org.springframework.data.elasticsearch.client.elc.rest_client.RestClients.*;
|
||||||
|
|
||||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||||
import co.elastic.clients.json.JsonpMapper;
|
import co.elastic.clients.json.JsonpMapper;
|
||||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
import co.elastic.clients.transport.TransportOptions;
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
import co.elastic.clients.transport.TransportUtils;
|
|
||||||
import co.elastic.clients.transport.Version;
|
import co.elastic.clients.transport.Version;
|
||||||
|
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
|
||||||
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.HttpRequest;
|
|
||||||
import org.apache.http.HttpRequestInterceptor;
|
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
|
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
import org.elasticsearch.client.RequestOptions;
|
|
||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.elasticsearch.client.RestClientBuilder;
|
|
||||||
import org.jspecify.annotations.Nullable;
|
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.VersionInfo;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to create the different Elasticsearch clients
|
* Utility class to create the different Elasticsearch clients. The RestClient class is the one used in Elasticsearch
|
||||||
|
* until version 9, it is still available, but it's use is deprecated. The Rest5Client class is the one that should be
|
||||||
|
* used from Elasticsearch 9 on.
|
||||||
*
|
*
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
@ -119,18 +113,32 @@ public final class ElasticsearchClients {
|
|||||||
*
|
*
|
||||||
* @param restClient the underlying {@link RestClient}
|
* @param restClient the underlying {@link RestClient}
|
||||||
* @return the {@link ReactiveElasticsearchClient}
|
* @return the {@link ReactiveElasticsearchClient}
|
||||||
|
* @deprecated since 6.0, use the version with a Rest5Client.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
public static ReactiveElasticsearchClient createReactive(RestClient restClient) {
|
public static ReactiveElasticsearchClient createReactive(RestClient restClient) {
|
||||||
return createReactive(restClient, null, DEFAULT_JSONP_MAPPER);
|
return createReactive(restClient, null, DEFAULT_JSONP_MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ReactiveElasticsearchClient}.
|
||||||
|
*
|
||||||
|
* @param rest5Client the underlying {@link RestClient}
|
||||||
|
* @return the {@link ReactiveElasticsearchClient}
|
||||||
|
*/
|
||||||
|
public static ReactiveElasticsearchClient createReactive(Rest5Client rest5Client) {
|
||||||
|
return createReactive(rest5Client, null, DEFAULT_JSONP_MAPPER);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ReactiveElasticsearchClient}.
|
* Creates a new {@link ReactiveElasticsearchClient}.
|
||||||
*
|
*
|
||||||
* @param restClient the underlying {@link RestClient}
|
* @param restClient the underlying {@link RestClient}
|
||||||
* @param transportOptions options to be added to each request.
|
* @param transportOptions options to be added to each request.
|
||||||
* @return the {@link ReactiveElasticsearchClient}
|
* @return the {@link ReactiveElasticsearchClient}
|
||||||
|
* @deprecated since 6.0, use the version with a Rest5Client.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
public static ReactiveElasticsearchClient createReactive(RestClient restClient,
|
public static ReactiveElasticsearchClient createReactive(RestClient restClient,
|
||||||
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
@ -139,6 +147,21 @@ public final class ElasticsearchClients {
|
|||||||
var transport = getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions, jsonpMapper);
|
var transport = getElasticsearchTransport(restClient, REACTIVE_CLIENT, transportOptions, jsonpMapper);
|
||||||
return createReactive(transport);
|
return createReactive(transport);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ReactiveElasticsearchClient}.
|
||||||
|
*
|
||||||
|
* @param rest5Client the underlying {@link RestClient}
|
||||||
|
* @param transportOptions options to be added to each request.
|
||||||
|
* @return the {@link ReactiveElasticsearchClient}
|
||||||
|
*/
|
||||||
|
public static ReactiveElasticsearchClient createReactive(Rest5Client rest5Client,
|
||||||
|
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
|
Assert.notNull(rest5Client, "restClient must not be null");
|
||||||
|
|
||||||
|
var transport = getElasticsearchTransport(rest5Client, REACTIVE_CLIENT, transportOptions, jsonpMapper);
|
||||||
|
return createReactive(transport);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ReactiveElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
|
* Creates a new {@link ReactiveElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
|
||||||
@ -156,17 +179,21 @@ public final class ElasticsearchClients {
|
|||||||
|
|
||||||
// region imperative client
|
// region imperative client
|
||||||
/**
|
/**
|
||||||
* Creates a new imperative {@link ElasticsearchClient}
|
* Creates a new imperative {@link ElasticsearchClient}. This uses a RestClient, if the old RestClient is needed, this
|
||||||
|
* must be created with the {@link org.springframework.data.elasticsearch.client.elc.rest_client.RestClients} class
|
||||||
|
* and passed in as parameter.
|
||||||
*
|
*
|
||||||
* @param clientConfiguration configuration options, must not be {@literal null}.
|
* @param clientConfiguration configuration options, must not be {@literal null}.
|
||||||
* @return the {@link ElasticsearchClient}
|
* @return the {@link ElasticsearchClient}
|
||||||
*/
|
*/
|
||||||
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration) {
|
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration) {
|
||||||
return createImperative(getRestClient(clientConfiguration), null, DEFAULT_JSONP_MAPPER);
|
return createImperative(getRest5Client(clientConfiguration), null, DEFAULT_JSONP_MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new imperative {@link ElasticsearchClient}
|
* Creates a new imperative {@link ElasticsearchClient}. This uses a RestClient, if the old RestClient is needed, this
|
||||||
|
* must be created with the {@link org.springframework.data.elasticsearch.client.elc.rest_client.RestClients} class
|
||||||
|
* and passed in as parameter.
|
||||||
*
|
*
|
||||||
* @param clientConfiguration configuration options, must not be {@literal null}.
|
* @param clientConfiguration configuration options, must not be {@literal null}.
|
||||||
* @param transportOptions options to be added to each request.
|
* @param transportOptions options to be added to each request.
|
||||||
@ -174,7 +201,7 @@ public final class ElasticsearchClients {
|
|||||||
*/
|
*/
|
||||||
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration,
|
public static ElasticsearchClient createImperative(ClientConfiguration clientConfiguration,
|
||||||
TransportOptions transportOptions) {
|
TransportOptions transportOptions) {
|
||||||
return createImperative(getRestClient(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER);
|
return createImperative(getRest5Client(clientConfiguration), transportOptions, DEFAULT_JSONP_MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,11 +209,23 @@ public final class ElasticsearchClients {
|
|||||||
*
|
*
|
||||||
* @param restClient the RestClient to use
|
* @param restClient the RestClient to use
|
||||||
* @return the {@link ElasticsearchClient}
|
* @return the {@link ElasticsearchClient}
|
||||||
|
* @deprecated since 6.0, use the version with a Rest5Client.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
public static ElasticsearchClient createImperative(RestClient restClient) {
|
public static ElasticsearchClient createImperative(RestClient restClient) {
|
||||||
return createImperative(restClient, null, DEFAULT_JSONP_MAPPER);
|
return createImperative(restClient, null, DEFAULT_JSONP_MAPPER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new imperative {@link ElasticsearchClient}
|
||||||
|
*
|
||||||
|
* @param rest5Client the Rest5Client to use
|
||||||
|
* @return the {@link ElasticsearchClient}
|
||||||
|
*/
|
||||||
|
public static ElasticsearchClient createImperative(Rest5Client rest5Client) {
|
||||||
|
return createImperative(rest5Client, null, DEFAULT_JSONP_MAPPER);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new imperative {@link ElasticsearchClient}
|
* Creates a new imperative {@link ElasticsearchClient}
|
||||||
*
|
*
|
||||||
@ -194,7 +233,9 @@ public final class ElasticsearchClients {
|
|||||||
* @param transportOptions options to be added to each request.
|
* @param transportOptions options to be added to each request.
|
||||||
* @param jsonpMapper the mapper for the transport to use
|
* @param jsonpMapper the mapper for the transport to use
|
||||||
* @return the {@link ElasticsearchClient}
|
* @return the {@link ElasticsearchClient}
|
||||||
|
* @deprecated since 6.0, use the version with a Rest5Client.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
public static ElasticsearchClient createImperative(RestClient restClient, @Nullable TransportOptions transportOptions,
|
public static ElasticsearchClient createImperative(RestClient restClient, @Nullable TransportOptions transportOptions,
|
||||||
JsonpMapper jsonpMapper) {
|
JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
@ -206,6 +247,27 @@ public final class ElasticsearchClients {
|
|||||||
return createImperative(transport);
|
return createImperative(transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new imperative {@link ElasticsearchClient}
|
||||||
|
*
|
||||||
|
* @param rest5Client the Rest5Client to use
|
||||||
|
* @param transportOptions options to be added to each request.
|
||||||
|
* @param jsonpMapper the mapper for the transport to use
|
||||||
|
* @return the {@link ElasticsearchClient}
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public static ElasticsearchClient createImperative(Rest5Client rest5Client,
|
||||||
|
@Nullable TransportOptions transportOptions,
|
||||||
|
JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
|
Assert.notNull(rest5Client, "restClient must not be null");
|
||||||
|
|
||||||
|
ElasticsearchTransport transport = getElasticsearchTransport(rest5Client, IMPERATIVE_CLIENT, transportOptions,
|
||||||
|
jsonpMapper);
|
||||||
|
|
||||||
|
return createImperative(transport);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
|
* Creates a new {@link ElasticsearchClient} that uses the given {@link ElasticsearchTransport}.
|
||||||
*
|
*
|
||||||
@ -220,96 +282,6 @@ public final class ElasticsearchClients {
|
|||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region low level RestClient
|
|
||||||
private static RestClientOptions.Builder getRestClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
|
|
||||||
|
|
||||||
if (transportOptions instanceof RestClientOptions restClientOptions) {
|
|
||||||
return restClientOptions.toBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new RestClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
|
|
||||||
|
|
||||||
if (transportOptions != null) {
|
|
||||||
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
|
|
||||||
transportOptions.queryParameters().forEach(builder::setParameter);
|
|
||||||
builder.onWarnings(transportOptions.onWarnings());
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a low level {@link RestClient} for the given configuration.
|
|
||||||
*
|
|
||||||
* @param clientConfiguration must not be {@literal null}
|
|
||||||
* @return the {@link RestClient}
|
|
||||||
*/
|
|
||||||
public static RestClient getRestClient(ClientConfiguration clientConfiguration) {
|
|
||||||
return getRestClientBuilder(clientConfiguration).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RestClientBuilder getRestClientBuilder(ClientConfiguration clientConfiguration) {
|
|
||||||
HttpHost[] httpHosts = formattedHosts(clientConfiguration.getEndpoints(), clientConfiguration.useSsl()).stream()
|
|
||||||
.map(HttpHost::create).toArray(HttpHost[]::new);
|
|
||||||
RestClientBuilder builder = RestClient.builder(httpHosts);
|
|
||||||
|
|
||||||
if (clientConfiguration.getPathPrefix() != null) {
|
|
||||||
builder.setPathPrefix(clientConfiguration.getPathPrefix());
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
|
|
||||||
|
|
||||||
if (!headers.isEmpty()) {
|
|
||||||
builder.setDefaultHeaders(toHeaderArray(headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setHttpClientConfigCallback(clientBuilder -> {
|
|
||||||
if (clientConfiguration.getCaFingerprint().isPresent()) {
|
|
||||||
clientBuilder
|
|
||||||
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
|
|
||||||
}
|
|
||||||
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
|
|
||||||
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
|
|
||||||
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
|
|
||||||
|
|
||||||
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
|
|
||||||
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
|
||||||
|
|
||||||
if (!connectTimeout.isNegative()) {
|
|
||||||
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Duration socketTimeout = clientConfiguration.getSocketTimeout();
|
|
||||||
|
|
||||||
if (!socketTimeout.isNegative()) {
|
|
||||||
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
|
||||||
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
|
|
||||||
|
|
||||||
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
|
|
||||||
|
|
||||||
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
|
||||||
.getClientConfigurers()) {
|
|
||||||
if (clientConfigurer instanceof ElasticsearchHttpClientConfigurationCallback restClientConfigurationCallback) {
|
|
||||||
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientBuilder;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
|
|
||||||
.getClientConfigurers()) {
|
|
||||||
if (clientConfigurationCallback instanceof ElasticsearchRestClientConfigurationCallback configurationCallback) {
|
|
||||||
builder = configurationCallback.configure(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Elasticsearch transport
|
// region Elasticsearch transport
|
||||||
/**
|
/**
|
||||||
* Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a
|
* Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a
|
||||||
@ -320,7 +292,9 @@ public final class ElasticsearchClients {
|
|||||||
* @param transportOptions options for the transport
|
* @param transportOptions options for the transport
|
||||||
* @param jsonpMapper mapper for the transport
|
* @param jsonpMapper mapper for the transport
|
||||||
* @return ElasticsearchTransport
|
* @return ElasticsearchTransport
|
||||||
|
* @deprecated since 6.0, use the version taking a Rest5Client
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
public static ElasticsearchTransport getElasticsearchTransport(RestClient restClient, String clientType,
|
public static ElasticsearchTransport getElasticsearchTransport(RestClient restClient, String clientType,
|
||||||
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
@ -329,7 +303,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(org.elasticsearch.client.RequestOptions.DEFAULT, false).toBuilder();
|
||||||
|
|
||||||
RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions);
|
RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions);
|
||||||
|
|
||||||
@ -353,70 +327,35 @@ public final class ElasticsearchClients {
|
|||||||
|
|
||||||
return new RestClientTransport(restClient, jsonpMapper, restClientOptionsBuilder.build());
|
return new RestClientTransport(restClient, jsonpMapper, restClientOptionsBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link ElasticsearchTransport} that will use the given client that additionally is customized with a
|
||||||
|
* header to contain the clientType
|
||||||
|
*
|
||||||
|
* @param rest5Client the client to use
|
||||||
|
* @param clientType the client type to pass in each request as header
|
||||||
|
* @param transportOptions options for the transport
|
||||||
|
* @param jsonpMapper mapper for the transport
|
||||||
|
* @return ElasticsearchTransport
|
||||||
|
*/
|
||||||
|
public static ElasticsearchTransport getElasticsearchTransport(Rest5Client rest5Client, String clientType,
|
||||||
|
@Nullable TransportOptions transportOptions, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
|
Assert.notNull(rest5Client, "restClient must not be null");
|
||||||
|
Assert.notNull(clientType, "clientType must not be null");
|
||||||
|
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||||
|
|
||||||
|
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
|
||||||
|
: new Rest5ClientOptions(RequestOptions.DEFAULT, false).toBuilder();
|
||||||
|
|
||||||
|
Rest5ClientOptions.Builder rest5ClientOptionsBuilder = getRest5ClientOptionsBuilder(transportOptions);
|
||||||
|
|
||||||
|
rest5ClientOptionsBuilder.addHeader(X_SPRING_DATA_ELASTICSEARCH_CLIENT,
|
||||||
|
VersionInfo.clientVersions() + " / " + clientType);
|
||||||
|
|
||||||
|
return new Rest5ClientTransport(rest5Client, jsonpMapper, rest5ClientOptionsBuilder.build());
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
private static List<String> formattedHosts(List<InetSocketAddress> hosts, boolean useSsl) {
|
// todo #3117 remove and document that ElasticsearchHttpClientConfigurationCallback has been move to RestClients.
|
||||||
return hosts.stream().map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static org.apache.http.Header[] toHeaderArray(HttpHeaders headers) {
|
|
||||||
return headers.entrySet().stream() //
|
|
||||||
.flatMap(entry -> entry.getValue().stream() //
|
|
||||||
.map(value -> new BasicHeader(entry.getKey(), value))) //
|
|
||||||
.toArray(org.apache.http.Header[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interceptor to inject custom supplied headers.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
private record CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) implements HttpRequestInterceptor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void process(HttpRequest request, HttpContext context) {
|
|
||||||
HttpHeaders httpHeaders = headersSupplier.get();
|
|
||||||
|
|
||||||
if (httpHeaders != null && !httpHeaders.isEmpty()) {
|
|
||||||
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
|
||||||
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
public interface ElasticsearchHttpClientConfigurationCallback
|
|
||||||
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
|
|
||||||
|
|
||||||
static ElasticsearchHttpClientConfigurationCallback from(
|
|
||||||
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
|
|
||||||
|
|
||||||
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
|
|
||||||
|
|
||||||
return httpClientBuilderCallback::apply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
|
||||||
* the RestClient client with a {@link RestClientBuilder}
|
|
||||||
*
|
|
||||||
* @since 5.0
|
|
||||||
*/
|
|
||||||
public interface ElasticsearchRestClientConfigurationCallback
|
|
||||||
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
|
|
||||||
|
|
||||||
static ElasticsearchRestClientConfigurationCallback from(
|
|
||||||
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
|
|
||||||
|
|
||||||
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
|
|
||||||
|
|
||||||
return restClientBuilderCallback::apply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,13 @@ import co.elastic.clients.json.JsonpMapper;
|
|||||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
import co.elastic.clients.transport.TransportOptions;
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
|
||||||
|
|
||||||
import org.elasticsearch.client.RequestOptions;
|
|
||||||
import org.elasticsearch.client.RestClient;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
|
||||||
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||||
@ -38,7 +39,9 @@ import com.fasterxml.jackson.databind.SerializationFeature;
|
|||||||
/**
|
/**
|
||||||
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
|
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
|
||||||
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
|
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
|
||||||
* classes must provide the {@link ClientConfiguration} to use.
|
* classes must provide the {@link ClientConfiguration} to use. From Version 6.0 on, this class uses the new Rest5Client
|
||||||
|
* from Elasticsearch 9. The old implementation using the RestClient is still available under the name
|
||||||
|
* {@link ElasticsearchLegacyRestClientConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
@ -60,27 +63,27 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
|
|||||||
* @return RestClient
|
* @return RestClient
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
|
public Rest5Client elasticsearchRest5Client(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
||||||
|
|
||||||
return ElasticsearchClients.getRestClient(clientConfiguration);
|
return Rest5Clients.getRest5Client(clientConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
|
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link Rest5Client} bean and
|
||||||
* the {@link JsonpMapper} bean provided in this class.
|
* the {@link JsonpMapper} bean provided in this class.
|
||||||
*
|
*
|
||||||
* @return the {@link ElasticsearchTransport}
|
* @return the {@link ElasticsearchTransport}
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
|
public ElasticsearchTransport elasticsearchTransport(Rest5Client rest5Client, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
Assert.notNull(restClient, "restClient must not be null");
|
Assert.notNull(rest5Client, "restClient must not be null");
|
||||||
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||||
|
|
||||||
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.IMPERATIVE_CLIENT,
|
return ElasticsearchClients.getElasticsearchTransport(rest5Client, ElasticsearchClients.IMPERATIVE_CLIENT,
|
||||||
transportOptions(), jsonpMapper);
|
transportOptions(), jsonpMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +118,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method.
|
* Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(Rest5Client, JsonpMapper)} method.
|
||||||
*
|
*
|
||||||
* @return the {@link JsonpMapper} to use
|
* @return the {@link JsonpMapper} to use
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
@ -135,6 +138,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 Rest5ClientOptions(RequestOptions.DEFAULT, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.dao.DataAccessResourceFailureException;
|
import org.springframework.dao.DataAccessResourceFailureException;
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
@ -33,6 +34,7 @@ import org.springframework.data.elasticsearch.NoSuchIndexException;
|
|||||||
import org.springframework.data.elasticsearch.ResourceNotFoundException;
|
import org.springframework.data.elasticsearch.ResourceNotFoundException;
|
||||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||||
import org.springframework.data.elasticsearch.VersionConflictException;
|
import org.springframework.data.elasticsearch.VersionConflictException;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
|
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
|
||||||
@ -45,6 +47,9 @@ import org.springframework.data.elasticsearch.VersionConflictException;
|
|||||||
*/
|
*/
|
||||||
public class ElasticsearchExceptionTranslator implements PersistenceExceptionTranslator {
|
public class ElasticsearchExceptionTranslator implements PersistenceExceptionTranslator {
|
||||||
|
|
||||||
|
public static final boolean LEGACY_RESTCLIENT_PRESENT = ClassUtils
|
||||||
|
.isPresent("org.elasticsearch.client.ResponseException", ElasticsearchExceptionTranslator.class.getClassLoader());
|
||||||
|
|
||||||
private final JsonpMapper jsonpMapper;
|
private final JsonpMapper jsonpMapper;
|
||||||
|
|
||||||
public ElasticsearchExceptionTranslator(JsonpMapper jsonpMapper) {
|
public ElasticsearchExceptionTranslator(JsonpMapper jsonpMapper) {
|
||||||
@ -68,7 +73,7 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||||
|
|
||||||
checkForConflictException(ex);
|
checkForConflictException(ex);
|
||||||
|
|
||||||
@ -118,15 +123,20 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
|||||||
Integer status = null;
|
Integer status = null;
|
||||||
String message = null;
|
String message = null;
|
||||||
|
|
||||||
if (exception instanceof ResponseException responseException) {
|
if (LEGACY_RESTCLIENT_PRESENT && exception instanceof ResponseException responseException) {
|
||||||
|
// this code is for the old RestClient
|
||||||
status = responseException.getResponse().getStatusLine().getStatusCode();
|
status = responseException.getResponse().getStatusLine().getStatusCode();
|
||||||
message = responseException.getMessage();
|
message = responseException.getMessage();
|
||||||
|
} else if (exception instanceof ElasticsearchException elasticsearchException) {
|
||||||
|
// using the RestClient throws this
|
||||||
|
status = elasticsearchException.status();
|
||||||
|
message = elasticsearchException.getMessage();
|
||||||
} else if (exception.getCause() != null) {
|
} else if (exception.getCause() != null) {
|
||||||
checkForConflictException(exception.getCause());
|
checkForConflictException(exception.getCause());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != null && message != null) {
|
if (status != null && message != null) {
|
||||||
if (status == 409 && message.contains("type\":\"version_conflict_engine_exception"))
|
if (status == 409 && message.contains("version_conflict_engine_exception"))
|
||||||
if (message.contains("version conflict, required seqNo")) {
|
if (message.contains("version conflict, required seqNo")) {
|
||||||
throw new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict",
|
throw new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict",
|
||||||
exception);
|
exception);
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.client.elc;
|
||||||
|
|
||||||
|
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||||
|
import co.elastic.clients.json.JsonpMapper;
|
||||||
|
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||||
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
|
|
||||||
|
import org.elasticsearch.client.RequestOptions;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
|
||||||
|
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||||
|
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||||
|
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
|
||||||
|
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
|
||||||
|
* classes must provide the {@link ClientConfiguration} to use. <br/>
|
||||||
|
* This class uses the Elasticsearch RestClient which was replaced by the Rest5Client in Elasticsearch 9. It is still
|
||||||
|
* available here but deprecated.
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
* @since 4.4
|
||||||
|
* @deprecated since 6.0, use {@link ElasticsearchConfiguration}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval=true)
|
||||||
|
public abstract class ElasticsearchLegacyRestClientConfiguration extends ElasticsearchConfigurationSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be implemented by deriving classes to provide the {@link ClientConfiguration}.
|
||||||
|
*
|
||||||
|
* @return configuration, must not be {@literal null}
|
||||||
|
*/
|
||||||
|
@Bean(name = "elasticsearchClientConfiguration")
|
||||||
|
public abstract ClientConfiguration clientConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the underlying low level Elasticsearch RestClient.
|
||||||
|
*
|
||||||
|
* @param clientConfiguration configuration for the client, must not be {@literal null}
|
||||||
|
* @return RestClient
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
|
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
||||||
|
|
||||||
|
return RestClients.getRestClient(clientConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
|
||||||
|
* the {@link JsonpMapper} bean provided in this class.
|
||||||
|
*
|
||||||
|
* @return the {@link ElasticsearchTransport}
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
|
Assert.notNull(restClient, "restClient must not be null");
|
||||||
|
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||||
|
|
||||||
|
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.IMPERATIVE_CLIENT,
|
||||||
|
transportOptions(), jsonpMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the {@link ElasticsearchClient} to be used.
|
||||||
|
*
|
||||||
|
* @param transport the {@link ElasticsearchTransport} to use
|
||||||
|
* @return ElasticsearchClient instance
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ElasticsearchClient elasticsearchClient(ElasticsearchTransport transport) {
|
||||||
|
|
||||||
|
Assert.notNull(transport, "transport must not be null");
|
||||||
|
|
||||||
|
return ElasticsearchClients.createImperative(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link ElasticsearchOperations} implementation using an {@link ElasticsearchClient}.
|
||||||
|
*
|
||||||
|
* @return never {@literal null}.
|
||||||
|
*/
|
||||||
|
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
|
||||||
|
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
|
||||||
|
ElasticsearchClient elasticsearchClient) {
|
||||||
|
|
||||||
|
ElasticsearchTemplate template = new ElasticsearchTemplate(elasticsearchClient, elasticsearchConverter);
|
||||||
|
template.setRefreshPolicy(refreshPolicy());
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the JsonpMapper bean that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method.
|
||||||
|
*
|
||||||
|
* @return the {@link JsonpMapper} to use
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JsonpMapper jsonpMapper() {
|
||||||
|
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
|
||||||
|
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
|
||||||
|
// into this mapper, so we can safely keep them here.
|
||||||
|
var objectMapper = (new ObjectMapper())
|
||||||
|
.configure(SerializationFeature.INDENT_OUTPUT, false)
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
return new JacksonJsonpMapper(objectMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the options that should be added to every request. Must not be {@literal null}
|
||||||
|
*/
|
||||||
|
public TransportOptions transportOptions() {
|
||||||
|
return new RestClientOptions(RequestOptions.DEFAULT, false);
|
||||||
|
}
|
||||||
|
}
|
@ -658,7 +658,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
|||||||
QueryResponse response = sqlClient.query(requestConverter.sqlQueryRequest(query));
|
QueryResponse response = sqlClient.query(requestConverter.sqlQueryRequest(query));
|
||||||
|
|
||||||
return responseConverter.sqlResponse(response);
|
return responseConverter.sqlResponse(response);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
throw exceptionTranslator.translateException(e);
|
throw exceptionTranslator.translateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ package org.springframework.data.elasticsearch.client.elc;
|
|||||||
|
|
||||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||||
|
|
||||||
|
import co.elastic.clients.util.NamedValue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -60,10 +62,11 @@ class HighlightQueryBuilder {
|
|||||||
|
|
||||||
for (HighlightField highlightField : highlight.getFields()) {
|
for (HighlightField highlightField : highlight.getFields()) {
|
||||||
String mappedName = mapFieldName(highlightField.getName(), type);
|
String mappedName = mapFieldName(highlightField.getName(), type);
|
||||||
highlightBuilder.fields(mappedName, hf -> {
|
highlightBuilder.fields(
|
||||||
addParameters(highlightField.getParameters(), hf, type);
|
NamedValue.of(mappedName, co.elastic.clients.elasticsearch.core.search.HighlightField.of(hf -> {
|
||||||
return hf;
|
addParameters(highlightField.getParameters(), hf, type);
|
||||||
});
|
return hf;
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
return highlightBuilder.build();
|
return highlightBuilder.build();
|
||||||
|
@ -30,6 +30,7 @@ import java.util.Set;
|
|||||||
import org.jspecify.annotations.Nullable;
|
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.ResourceNotFoundException;
|
||||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||||
import org.springframework.data.elasticsearch.core.IndexInformation;
|
import org.springframework.data.elasticsearch.core.IndexInformation;
|
||||||
@ -315,15 +316,20 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TemplateData getTemplate(GetTemplateRequest getTemplateRequest) {
|
public @Nullable TemplateData getTemplate(GetTemplateRequest getTemplateRequest) {
|
||||||
|
|
||||||
Assert.notNull(getTemplateRequest, "getTemplateRequest must not be null");
|
Assert.notNull(getTemplateRequest, "getTemplateRequest must not be null");
|
||||||
|
|
||||||
co.elastic.clients.elasticsearch.indices.GetTemplateRequest getTemplateRequestES = requestConverter
|
co.elastic.clients.elasticsearch.indices.GetTemplateRequest getTemplateRequestES = requestConverter
|
||||||
.indicesGetTemplateRequest(getTemplateRequest);
|
.indicesGetTemplateRequest(getTemplateRequest);
|
||||||
GetTemplateResponse getTemplateResponse = execute(client -> client.getTemplate(getTemplateRequestES));
|
|
||||||
|
|
||||||
return responseConverter.indicesGetTemplateData(getTemplateResponse, getTemplateRequest.getTemplateName());
|
try {
|
||||||
|
GetTemplateResponse getTemplateResponse = execute(client -> client.getTemplate(getTemplateRequestES));
|
||||||
|
return responseConverter.indicesGetTemplateData(getTemplateResponse, getTemplateRequest.getTemplateName());
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// since Elasticsearch 9.1.0 we get an exception (404) instead of an empty result
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,12 +19,18 @@ import co.elastic.clients.json.JsonpMapper;
|
|||||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
import co.elastic.clients.transport.TransportOptions;
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
|
||||||
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
|
|
||||||
import org.elasticsearch.client.RequestOptions;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
|
||||||
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||||
@ -55,11 +61,11 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
|
|||||||
* @return RestClient
|
* @return RestClient
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
|
public Rest5Client elasticsearchRestClient(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
||||||
|
|
||||||
return ElasticsearchClients.getRestClient(clientConfiguration);
|
return Rest5Clients.getRest5Client(clientConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,12 +76,12 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
|
|||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
|
public ElasticsearchTransport elasticsearchTransport(Rest5Client rest5Client, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
Assert.notNull(restClient, "restClient must not be null");
|
Assert.notNull(rest5Client, "restClient must not be null");
|
||||||
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||||
|
|
||||||
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.REACTIVE_CLIENT,
|
return ElasticsearchClients.getElasticsearchTransport(rest5Client, ElasticsearchClients.REACTIVE_CLIENT,
|
||||||
transportOptions(), jsonpMapper);
|
transportOptions(), jsonpMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +116,7 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method and
|
* Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(Rest5Client, JsonpMapper)} method and
|
||||||
* exposes it as a bean.
|
* exposes it as a bean.
|
||||||
*
|
*
|
||||||
* @return the {@link JsonpMapper} to use
|
* @return the {@link JsonpMapper} to use
|
||||||
@ -118,13 +124,19 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public JsonpMapper jsonpMapper() {
|
public JsonpMapper jsonpMapper() {
|
||||||
return new JacksonJsonpMapper();
|
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
|
||||||
|
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
|
||||||
|
// into this mapper, so we can safely keep them here.
|
||||||
|
var objectMapper = (new ObjectMapper())
|
||||||
|
.configure(SerializationFeature.INDENT_OUTPUT, false)
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
return new JacksonJsonpMapper(objectMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the options that should be added to every request. Must not be {@literal null}
|
* @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 Rest5ClientOptions(RequestOptions.DEFAULT, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,14 +539,6 @@ 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));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.client.elc;
|
||||||
|
|
||||||
|
import co.elastic.clients.json.JsonpMapper;
|
||||||
|
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||||
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import org.elasticsearch.client.RequestOptions;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
|
||||||
|
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||||
|
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||||
|
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
|
||||||
|
* connection using the {@link ReactiveElasticsearchClient}. This class exposes different parts of the setup as Spring
|
||||||
|
* beans. Deriving * classes must provide the {@link ClientConfiguration} to use. <br/>
|
||||||
|
* This class uses the Elasticsearch RestClient which was replaced b y the Rest5Client in Elasticsearch 9. It is still
|
||||||
|
* available here but deprecated. *
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
* @since 4.4
|
||||||
|
* @deprecated since 6.0 use {@link ReactiveElasticsearchConfiguration}
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval=true)
|
||||||
|
public abstract class ReactiveElasticsearchLegacyRestClientConfiguration extends ElasticsearchConfigurationSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be implemented by deriving classes to provide the {@link ClientConfiguration}.
|
||||||
|
*
|
||||||
|
* @return configuration, must not be {@literal null}
|
||||||
|
*/
|
||||||
|
@Bean(name = "elasticsearchClientConfiguration")
|
||||||
|
public abstract ClientConfiguration clientConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the underlying low level RestClient.
|
||||||
|
*
|
||||||
|
* @param clientConfiguration configuration for the client, must not be {@literal null}
|
||||||
|
* @return RestClient
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
|
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
|
||||||
|
|
||||||
|
return RestClients.getRestClient(clientConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the Elasticsearch transport to be used. The default implementation uses the {@link RestClient} bean and
|
||||||
|
* the {@link JsonpMapper} bean provided in this class.
|
||||||
|
*
|
||||||
|
* @return the {@link ElasticsearchTransport}
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ElasticsearchTransport elasticsearchTransport(RestClient restClient, JsonpMapper jsonpMapper) {
|
||||||
|
|
||||||
|
Assert.notNull(restClient, "restClient must not be null");
|
||||||
|
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||||
|
|
||||||
|
return ElasticsearchClients.getElasticsearchTransport(restClient, ElasticsearchClients.REACTIVE_CLIENT,
|
||||||
|
transportOptions(), jsonpMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the {@link ReactiveElasticsearchClient} instance used.
|
||||||
|
*
|
||||||
|
* @param transport the ElasticsearchTransport to use
|
||||||
|
* @return ReactiveElasticsearchClient instance.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ReactiveElasticsearchClient reactiveElasticsearchClient(ElasticsearchTransport transport) {
|
||||||
|
|
||||||
|
Assert.notNull(transport, "transport must not be null");
|
||||||
|
|
||||||
|
return ElasticsearchClients.createReactive(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link ReactiveElasticsearchOperations}.
|
||||||
|
*
|
||||||
|
* @return never {@literal null}.
|
||||||
|
*/
|
||||||
|
@Bean(name = { "reactiveElasticsearchOperations", "reactiveElasticsearchTemplate" })
|
||||||
|
public ReactiveElasticsearchOperations reactiveElasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
|
||||||
|
ReactiveElasticsearchClient reactiveElasticsearchClient) {
|
||||||
|
|
||||||
|
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient,
|
||||||
|
elasticsearchConverter);
|
||||||
|
template.setRefreshPolicy(refreshPolicy());
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the JsonpMapper that is used in the {@link #elasticsearchTransport(RestClient, JsonpMapper)} method and
|
||||||
|
* exposes it as a bean.
|
||||||
|
*
|
||||||
|
* @return the {@link JsonpMapper} to use
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JsonpMapper jsonpMapper() {
|
||||||
|
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
|
||||||
|
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
|
||||||
|
// into this mapper, so we can safely keep them here.
|
||||||
|
var objectMapper = (new ObjectMapper())
|
||||||
|
.configure(SerializationFeature.INDENT_OUTPUT, false)
|
||||||
|
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
||||||
|
return new JacksonJsonpMapper(objectMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the options that should be added to every request. Must not be {@literal null}
|
||||||
|
*/
|
||||||
|
public TransportOptions transportOptions() {
|
||||||
|
return new RestClientOptions(RequestOptions.DEFAULT, false).toBuilder().build();
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
|||||||
import co.elastic.clients.json.JsonpMapper;
|
import co.elastic.clients.json.JsonpMapper;
|
||||||
import co.elastic.clients.transport.Version;
|
import co.elastic.clients.transport.Version;
|
||||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.util.function.Tuple2;
|
import reactor.util.function.Tuple2;
|
||||||
@ -162,7 +163,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
|||||||
ExistsRequest existsRequest = requestConverter.documentExistsRequest(id, routingResolver.getRouting(), index);
|
ExistsRequest existsRequest = requestConverter.documentExistsRequest(id, routingResolver.getRouting(), index);
|
||||||
|
|
||||||
return Mono.from(execute(
|
return Mono.from(execute(
|
||||||
((ClientCallback<Publisher<BooleanResponse>>) client -> client.exists(existsRequest))))
|
((ClientCallback<@NonNull Publisher<BooleanResponse>>) client -> client.exists(existsRequest))))
|
||||||
.map(BooleanResponse::value) //
|
.map(BooleanResponse::value) //
|
||||||
.onErrorReturn(NoSuchIndexException.class, false);
|
.onErrorReturn(NoSuchIndexException.class, false);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import co.elastic.clients.elasticsearch._types.AcknowledgedResponseBase;
|
|||||||
import co.elastic.clients.elasticsearch.indices.*;
|
import co.elastic.clients.elasticsearch.indices.*;
|
||||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||||
|
import org.springframework.data.elasticsearch.ResourceNotFoundException;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@ -389,7 +390,8 @@ public class ReactiveIndicesTemplate
|
|||||||
co.elastic.clients.elasticsearch.indices.GetTemplateRequest getTemplateRequestES = requestConverter
|
co.elastic.clients.elasticsearch.indices.GetTemplateRequest getTemplateRequestES = requestConverter
|
||||||
.indicesGetTemplateRequest(getTemplateRequest);
|
.indicesGetTemplateRequest(getTemplateRequest);
|
||||||
Mono<GetTemplateResponse> getTemplateResponse = Mono
|
Mono<GetTemplateResponse> getTemplateResponse = Mono
|
||||||
.from(execute(client -> client.getTemplate(getTemplateRequestES)));
|
.from(execute(client -> client.getTemplate(getTemplateRequestES)))
|
||||||
|
.onErrorComplete(ResourceNotFoundException.class);
|
||||||
|
|
||||||
return getTemplateResponse.flatMap(response -> {
|
return getTemplateResponse.flatMap(response -> {
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
|
@ -42,10 +42,10 @@ 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;
|
||||||
@ -55,6 +55,7 @@ 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;
|
||||||
|
|
||||||
@ -72,6 +73,7 @@ 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;
|
||||||
@ -118,9 +120,6 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
|
|
||||||
private static final Log LOGGER = LogFactory.getLog(RequestConverter.class);
|
private static final Log LOGGER = LogFactory.getLog(RequestConverter.class);
|
||||||
|
|
||||||
// the default max result window size of Elasticsearch
|
|
||||||
public static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
|
|
||||||
|
|
||||||
protected final JsonpMapper jsonpMapper;
|
protected final JsonpMapper jsonpMapper;
|
||||||
protected final ElasticsearchConverter elasticsearchConverter;
|
protected final ElasticsearchConverter elasticsearchConverter;
|
||||||
|
|
||||||
@ -724,12 +723,11 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
return a;
|
return a;
|
||||||
});
|
});
|
||||||
|
|
||||||
uob //
|
uob
|
||||||
.routing(query.getRouting()) //
|
.routing(query.getRouting())
|
||||||
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
|
.ifSeqNo(query.getIfSeqNo())
|
||||||
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
|
.ifPrimaryTerm(query.getIfPrimaryTerm())
|
||||||
.retryOnConflict(query.getRetryOnConflict()) //
|
.retryOnConflict(query.getRetryOnConflict());
|
||||||
;
|
|
||||||
|
|
||||||
// no refresh, timeout, waitForActiveShards on UpdateOperation or UpdateAction
|
// no refresh, timeout, waitForActiveShards on UpdateOperation or UpdateAction
|
||||||
|
|
||||||
@ -749,11 +747,10 @@ 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)
|
||||||
if (scriptData.type() == ScriptType.INLINE) {
|
.id(scriptData.scriptName());
|
||||||
sb.source(scriptData.script());
|
if (scriptData.script() != null) {
|
||||||
} else if (scriptData.type() == ScriptType.STORED) {
|
sb.source(s -> s.scriptString(scriptData.script()));
|
||||||
sb.id(scriptData.script());
|
|
||||||
}
|
}
|
||||||
return sb;
|
return sb;
|
||||||
});
|
});
|
||||||
@ -925,9 +922,13 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
|
|
||||||
ReindexRequest.Script script = reindexRequest.getScript();
|
ReindexRequest.Script script = reindexRequest.getScript();
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
builder.script(sb -> sb
|
builder.script(sb -> {
|
||||||
.lang(script.getLang())
|
if (script.getSource() != null) {
|
||||||
.source(script.getSource()));
|
sb.source(s -> s.scriptString(script.getSource()));
|
||||||
|
}
|
||||||
|
sb.lang(script.getLang());
|
||||||
|
return sb;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.timeout(time(reindexRequest.getTimeout())) //
|
builder.timeout(time(reindexRequest.getTimeout())) //
|
||||||
@ -1084,27 +1085,25 @@ 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) {
|
||||||
if (query.getScriptType() == ScriptType.INLINE) {
|
sb.source(s -> s.scriptString(query.getScript()));
|
||||||
sb.source(query.getScript()); //
|
|
||||||
} else if (query.getScriptType() == ScriptType.STORED) {
|
|
||||||
sb.id(query.getScript());
|
|
||||||
}
|
}
|
||||||
|
sb.id(query.getId());
|
||||||
|
|
||||||
return sb;
|
return sb;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uqb //
|
uqb
|
||||||
.doc(query.getDocument()) //
|
.doc(query.getDocument())
|
||||||
.upsert(query.getUpsert()) //
|
.upsert(query.getUpsert())
|
||||||
.routing(query.getRouting() != null ? query.getRouting() : routing) //
|
.routing(query.getRouting() != null ? query.getRouting() : routing)
|
||||||
.scriptedUpsert(query.getScriptedUpsert()) //
|
.scriptedUpsert(query.getScriptedUpsert())
|
||||||
.docAsUpsert(query.getDocAsUpsert()) //
|
.docAsUpsert(query.getDocAsUpsert())
|
||||||
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
|
.ifSeqNo(query.getIfSeqNo())
|
||||||
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
|
.ifPrimaryTerm(query.getIfPrimaryTerm())
|
||||||
.refresh(query.getRefreshPolicy() != null ? refresh(query.getRefreshPolicy()) : refresh(refreshPolicy)) //
|
.refresh(query.getRefreshPolicy() != null ? refresh(query.getRefreshPolicy()) : refresh(refreshPolicy))
|
||||||
.retryOnConflict(query.getRetryOnConflict()) //
|
.retryOnConflict(query.getRetryOnConflict());
|
||||||
;
|
|
||||||
|
|
||||||
if (query.getFetchSource() != null) {
|
if (query.getFetchSource() != null) {
|
||||||
uqb.source(sc -> sc.fetch(query.getFetchSource()));
|
uqb.source(sc -> sc.fetch(query.getFetchSource()));
|
||||||
@ -1252,11 +1251,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 //
|
bb.explain(query.getExplain()) //
|
||||||
.explain(query.getExplain()) //
|
.id(query.getId()); //
|
||||||
.id(query.getId()) //
|
if (query.getSource() != null) {
|
||||||
.source(query.getSource()) //
|
bb.source(s -> s.scriptString(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());
|
||||||
@ -1292,11 +1291,8 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
.timeout(timeStringMs(query.getTimeout())) //
|
.timeout(timeStringMs(query.getTimeout())) //
|
||||||
;
|
;
|
||||||
|
|
||||||
if (query.getPageable().isPaged()) {
|
bb.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
|
||||||
bb //
|
.size(query.getRequestSize());
|
||||||
.from((int) query.getPageable().getOffset()) //
|
|
||||||
.size(query.getPageable().getPageSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEmpty(query.getFields())) {
|
if (!isEmpty(query.getFields())) {
|
||||||
bb.fields(fb -> {
|
bb.fields(fb -> {
|
||||||
@ -1309,10 +1305,6 @@ 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());
|
||||||
}
|
}
|
||||||
@ -1347,7 +1339,9 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
|
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
rfb.script(s -> {
|
rfb.script(s -> {
|
||||||
s.source(script);
|
if (script != null) {
|
||||||
|
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()));
|
||||||
@ -1364,9 +1358,14 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEmpty(query.getIndicesBoost())) {
|
if (!isEmpty(query.getIndicesBoost())) {
|
||||||
bb.indicesBoost(query.getIndicesBoost().stream()
|
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream()
|
||||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
|
.map(indexBoost -> {
|
||||||
.collect(Collectors.toList()));
|
var namedValue = new NamedValue(indexBoost.getIndexName(),
|
||||||
|
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(),
|
||||||
@ -1465,13 +1464,8 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
builder.seqNoPrimaryTerm(true);
|
builder.seqNoPrimaryTerm(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.getPageable().isPaged()) {
|
builder.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
|
||||||
builder //
|
.size(query.getRequestSize());
|
||||||
.from((int) query.getPageable().getOffset()) //
|
|
||||||
.size(query.getPageable().getPageSize());
|
|
||||||
} else {
|
|
||||||
builder.from(0).size(INDEX_MAX_RESULT_WINDOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
@ -1486,10 +1480,6 @@ 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());
|
||||||
}
|
}
|
||||||
@ -1547,7 +1537,9 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
String script = runtimeField.getScript();
|
String script = runtimeField.getScript();
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
rfb.script(s -> {
|
rfb.script(s -> {
|
||||||
s.source(script);
|
if (script != null) {
|
||||||
|
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()));
|
||||||
@ -1575,9 +1567,14 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEmpty(query.getIndicesBoost())) {
|
if (!isEmpty(query.getIndicesBoost())) {
|
||||||
builder.indicesBoost(query.getIndicesBoost().stream()
|
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream()
|
||||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
|
.map(indexBoost -> {
|
||||||
.collect(Collectors.toList()));
|
var namedValue = new NamedValue(indexBoost.getIndexName(),
|
||||||
|
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())) {
|
||||||
@ -1639,7 +1636,7 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
builder.highlight(highlight);
|
builder.highlight(highlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addHighlight(Query query, MultisearchBody.Builder builder) {
|
private void addHighlight(Query query, SearchRequestBody.Builder builder) {
|
||||||
|
|
||||||
Highlight highlight = query.getHighlightQuery()
|
Highlight highlight = query.getHighlightQuery()
|
||||||
.map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext(), this)
|
.map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext(), this)
|
||||||
@ -1763,7 +1760,7 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
private void prepareNativeSearch(NativeQuery query, MultisearchBody.Builder builder) {
|
private void prepareNativeSearch(NativeQuery query, SearchRequestBody.Builder builder) {
|
||||||
|
|
||||||
builder //
|
builder //
|
||||||
.suggest(query.getSuggester()) //
|
.suggest(query.getSuggester()) //
|
||||||
@ -1882,10 +1879,11 @@ 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)) {
|
||||||
@ -1928,7 +1926,7 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
.id(script.id()) //
|
.id(script.id()) //
|
||||||
.script(sb -> sb //
|
.script(sb -> sb //
|
||||||
.lang(script.language()) //
|
.lang(script.language()) //
|
||||||
.source(script.source())));
|
.source(s -> s.scriptString(script.source()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetScriptRequest scriptGet(String name) {
|
public GetScriptRequest scriptGet(String name) {
|
||||||
|
@ -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(Double.parseDouble(healthResponse.activeShardsPercentAsNumber()))//
|
.withActiveShardsPercent(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.result();
|
Map<String, IndexMappingRecord> mappings = getMappingResponse.mappings();
|
||||||
|
|
||||||
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.result().forEach((indexName, indexState) -> {
|
getIndexResponse.indices().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.result().forEach((indexName, alias) -> {
|
getAliasResponse.aliases().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)));
|
||||||
@ -497,6 +497,10 @@ 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()));
|
||||||
}
|
}
|
||||||
@ -531,7 +535,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()).build() //
|
.withSource(response.script().source().scriptString()).build() //
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
@ -0,0 +1,308 @@
|
|||||||
|
package org.springframework.data.elasticsearch.client.elc.rest5_client;
|
||||||
|
|
||||||
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
|
import co.elastic.clients.transport.TransportUtils;
|
||||||
|
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
|
||||||
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5ClientBuilder;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
|
import org.apache.hc.client5.http.config.ConnectionConfig;
|
||||||
|
import org.apache.hc.client5.http.config.RequestConfig;
|
||||||
|
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
|
||||||
|
import org.apache.hc.core5.http.Header;
|
||||||
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
|
import org.apache.hc.core5.http.message.BasicHeader;
|
||||||
|
import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy;
|
||||||
|
import org.apache.hc.core5.util.Timeout;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.support.HttpHeaders;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class containing the functions to create the Elasticsearch Rest5Client used from Elasticsearch 9 on.
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public final class Rest5Clients {
|
||||||
|
|
||||||
|
// values copied from Rest5ClientBuilder
|
||||||
|
public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 30000;
|
||||||
|
public static final int DEFAULT_RESPONSE_TIMEOUT_MILLIS = 0; // meaning infinite
|
||||||
|
|
||||||
|
private Rest5Clients() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a low level {@link Rest5Client} for the given configuration.
|
||||||
|
*
|
||||||
|
* @param clientConfiguration must not be {@literal null}
|
||||||
|
* @return the {@link Rest5Client}
|
||||||
|
*/
|
||||||
|
public static Rest5Client getRest5Client(ClientConfiguration clientConfiguration) {
|
||||||
|
return getRest5ClientBuilder(clientConfiguration).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rest5ClientBuilder getRest5ClientBuilder(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
|
HttpHost[] httpHosts = getHttpHosts(clientConfiguration);
|
||||||
|
Rest5ClientBuilder builder = Rest5Client.builder(httpHosts);
|
||||||
|
|
||||||
|
if (clientConfiguration.getPathPrefix() != null) {
|
||||||
|
builder.setPathPrefix(clientConfiguration.getPathPrefix());
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
|
||||||
|
|
||||||
|
if (!headers.isEmpty()) {
|
||||||
|
builder.setDefaultHeaders(toHeaderArray(headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestClientBuilder configuration callbacks from the consumer
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurationCallback instanceof ElasticsearchRest5ClientConfigurationCallback configurationCallback) {
|
||||||
|
builder = configurationCallback.configure(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
||||||
|
Duration socketTimeout = clientConfiguration.getSocketTimeout();
|
||||||
|
|
||||||
|
builder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
|
||||||
|
|
||||||
|
if (clientConfiguration.getProxy().isPresent()) {
|
||||||
|
var proxy = clientConfiguration.getProxy().get();
|
||||||
|
try {
|
||||||
|
var proxyRoutePlanner = new DefaultProxyRoutePlanner(HttpHost.create(proxy));
|
||||||
|
httpAsyncClientBuilder.setRoutePlanner(proxyRoutePlanner);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpAsyncClientBuilder.addRequestInterceptorFirst((request, entity, context) -> {
|
||||||
|
clientConfiguration.getHeadersSupplier().get().forEach((header, values) -> {
|
||||||
|
// The accept and content-type headers are already put on the request, despite this being the first
|
||||||
|
// interceptor.
|
||||||
|
if ("Accept".equalsIgnoreCase(header) || " Content-Type".equalsIgnoreCase(header)) {
|
||||||
|
request.removeHeaders(header);
|
||||||
|
}
|
||||||
|
values.forEach(value -> request.addHeader(header, value));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// add httpclient configurator callbacks provided by the configuration
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurer instanceof ElasticsearchHttpClientConfigurationCallback httpClientConfigurer) {
|
||||||
|
httpAsyncClientBuilder = httpClientConfigurer.configure(httpAsyncClientBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.setConnectionConfigCallback(connectionConfigBuilder -> {
|
||||||
|
|
||||||
|
if (!connectTimeout.isNegative()) {
|
||||||
|
connectionConfigBuilder.setConnectTimeout(
|
||||||
|
Timeout.of(Math.toIntExact(connectTimeout.toMillis()), TimeUnit.MILLISECONDS));
|
||||||
|
}
|
||||||
|
if (!socketTimeout.isNegative()) {
|
||||||
|
var soTimeout = Timeout.of(Math.toIntExact(socketTimeout.toMillis()), TimeUnit.MILLISECONDS);
|
||||||
|
connectionConfigBuilder.setSocketTimeout(soTimeout);
|
||||||
|
} else {
|
||||||
|
connectionConfigBuilder.setSocketTimeout(Timeout.of(DEFAULT_SOCKET_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add connectionConfig configurator callbacks provided by the configuration
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurer instanceof ElasticsearchConnectionConfigurationCallback connectionConfigurationCallback) {
|
||||||
|
connectionConfigBuilder = connectionConfigurationCallback.configure(connectionConfigBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.setConnectionManagerCallback(poolingAsyncClientConnectionManagerBuilder -> {
|
||||||
|
|
||||||
|
SSLContext sslContext = null;
|
||||||
|
try {
|
||||||
|
sslContext = clientConfiguration.getCaFingerprint().isPresent()
|
||||||
|
? TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get())
|
||||||
|
: (clientConfiguration.getSslContext().isPresent()
|
||||||
|
? clientConfiguration.getSslContext().get()
|
||||||
|
: SSLContext.getDefault());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("could not create the default ssl context", e);
|
||||||
|
}
|
||||||
|
poolingAsyncClientConnectionManagerBuilder.setTlsStrategy(new BasicClientTlsStrategy(sslContext));
|
||||||
|
|
||||||
|
// add connectionManager configurator callbacks provided by the configuration
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurer instanceof ElasticsearchConnectionManagerCallback connectionManagerCallback) {
|
||||||
|
poolingAsyncClientConnectionManagerBuilder = connectionManagerCallback
|
||||||
|
.configure(poolingAsyncClientConnectionManagerBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.setRequestConfigCallback(requestConfigBuilder -> {
|
||||||
|
|
||||||
|
if (!socketTimeout.isNegative()) {
|
||||||
|
var soTimeout = Timeout.of(Math.toIntExact(socketTimeout.toMillis()), TimeUnit.MILLISECONDS);
|
||||||
|
requestConfigBuilder.setConnectionRequestTimeout(soTimeout);
|
||||||
|
} else {
|
||||||
|
requestConfigBuilder
|
||||||
|
.setConnectionRequestTimeout(Timeout.of(DEFAULT_RESPONSE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
|
||||||
|
}
|
||||||
|
// add connectionConfig configurator callbacks provided by the configuration
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurer instanceof ElasticsearchRequestConfigCallback requestConfigCallback) {
|
||||||
|
requestConfigBuilder = requestConfigCallback.configure(requestConfigBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
|
||||||
|
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
|
||||||
|
boolean useSsl = clientConfiguration.useSsl();
|
||||||
|
return hosts.stream()
|
||||||
|
.map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
|
||||||
|
.map(URI::create)
|
||||||
|
.map(HttpHost::create)
|
||||||
|
.toArray(HttpHost[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Header[] toHeaderArray(HttpHeaders headers) {
|
||||||
|
return headers.entrySet().stream() //
|
||||||
|
.flatMap(entry -> entry.getValue().stream() //
|
||||||
|
.map(value -> new BasicHeader(entry.getKey(), value))) //
|
||||||
|
.toList().toArray(new Header[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ClientConfiguration.ClientConfigurationCallback} to configure the Rest5Client client with a
|
||||||
|
* {@link Rest5ClientBuilder}
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchRest5ClientConfigurationCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<Rest5ClientBuilder> {
|
||||||
|
|
||||||
|
static ElasticsearchRest5ClientConfigurationCallback from(
|
||||||
|
Function<Rest5ClientBuilder, Rest5ClientBuilder> rest5ClientBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(rest5ClientBuilderCallback, "rest5ClientBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return rest5ClientBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the Elasticsearch Rest5Client's Http client with a {@link HttpAsyncClientBuilder}
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchHttpClientConfigurationCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
|
||||||
|
|
||||||
|
static Rest5Clients.ElasticsearchHttpClientConfigurationCallback from(
|
||||||
|
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return httpClientBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the Elasticsearch Rest5Client's connection with a {@link ConnectionConfig.Builder}
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchConnectionConfigurationCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<ConnectionConfig.Builder> {
|
||||||
|
|
||||||
|
static ElasticsearchConnectionConfigurationCallback from(
|
||||||
|
Function<ConnectionConfig.Builder, ConnectionConfig.Builder> connectionConfigBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(connectionConfigBuilderCallback, "connectionConfigBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return connectionConfigBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the Elasticsearch Rest5Client's connection manager with a {@link PoolingAsyncClientConnectionManagerBuilder}
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchConnectionManagerCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<PoolingAsyncClientConnectionManagerBuilder> {
|
||||||
|
|
||||||
|
static ElasticsearchConnectionManagerCallback from(
|
||||||
|
Function<PoolingAsyncClientConnectionManagerBuilder, PoolingAsyncClientConnectionManagerBuilder> connectionManagerBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(connectionManagerBuilderCallback, "connectionManagerBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return connectionManagerBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the Elasticsearch Rest5Client's connection manager with a {@link RequestConfig.Builder}
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchRequestConfigCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<RequestConfig.Builder> {
|
||||||
|
|
||||||
|
static ElasticsearchRequestConfigCallback from(
|
||||||
|
Function<RequestConfig.Builder, RequestConfig.Builder> requestConfigBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(requestConfigBuilderCallback, "requestConfigBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return requestConfigBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rest5ClientOptions.Builder getRest5ClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
|
||||||
|
|
||||||
|
if (transportOptions instanceof Rest5ClientOptions rest5ClientOptions) {
|
||||||
|
return rest5ClientOptions.toBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new Rest5ClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
|
||||||
|
|
||||||
|
if (transportOptions != null) {
|
||||||
|
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
|
||||||
|
transportOptions.queryParameters().forEach(builder::setParameter);
|
||||||
|
builder.onWarnings(transportOptions.onWarnings());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains related to the new (from Elasticsearch 9 on) Rest5Client. There are also classes copied over from Elasticsearch in order to have a Rest5ClientBuilder that allows to configure the http client.
|
||||||
|
*/
|
||||||
|
@org.jspecify.annotations.NullMarked
|
||||||
|
package org.springframework.data.elasticsearch.client.elc.rest5_client;
|
@ -0,0 +1,195 @@
|
|||||||
|
package org.springframework.data.elasticsearch.client.elc.rest_client;
|
||||||
|
|
||||||
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
|
import co.elastic.clients.transport.TransportUtils;
|
||||||
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.HttpRequestInterceptor;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.elasticsearch.client.RequestOptions;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.elasticsearch.client.RestClientBuilder;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
|
||||||
|
import org.springframework.data.elasticsearch.support.HttpHeaders;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class containing the functions to create the Elasticsearch RestClient used up to Elasticsearch 9.
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
* @deprecated since 6.0, use the new Rest5Client the code for that is in the package ../rest_client.
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
|
public final class RestClients {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a low level {@link RestClient} for the given configuration.
|
||||||
|
*
|
||||||
|
* @param clientConfiguration must not be {@literal null}
|
||||||
|
* @return the {@link RestClient}
|
||||||
|
*/
|
||||||
|
public static RestClient getRestClient(ClientConfiguration clientConfiguration) {
|
||||||
|
return getRestClientBuilder(clientConfiguration).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RestClientBuilder getRestClientBuilder(ClientConfiguration clientConfiguration) {
|
||||||
|
HttpHost[] httpHosts = getHttpHosts(clientConfiguration);
|
||||||
|
RestClientBuilder builder = RestClient.builder(httpHosts);
|
||||||
|
|
||||||
|
if (clientConfiguration.getPathPrefix() != null) {
|
||||||
|
builder.setPathPrefix(clientConfiguration.getPathPrefix());
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
|
||||||
|
|
||||||
|
if (!headers.isEmpty()) {
|
||||||
|
builder.setDefaultHeaders(toHeaderArray(headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setHttpClientConfigCallback(clientBuilder -> {
|
||||||
|
if (clientConfiguration.getCaFingerprint().isPresent()) {
|
||||||
|
clientBuilder
|
||||||
|
.setSSLContext(TransportUtils.sslContextFromCaFingerprint(clientConfiguration.getCaFingerprint().get()));
|
||||||
|
}
|
||||||
|
clientConfiguration.getSslContext().ifPresent(clientBuilder::setSSLContext);
|
||||||
|
clientConfiguration.getHostNameVerifier().ifPresent(clientBuilder::setSSLHostnameVerifier);
|
||||||
|
clientBuilder.addInterceptorLast(new CustomHeaderInjector(clientConfiguration.getHeadersSupplier()));
|
||||||
|
|
||||||
|
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
|
||||||
|
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
||||||
|
|
||||||
|
if (!connectTimeout.isNegative()) {
|
||||||
|
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Duration socketTimeout = clientConfiguration.getSocketTimeout();
|
||||||
|
|
||||||
|
if (!socketTimeout.isNegative()) {
|
||||||
|
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
||||||
|
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
||||||
|
}
|
||||||
|
|
||||||
|
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
|
||||||
|
|
||||||
|
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
|
||||||
|
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurer instanceof RestClients.ElasticsearchHttpClientConfigurationCallback restClientConfigurationCallback) {
|
||||||
|
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientBuilder;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurationCallback : clientConfiguration
|
||||||
|
.getClientConfigurers()) {
|
||||||
|
if (clientConfigurationCallback instanceof ElasticsearchRestClientConfigurationCallback configurationCallback) {
|
||||||
|
builder = configurationCallback.configure(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
|
||||||
|
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
|
||||||
|
boolean useSsl = clientConfiguration.useSsl();
|
||||||
|
return hosts.stream()
|
||||||
|
.map(it -> (useSsl ? "https" : "http") + "://" + it.getHostString() + ':' + it.getPort())
|
||||||
|
.map(HttpHost::create).toArray(HttpHost[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static org.apache.http.Header[] toHeaderArray(HttpHeaders headers) {
|
||||||
|
return headers.entrySet().stream() //
|
||||||
|
.flatMap(entry -> entry.getValue().stream() //
|
||||||
|
.map(value -> new BasicHeader(entry.getKey(), value))) //
|
||||||
|
.toArray(org.apache.http.Header[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor to inject custom supplied headers.
|
||||||
|
*
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
record CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) implements HttpRequestInterceptor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(HttpRequest request, HttpContext context) {
|
||||||
|
HttpHeaders httpHeaders = headersSupplier.get();
|
||||||
|
|
||||||
|
if (httpHeaders != null && !httpHeaders.isEmpty()) {
|
||||||
|
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
|
||||||
|
*
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchHttpClientConfigurationCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
|
||||||
|
|
||||||
|
static RestClients.ElasticsearchHttpClientConfigurationCallback from(
|
||||||
|
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return httpClientBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||||
|
* the RestClient client with a {@link RestClientBuilder}
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public interface ElasticsearchRestClientConfigurationCallback
|
||||||
|
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
|
||||||
|
|
||||||
|
static ElasticsearchRestClientConfigurationCallback from(
|
||||||
|
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
|
||||||
|
|
||||||
|
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
|
||||||
|
|
||||||
|
return restClientBuilderCallback::apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RestClientOptions.Builder getRestClientOptionsBuilder(@Nullable TransportOptions transportOptions) {
|
||||||
|
|
||||||
|
if (transportOptions instanceof RestClientOptions restClientOptions) {
|
||||||
|
return restClientOptions.toBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new RestClientOptions.Builder(RequestOptions.DEFAULT.toBuilder());
|
||||||
|
|
||||||
|
if (transportOptions != null) {
|
||||||
|
transportOptions.headers().forEach(header -> builder.addHeader(header.getKey(), header.getValue()));
|
||||||
|
transportOptions.queryParameters().forEach(builder::setParameter);
|
||||||
|
builder.onWarnings(transportOptions.onWarnings());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2021-2025 the original author or authors.
|
* Copyright 2022-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,15 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.query;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define script types for update queries.
|
* This package contains related to the old (up to Elasticsearch 9) RestClient.
|
||||||
*
|
|
||||||
* @author Farid Faoudi
|
|
||||||
* @since 4.2
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.0", forRemoval=true)
|
||||||
public enum ScriptType {
|
@org.jspecify.annotations.NullMarked
|
||||||
INLINE, STORED
|
package org.springframework.data.elasticsearch.client.elc.rest_client;
|
||||||
}
|
|
@ -233,6 +233,7 @@ 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) {
|
||||||
@ -242,6 +243,7 @@ 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(() -> {
|
||||||
@ -267,6 +269,10 @@ 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();
|
||||||
|
@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
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;
|
||||||
@ -369,7 +370,7 @@ public class EntityOperations {
|
|||||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#initializeVersionProperty()
|
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#initializeVersionProperty()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public T initializeVersionProperty() {
|
public @NonNull T initializeVersionProperty() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +390,7 @@ public class EntityOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SeqNoPrimaryTerm getSeqNoPrimaryTerm() {
|
public @Nullable SeqNoPrimaryTerm getSeqNoPrimaryTerm() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +399,7 @@ public class EntityOperations {
|
|||||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#incrementVersion()
|
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#incrementVersion()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public T incrementVersion() {
|
public @NonNull T incrementVersion() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +408,7 @@ public class EntityOperations {
|
|||||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getBean()
|
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getBean()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public T getBean() {
|
public @NonNull T getBean() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,12 +426,12 @@ public class EntityOperations {
|
|||||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getPersistentEntity()
|
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getPersistentEntity()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ElasticsearchPersistentEntity<?> getPersistentEntity() {
|
public @Nullable ElasticsearchPersistentEntity<?> getPersistentEntity() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRouting() {
|
public @Nullable String getRouting() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,7 +651,7 @@ public class EntityOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRouting() {
|
public @Nullable String getRouting() {
|
||||||
|
|
||||||
String routing = routingResolver.getRouting(propertyAccessor.getBean());
|
String routing = routingResolver.getRouting(propertyAccessor.getBean());
|
||||||
|
|
||||||
|
@ -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 List<String> matchedQueries = new ArrayList<>();
|
private final Map<String, Double> matchedQueries = new LinkedHashMap<>();
|
||||||
|
|
||||||
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 List<String> matchedQueries, T content) {
|
@Nullable Explanation explanation, @Nullable Map<String, Double> 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.addAll(matchedQueries);
|
this.matchedQueries.putAll(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 List<String> getMatchedQueries() {
|
public Map<String, Double> getMatchedQueries() {
|
||||||
return matchedQueries;
|
return matchedQueries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -714,7 +714,7 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getPropertyValue(ElasticsearchPersistentProperty property) {
|
public <T> @Nullable T getPropertyValue(ElasticsearchPersistentProperty property) {
|
||||||
|
|
||||||
String expression = property.getSpelExpression();
|
String expression = property.getSpelExpression();
|
||||||
Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property);
|
Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property);
|
||||||
|
@ -125,5 +125,5 @@ public interface SearchDocument extends Document {
|
|||||||
* @return the matched queries for the SearchHit.
|
* @return the matched queries for the SearchHit.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
List<String> getMatchedQueries();
|
Map<String, Double> getMatchedQueries();
|
||||||
}
|
}
|
||||||
|
@ -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 List<String> matchedQueries;
|
@Nullable private final Map<String, Double> 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 List<String> matchedQueries,
|
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation, @Nullable Map<String, Double> 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 List<String> getMatchedQueries() {
|
public Map<String, Double> getMatchedQueries() {
|
||||||
return matchedQueries;
|
return matchedQueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,8 +66,7 @@ public class AliasActionParameters {
|
|||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public String@Nullable[] getAliases() {
|
||||||
public String[] getAliases() {
|
|
||||||
return aliases;
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +106,8 @@ public class AliasActionParameters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
@Nullable private String[] indices;
|
private String @Nullable [] indices;
|
||||||
@Nullable private String[] aliases;
|
private String @Nullable [] aliases;
|
||||||
@Nullable private Query filterQuery;
|
@Nullable private Query filterQuery;
|
||||||
@Nullable private Class<?> filterQueryClass;
|
@Nullable private Class<?> filterQueryClass;
|
||||||
@Nullable private Boolean isHidden;
|
@Nullable private Boolean isHidden;
|
||||||
|
@ -55,6 +55,7 @@ public class AliasActions {
|
|||||||
public AliasActions add(@Nullable AliasAction... actions) {
|
public AliasActions add(@Nullable AliasAction... actions) {
|
||||||
|
|
||||||
if (actions != null) {
|
if (actions != null) {
|
||||||
|
// noinspection NullableProblems
|
||||||
this.actions.addAll(Arrays.asList(actions));
|
this.actions.addAll(Arrays.asList(actions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ 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.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.data.annotation.Transient;
|
import org.springframework.data.annotation.Transient;
|
||||||
@ -273,7 +274,7 @@ public class MappingBuilder {
|
|||||||
writeTypeHintMapping(propertiesNode);
|
writeTypeHintMapping(propertiesNode);
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
entity.doWithProperties((PropertyHandler<ElasticsearchPersistentProperty>) property -> {
|
entity.doWithProperties((PropertyHandler<@NonNull ElasticsearchPersistentProperty>) property -> {
|
||||||
try {
|
try {
|
||||||
if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) {
|
if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) {
|
||||||
return;
|
return;
|
||||||
|
@ -35,7 +35,7 @@ public record PutIndexTemplateRequest(String name, String[] indexPatterns, @Null
|
|||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
@Nullable private String name;
|
@Nullable private String name;
|
||||||
@Nullable private String[] indexPatterns;
|
private String @Nullable [] indexPatterns;
|
||||||
@Nullable private Settings settings;
|
@Nullable private Settings settings;
|
||||||
@Nullable private Document mapping;
|
@Nullable private Document mapping;
|
||||||
@Nullable AliasActions aliasActions;
|
@Nullable AliasActions aliasActions;
|
||||||
|
@ -81,7 +81,7 @@ public class TemplateData {
|
|||||||
@Nullable Document mapping;
|
@Nullable Document mapping;
|
||||||
int order;
|
int order;
|
||||||
@Nullable Integer version;
|
@Nullable Integer version;
|
||||||
@Nullable private String[] indexPatterns;
|
@Nullable private String@Nullable [] indexPatterns;
|
||||||
@Nullable private Map<String, AliasData> aliases;
|
@Nullable private Map<String, AliasData> aliases;
|
||||||
|
|
||||||
private TemplateDataBuilder() {}
|
private TemplateDataBuilder() {}
|
||||||
|
@ -65,7 +65,7 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
|
|||||||
String getIndexStoreType();
|
String getIndexStoreType();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ElasticsearchPersistentProperty getVersionProperty();
|
@Nullable ElasticsearchPersistentProperty getVersionProperty();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
String settingPath();
|
String settingPath();
|
||||||
|
@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
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.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
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;
|
||||||
@ -298,7 +299,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
|||||||
|
|
||||||
return fieldNamePropertyCache.computeIfAbsent(fieldName, key -> {
|
return fieldNamePropertyCache.computeIfAbsent(fieldName, key -> {
|
||||||
AtomicReference<ElasticsearchPersistentProperty> propertyRef = new AtomicReference<>();
|
AtomicReference<ElasticsearchPersistentProperty> propertyRef = new AtomicReference<>();
|
||||||
doWithProperties((PropertyHandler<ElasticsearchPersistentProperty>) property -> {
|
doWithProperties((PropertyHandler<@NonNull ElasticsearchPersistentProperty>) property -> {
|
||||||
if (key.equals(property.getFieldName())) {
|
if (key.equals(property.getFieldName())) {
|
||||||
propertyRef.set(property);
|
propertyRef.set(property);
|
||||||
}
|
}
|
||||||
@ -552,10 +553,10 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
|||||||
short replicas;
|
short replicas;
|
||||||
@Nullable String refreshIntervall;
|
@Nullable String refreshIntervall;
|
||||||
@Nullable String indexStoreType;
|
@Nullable String indexStoreType;
|
||||||
@Nullable private String[] sortFields;
|
private String @Nullable [] sortFields;
|
||||||
private Setting.@Nullable SortOrder[] sortOrders;
|
private Setting.@Nullable SortOrder @Nullable [] sortOrders;
|
||||||
private Setting.@Nullable SortMode[] sortModes;
|
private Setting.@Nullable SortMode @Nullable [] sortModes;
|
||||||
private Setting.@Nullable SortMissing[] sortMissingValues;
|
private Setting.@Nullable SortMissing @Nullable [] sortMissingValues;
|
||||||
|
|
||||||
Settings toSettings() {
|
Settings toSettings() {
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import java.util.Optional;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -47,10 +48,15 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class BaseQuery implements Query {
|
public class BaseQuery implements Query {
|
||||||
|
|
||||||
|
public static final int INDEX_MAX_RESULT_WINDOW = 10_000;
|
||||||
|
|
||||||
private static final int DEFAULT_REACTIVE_BATCH_SIZE = 500;
|
private static final int DEFAULT_REACTIVE_BATCH_SIZE = 500;
|
||||||
|
// the instance to mark the query pageable initial status, needed to distinguish between the initial
|
||||||
|
// value and a user-set unpaged value; values don't matter, the RequestConverter compares to the isntance.
|
||||||
|
private static final Pageable UNSET_PAGE = PageRequest.of(0, 1);
|
||||||
|
|
||||||
@Nullable protected Sort sort;
|
@Nullable protected Sort sort;
|
||||||
protected Pageable pageable = DEFAULT_PAGE;
|
protected Pageable pageable = UNSET_PAGE;
|
||||||
protected List<String> fields = new ArrayList<>();
|
protected List<String> fields = new ArrayList<>();
|
||||||
@Nullable protected List<String> storedFields;
|
@Nullable protected List<String> storedFields;
|
||||||
@Nullable protected SourceFilter sourceFilter;
|
@Nullable protected SourceFilter sourceFilter;
|
||||||
@ -78,7 +84,7 @@ public class BaseQuery implements Query {
|
|||||||
private boolean queryIsUpdatedByConverter = false;
|
private boolean queryIsUpdatedByConverter = false;
|
||||||
@Nullable private Integer reactiveBatchSize = null;
|
@Nullable private Integer reactiveBatchSize = null;
|
||||||
@Nullable private Boolean allowNoIndices = null;
|
@Nullable private Boolean allowNoIndices = null;
|
||||||
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
|
private EnumSet<IndicesOptions.WildcardStates> expandWildcards = EnumSet.noneOf(IndicesOptions.WildcardStates.class);
|
||||||
private List<DocValueField> docValueFields = new ArrayList<>();
|
private List<DocValueField> docValueFields = new ArrayList<>();
|
||||||
private List<ScriptedField> scriptedFields = new ArrayList<>();
|
private List<ScriptedField> scriptedFields = new ArrayList<>();
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ public class BaseQuery implements Query {
|
|||||||
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
|
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
|
||||||
this.sort = builder.getSort();
|
this.sort = builder.getSort();
|
||||||
// do a setPageable after setting the sort, because the pageable may contain an additional sort
|
// do a setPageable after setting the sort, because the pageable may contain an additional sort
|
||||||
this.setPageable(builder.getPageable() != null ? builder.getPageable() : DEFAULT_PAGE);
|
this.setPageable(builder.getPageable() != null ? builder.getPageable() : UNSET_PAGE);
|
||||||
this.fields = builder.getFields();
|
this.fields = builder.getFields();
|
||||||
this.storedFields = builder.getStoredFields();
|
this.storedFields = builder.getStoredFields();
|
||||||
this.sourceFilter = builder.getSourceFilter();
|
this.sourceFilter = builder.getSourceFilter();
|
||||||
@ -203,7 +209,7 @@ public class BaseQuery implements Query {
|
|||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final <T extends Query> T addSort(@Nullable Sort sort) {
|
public final <T extends Query> T addSort(@Nullable Sort sort) {
|
||||||
if (sort == null) {
|
if (sort == null || sort.isUnsorted()) {
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,4 +567,52 @@ public class BaseQuery implements Query {
|
|||||||
public List<ScriptedField> getScriptedFields() {
|
public List<ScriptedField> getScriptedFields() {
|
||||||
return scriptedFields;
|
return scriptedFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getRequestSize() {
|
||||||
|
|
||||||
|
var pageable = getPageable();
|
||||||
|
Integer requestSize = null;
|
||||||
|
|
||||||
|
if (pageable.isPaged() && pageable != UNSET_PAGE) {
|
||||||
|
// pagesize defined by the user
|
||||||
|
if (!isLimiting()) {
|
||||||
|
// no maxResults
|
||||||
|
requestSize = pageable.getPageSize();
|
||||||
|
} else {
|
||||||
|
// if we have both a page size and a max results, we take the min, this is necessary for
|
||||||
|
// 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.
|
||||||
|
requestSize = Math.min(pageable.getPageSize(), getMaxResults());
|
||||||
|
}
|
||||||
|
} else if (pageable == UNSET_PAGE) {
|
||||||
|
// no user defined pageable
|
||||||
|
if (isLimiting()) {
|
||||||
|
// maxResults
|
||||||
|
requestSize = getMaxResults();
|
||||||
|
} else {
|
||||||
|
requestSize = DEFAULT_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// explicitly set unpaged
|
||||||
|
if (!isLimiting()) {
|
||||||
|
// no maxResults
|
||||||
|
requestSize = INDEX_MAX_RESULT_WINDOW;
|
||||||
|
} else {
|
||||||
|
// if we have both a implicit page size and a max results, we take the min, this is necessary for
|
||||||
|
// 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.
|
||||||
|
requestSize = Math.min(INDEX_MAX_RESULT_WINDOW, getMaxResults());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestSize == null) {
|
||||||
|
// this should not happen
|
||||||
|
requestSize = DEFAULT_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
|||||||
/**
|
/**
|
||||||
* @since 5.1
|
* @since 5.1
|
||||||
*/
|
*/
|
||||||
public Integer getReactiveBatchSize() {
|
public @Nullable Integer getReactiveBatchSize() {
|
||||||
return reactiveBatchSize;
|
return reactiveBatchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,8 +344,8 @@ public class ByQueryResponse {
|
|||||||
*
|
*
|
||||||
* @return a new {@link SearchFailureBuilder} to build {@link SearchFailure}
|
* @return a new {@link SearchFailureBuilder} to build {@link SearchFailure}
|
||||||
*/
|
*/
|
||||||
public static SearchFailureBuilder builder() {
|
public static SearchFailureBuilder builder(Throwable reason) {
|
||||||
return new SearchFailureBuilder();
|
return new SearchFailureBuilder(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -358,7 +358,9 @@ public class ByQueryResponse {
|
|||||||
@Nullable private Integer shardId;
|
@Nullable private Integer shardId;
|
||||||
@Nullable private String nodeId;
|
@Nullable private String nodeId;
|
||||||
|
|
||||||
private SearchFailureBuilder() {}
|
private SearchFailureBuilder(Throwable reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
public SearchFailureBuilder withReason(Throwable reason) {
|
public SearchFailureBuilder withReason(Throwable reason) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
@ -63,12 +63,12 @@ public class FetchSourceFilter implements SourceFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getIncludes() {
|
public @Nullable String[] getIncludes() {
|
||||||
return includes;
|
return includes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getExcludes() {
|
public @Nullable String[] getExcludes() {
|
||||||
return excludes;
|
return excludes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ import org.jspecify.annotations.Nullable;
|
|||||||
public class FetchSourceFilterBuilder {
|
public class FetchSourceFilterBuilder {
|
||||||
|
|
||||||
@Nullable private Boolean fetchSource;
|
@Nullable private Boolean fetchSource;
|
||||||
@Nullable private String[] includes;
|
private String @Nullable [] includes;
|
||||||
@Nullable private String[] excludes;
|
private String @Nullable [] excludes;
|
||||||
|
|
||||||
public FetchSourceFilterBuilder withIncludes(String... includes) {
|
public FetchSourceFilterBuilder withIncludes(String... includes) {
|
||||||
this.includes = includes;
|
this.includes = includes;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.query;
|
package org.springframework.data.elasticsearch.core.query;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ public class GeoDistanceOrder extends Order {
|
|||||||
getIgnoreUnmapped());
|
getIgnoreUnmapped());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeoDistanceOrder with(Mode mode) {
|
public GeoDistanceOrder with(@Nullable Mode mode) {
|
||||||
return new GeoDistanceOrder(getProperty(), getGeoPoint(), getDirection(), getDistanceType(), mode, getUnit(),
|
return new GeoDistanceOrder(getProperty(), getGeoPoint(), getDirection(), getDistanceType(), mode, getUnit(),
|
||||||
getIgnoreUnmapped());
|
getIgnoreUnmapped());
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public class HasChildQuery {
|
|||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private final String type;
|
private final String type;
|
||||||
private Query query;
|
@Nullable private Query query;
|
||||||
|
|
||||||
@Nullable private Boolean ignoreUnmapped;
|
@Nullable private Boolean ignoreUnmapped;
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public class HasParentQuery {
|
|||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final String parentType;
|
private final String parentType;
|
||||||
private Query query;
|
@Nullable private Query query;
|
||||||
|
|
||||||
@Nullable private Boolean score;
|
@Nullable private Boolean score;
|
||||||
@Nullable private Boolean ignoreUnmapped;
|
@Nullable private Boolean ignoreUnmapped;
|
||||||
|
@ -484,6 +484,13 @@ public interface Query {
|
|||||||
*/
|
*/
|
||||||
List<ScriptedField> getScriptedFields();
|
List<ScriptedField> getScriptedFields();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of documents that should be requested from Elasticsearch in this query. Depends wether a
|
||||||
|
* Pageable and/or maxResult size is set on the query.
|
||||||
|
* @since 5.4.8 5.5.2
|
||||||
|
*/
|
||||||
|
public Integer getRequestSize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
|
@ -15,100 +15,110 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.query;
|
package org.springframework.data.elasticsearch.core.query;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* value class combining script information.
|
* value class combining script information.
|
||||||
|
* <p>
|
||||||
|
* A script is either an inline script, then the script parameters must be set
|
||||||
|
* or it refers to a stored script, then the name parameter is required.
|
||||||
*
|
*
|
||||||
|
* @param language the language when the script is passed in the script parameter
|
||||||
|
* @param script the script to use as inline script
|
||||||
|
* @param scriptName the name when using a stored script
|
||||||
|
* @param params the script parameters
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
*/
|
*/
|
||||||
public record ScriptData(ScriptType type, @Nullable String language, @Nullable String script,
|
public record ScriptData(@Nullable String language, @Nullable String script,
|
||||||
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
||||||
|
|
||||||
public ScriptData(ScriptType type, @Nullable String language, @Nullable String script, @Nullable String scriptName,
|
/*
|
||||||
@Nullable Map<String, Object> params) {
|
* constructor overload to check the parameters
|
||||||
|
*/
|
||||||
|
public ScriptData(@Nullable String language, @Nullable String script, @Nullable String scriptName,
|
||||||
|
@Nullable Map<String, Object> params) {
|
||||||
|
|
||||||
Assert.notNull(type, "type must not be null");
|
Assert.isTrue(script != null || scriptName != null, "script or scriptName is required");
|
||||||
|
|
||||||
this.type = type;
|
this.language = language;
|
||||||
this.language = language;
|
this.script = script;
|
||||||
this.script = script;
|
this.scriptName = scriptName;
|
||||||
this.scriptName = scriptName;
|
this.params = params;
|
||||||
this.params = params;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 5.2
|
* factory method to create a ScriptData object.
|
||||||
*/
|
*
|
||||||
public static ScriptData of(ScriptType type, @Nullable String language, @Nullable String script,
|
* @since 5.2
|
||||||
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
*/
|
||||||
return new ScriptData(type, language, script, scriptName, params);
|
public static ScriptData of(@Nullable String language, @Nullable String script,
|
||||||
}
|
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
||||||
|
return new ScriptData(language, script, scriptName, params);
|
||||||
|
}
|
||||||
|
|
||||||
public static ScriptData of(Function<Builder, Builder> builderFunction) {
|
/**
|
||||||
|
* factory method to create a ScriptData object using a ScriptBuilder callback.
|
||||||
|
*
|
||||||
|
* @param builderFunction function called to populate the builder
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ScriptData of(Function<Builder, Builder> builderFunction) {
|
||||||
|
|
||||||
Assert.notNull(builderFunction, "f must not be null");
|
Assert.notNull(builderFunction, "builderFunction must not be null");
|
||||||
|
|
||||||
return builderFunction.apply(new Builder()).build();
|
return builderFunction.apply(new Builder()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
@Nullable private ScriptType type;
|
@Nullable
|
||||||
@Nullable private String language;
|
private String language;
|
||||||
@Nullable private String script;
|
@Nullable
|
||||||
@Nullable private String scriptName;
|
private String script;
|
||||||
@Nullable private Map<String, Object> params;
|
@Nullable
|
||||||
|
private String scriptName;
|
||||||
|
@Nullable
|
||||||
|
private Map<String, Object> params;
|
||||||
|
|
||||||
private Builder() {}
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
public Builder withType(ScriptType type) {
|
public Builder withLanguage(@Nullable String language) {
|
||||||
|
this.language = language;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Assert.notNull(type, "type must not be null");
|
public Builder withScript(@Nullable String script) {
|
||||||
|
this.script = script;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
this.type = type;
|
public Builder withScriptName(@Nullable String scriptName) {
|
||||||
return this;
|
this.scriptName = scriptName;
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder withLanguage(@Nullable String language) {
|
public Builder withParams(@Nullable Map<String, Object> params) {
|
||||||
this.language = language;
|
this.params = params;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withScript(@Nullable String script) {
|
public ScriptData build() {
|
||||||
this.script = script;
|
return new ScriptData(language, script, scriptName, params);
|
||||||
return this;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withScriptName(@Nullable String scriptName) {
|
|
||||||
this.scriptName = scriptName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withParams(@Nullable Map<String, Object> params) {
|
|
||||||
this.params = params;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScriptData build() {
|
|
||||||
|
|
||||||
Assert.notNull(type, "type must be set");
|
|
||||||
|
|
||||||
return new ScriptData(type, language, script, scriptName, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -32,14 +32,14 @@ public interface SourceFilter {
|
|||||||
/**
|
/**
|
||||||
* @return the name of the fields to include in a response.
|
* @return the name of the fields to include in a response.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
|
||||||
String[] getIncludes();
|
String@Nullable[] getIncludes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the names of the fields to exclude from a response.
|
* @return the names of the fields to exclude from a response.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
|
||||||
String[] getExcludes();
|
String@Nullable[] getExcludes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to set the _source parameter in a query to true or false. If this is not null, the values returned from
|
* Flag to set the _source parameter in a query to true or false. If this is not null, the values returned from
|
||||||
|
@ -42,8 +42,8 @@ public class UpdateQuery {
|
|||||||
@Nullable private final Boolean fetchSource;
|
@Nullable private final Boolean fetchSource;
|
||||||
@Nullable private final List<String> fetchSourceIncludes;
|
@Nullable private final List<String> fetchSourceIncludes;
|
||||||
@Nullable private final List<String> fetchSourceExcludes;
|
@Nullable private final List<String> fetchSourceExcludes;
|
||||||
@Nullable private final Integer ifSeqNo;
|
@Nullable private final Long ifSeqNo;
|
||||||
@Nullable private final Integer ifPrimaryTerm;
|
@Nullable private final Long ifPrimaryTerm;
|
||||||
@Nullable private final RefreshPolicy refreshPolicy;
|
@Nullable private final RefreshPolicy refreshPolicy;
|
||||||
@Nullable private final Integer retryOnConflict;
|
@Nullable private final Integer retryOnConflict;
|
||||||
@Nullable private final String timeout;
|
@Nullable private final String timeout;
|
||||||
@ -71,12 +71,12 @@ public class UpdateQuery {
|
|||||||
private UpdateQuery(String id, @Nullable String script, @Nullable Map<String, Object> params,
|
private UpdateQuery(String id, @Nullable String script, @Nullable Map<String, Object> params,
|
||||||
@Nullable Document document, @Nullable Document upsert, @Nullable String lang, @Nullable String routing,
|
@Nullable Document document, @Nullable Document upsert, @Nullable String lang, @Nullable String routing,
|
||||||
@Nullable Boolean scriptedUpsert, @Nullable Boolean docAsUpsert, @Nullable Boolean fetchSource,
|
@Nullable Boolean scriptedUpsert, @Nullable Boolean docAsUpsert, @Nullable Boolean fetchSource,
|
||||||
@Nullable List<String> fetchSourceIncludes, @Nullable List<String> fetchSourceExcludes, @Nullable Integer ifSeqNo,
|
@Nullable List<String> fetchSourceIncludes, @Nullable List<String> fetchSourceExcludes, @Nullable Long ifSeqNo,
|
||||||
@Nullable Integer ifPrimaryTerm, @Nullable RefreshPolicy refreshPolicy, @Nullable Integer retryOnConflict,
|
@Nullable Long ifPrimaryTerm, @Nullable RefreshPolicy refreshPolicy, @Nullable Integer retryOnConflict,
|
||||||
@Nullable String timeout, @Nullable String waitForActiveShards, @Nullable Query query,
|
@Nullable String timeout, @Nullable String waitForActiveShards, @Nullable Query query,
|
||||||
@Nullable Boolean abortOnVersionConflict, @Nullable Integer batchSize, @Nullable Integer maxDocs,
|
@Nullable Boolean abortOnVersionConflict, @Nullable Integer batchSize, @Nullable Integer maxDocs,
|
||||||
@Nullable Integer maxRetries, @Nullable String pipeline, @Nullable Float requestsPerSecond,
|
@Nullable Integer maxRetries, @Nullable String pipeline, @Nullable Float requestsPerSecond,
|
||||||
@Nullable Boolean shouldStoreResult, @Nullable Integer slices, @Nullable ScriptType scriptType,
|
@Nullable Boolean shouldStoreResult, @Nullable Integer slices,
|
||||||
@Nullable String scriptName, @Nullable String indexName) {
|
@Nullable String scriptName, @Nullable String indexName) {
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -105,8 +105,8 @@ public class UpdateQuery {
|
|||||||
this.slices = slices;
|
this.slices = slices;
|
||||||
this.indexName = indexName;
|
this.indexName = indexName;
|
||||||
|
|
||||||
if (scriptType != null || lang != null || script != null || scriptName != null || params != null) {
|
if (lang != null || script != null || scriptName != null || params != null) {
|
||||||
this.scriptData = new ScriptData(scriptType, lang, script, scriptName, params);
|
this.scriptData = new ScriptData(lang, script, scriptName, params);
|
||||||
} else {
|
} else {
|
||||||
this.scriptData = null;
|
this.scriptData = null;
|
||||||
}
|
}
|
||||||
@ -172,12 +172,12 @@ public class UpdateQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Integer getIfSeqNo() {
|
public Long getIfSeqNo() {
|
||||||
return ifSeqNo;
|
return ifSeqNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Integer getIfPrimaryTerm() {
|
public Long getIfPrimaryTerm() {
|
||||||
return ifPrimaryTerm;
|
return ifPrimaryTerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,11 +246,6 @@ public class UpdateQuery {
|
|||||||
return slices;
|
return slices;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public ScriptType getScriptType() {
|
|
||||||
return scriptData != null ? scriptData.type() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getScriptName() {
|
public String getScriptName() {
|
||||||
return scriptData != null ? scriptData.scriptName() : null;
|
return scriptData != null ? scriptData.scriptName() : null;
|
||||||
@ -273,7 +268,7 @@ public class UpdateQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private String id;
|
private String id = "";
|
||||||
@Nullable private String script = null;
|
@Nullable private String script = null;
|
||||||
@Nullable private Map<String, Object> params;
|
@Nullable private Map<String, Object> params;
|
||||||
@Nullable private Document document = null;
|
@Nullable private Document document = null;
|
||||||
@ -283,8 +278,8 @@ public class UpdateQuery {
|
|||||||
@Nullable private Boolean scriptedUpsert;
|
@Nullable private Boolean scriptedUpsert;
|
||||||
@Nullable private Boolean docAsUpsert;
|
@Nullable private Boolean docAsUpsert;
|
||||||
@Nullable private Boolean fetchSource;
|
@Nullable private Boolean fetchSource;
|
||||||
@Nullable private Integer ifSeqNo;
|
@Nullable private Long ifSeqNo;
|
||||||
@Nullable private Integer ifPrimaryTerm;
|
@Nullable private Long ifPrimaryTerm;
|
||||||
@Nullable private RefreshPolicy refreshPolicy;
|
@Nullable private RefreshPolicy refreshPolicy;
|
||||||
@Nullable private Integer retryOnConflict;
|
@Nullable private Integer retryOnConflict;
|
||||||
@Nullable private String timeout;
|
@Nullable private String timeout;
|
||||||
@ -300,7 +295,6 @@ public class UpdateQuery {
|
|||||||
@Nullable private Float requestsPerSecond;
|
@Nullable private Float requestsPerSecond;
|
||||||
@Nullable private Boolean shouldStoreResult;
|
@Nullable private Boolean shouldStoreResult;
|
||||||
@Nullable private Integer slices;
|
@Nullable private Integer slices;
|
||||||
@Nullable private ScriptType scriptType;
|
|
||||||
@Nullable private String scriptName;
|
@Nullable private String scriptName;
|
||||||
@Nullable private String indexName;
|
@Nullable private String indexName;
|
||||||
|
|
||||||
@ -357,12 +351,12 @@ public class UpdateQuery {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withIfSeqNo(Integer ifSeqNo) {
|
public Builder withIfSeqNo(Long ifSeqNo) {
|
||||||
this.ifSeqNo = ifSeqNo;
|
this.ifSeqNo = ifSeqNo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withIfPrimaryTerm(Integer ifPrimaryTerm) {
|
public Builder withIfPrimaryTerm(Long ifPrimaryTerm) {
|
||||||
this.ifPrimaryTerm = ifPrimaryTerm;
|
this.ifPrimaryTerm = ifPrimaryTerm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -437,11 +431,6 @@ public class UpdateQuery {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withScriptType(ScriptType scriptType) {
|
|
||||||
this.scriptType = scriptType;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder withScriptName(String scriptName) {
|
public Builder withScriptName(String scriptName) {
|
||||||
this.scriptName = scriptName;
|
this.scriptName = scriptName;
|
||||||
return this;
|
return this;
|
||||||
@ -456,7 +445,7 @@ public class UpdateQuery {
|
|||||||
return new UpdateQuery(id, script, params, document, upsert, lang, routing, scriptedUpsert, docAsUpsert,
|
return new UpdateQuery(id, script, params, document, upsert, lang, routing, scriptedUpsert, docAsUpsert,
|
||||||
fetchSource, fetchSourceIncludes, fetchSourceExcludes, ifSeqNo, ifPrimaryTerm, refreshPolicy, retryOnConflict,
|
fetchSource, fetchSourceIncludes, fetchSourceExcludes, ifSeqNo, ifPrimaryTerm, refreshPolicy, retryOnConflict,
|
||||||
timeout, waitForActiveShards, query, abortOnVersionConflict, batchSize, maxDocs, maxRetries, pipeline,
|
timeout, waitForActiveShards, query, abortOnVersionConflict, batchSize, maxDocs, maxRetries, pipeline,
|
||||||
requestsPerSecond, shouldStoreResult, slices, scriptType, scriptName, indexName);
|
requestsPerSecond, shouldStoreResult, slices, scriptName, indexName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withIndex(@Nullable String indexName) {
|
public Builder withIndex(@Nullable String indexName) {
|
||||||
|
@ -249,16 +249,15 @@ public class ReindexRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Script {
|
public static class Script {
|
||||||
private final String source;
|
@Nullable private final String source;
|
||||||
@Nullable private final String lang;
|
@Nullable private final String lang;
|
||||||
|
|
||||||
private Script(String source, @Nullable String lang) {
|
private Script(@Nullable String source, @Nullable String lang) {
|
||||||
Assert.notNull(source, "source must not be null");
|
|
||||||
|
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getSource() {
|
public String getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ public record Script(String id, String language, String source) {
|
|||||||
Assert.notNull(id, "id must not be null");
|
Assert.notNull(id, "id must not be null");
|
||||||
Assert.notNull(language, "language must not be null");
|
Assert.notNull(language, "language must not be null");
|
||||||
Assert.notNull(source, "source must not be null");
|
Assert.notNull(source, "source must not be null");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ScriptBuilder builder() {
|
public static ScriptBuilder builder() {
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.repository.query;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||||
@ -33,7 +34,7 @@ import org.springframework.util.Assert;
|
|||||||
public class ReactiveRepositorySearchTemplateQuery extends AbstractReactiveElasticsearchRepositoryQuery {
|
public class ReactiveRepositorySearchTemplateQuery extends AbstractReactiveElasticsearchRepositoryQuery {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private Map<String, Object> params;
|
private Map<String, Object> params = Map.of();
|
||||||
|
|
||||||
public ReactiveRepositorySearchTemplateQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
public ReactiveRepositorySearchTemplateQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||||
ReactiveElasticsearchOperations elasticsearchOperations,
|
ReactiveElasticsearchOperations elasticsearchOperations,
|
||||||
|
@ -33,7 +33,7 @@ import org.springframework.util.Assert;
|
|||||||
public class RepositorySearchTemplateQuery extends AbstractElasticsearchRepositoryQuery {
|
public class RepositorySearchTemplateQuery extends AbstractElasticsearchRepositoryQuery {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private Map<String, Object> params;
|
private Map<String, Object> params = Map.of();
|
||||||
|
|
||||||
public RepositorySearchTemplateQuery(ElasticsearchQueryMethod queryMethod,
|
public RepositorySearchTemplateQuery(ElasticsearchQueryMethod queryMethod,
|
||||||
ElasticsearchOperations elasticsearchOperations, ValueExpressionDelegate valueExpressionDelegate,
|
ElasticsearchOperations elasticsearchOperations, ValueExpressionDelegate valueExpressionDelegate,
|
||||||
|
@ -82,7 +82,7 @@ class ExampleCriteriaMapper {
|
|||||||
|
|
||||||
Object propertyValue = propertyAccessor.getProperty(property);
|
Object propertyValue = propertyAccessor.getProperty(property);
|
||||||
if (property.isMap() && propertyValue != null) {
|
if (property.isMap() && propertyValue != null) {
|
||||||
for (Map.Entry<String, Object> entry : ((Map<String, Object>) propertyValue).entrySet()) {
|
for (Map.Entry<String, @Nullable Object> entry : ((Map<String, @Nullable Object>) propertyValue).entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
Object value = entry.getValue();
|
Object value = entry.getValue();
|
||||||
criteria = applyPropertySpec(propertyPath + "." + key, value, exampleSpecAccessor, property, matchMode,
|
criteria = applyPropertySpec(propertyPath + "." + key, value, exampleSpecAccessor, property, matchMode,
|
||||||
@ -96,7 +96,7 @@ class ExampleCriteriaMapper {
|
|||||||
return criteria;
|
return criteria;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Criteria applyPropertySpec(String path, Object propertyValue, ExampleMatcherAccessor exampleSpecAccessor,
|
private Criteria applyPropertySpec(String path, @Nullable Object propertyValue, ExampleMatcherAccessor exampleSpecAccessor,
|
||||||
ElasticsearchPersistentProperty property, ExampleMatcher.MatchMode matchMode, Criteria criteria) {
|
ElasticsearchPersistentProperty property, ExampleMatcher.MatchMode matchMode, Criteria criteria) {
|
||||||
|
|
||||||
if (exampleSpecAccessor.isIgnoreCaseForPath(path)) {
|
if (exampleSpecAccessor.isIgnoreCaseForPath(path)) {
|
||||||
|
@ -47,13 +47,14 @@ public class QueryByExampleElasticsearchExecutor<T> implements QueryByExampleExe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S extends T> Optional<S> findOne(Example<S> example) {
|
public <S extends T> Optional<S> findOne(Example<S> example) {
|
||||||
CriteriaQuery criteriaQuery = CriteriaQuery.builder(exampleCriteriaMapper.criteria(example)).withMaxResults(2).build();
|
CriteriaQuery criteriaQuery = CriteriaQuery.builder(exampleCriteriaMapper.criteria(example)).withMaxResults(2)
|
||||||
|
.build();
|
||||||
SearchHits<S> searchHits = operations.search(criteriaQuery, example.getProbeType(),
|
SearchHits<S> searchHits = operations.search(criteriaQuery, example.getProbeType(),
|
||||||
operations.getIndexCoordinatesFor(example.getProbeType()));
|
operations.getIndexCoordinatesFor(example.getProbeType()));
|
||||||
if (searchHits.getTotalHits() > 1) {
|
if (searchHits.getTotalHits() > 1) {
|
||||||
throw new org.springframework.dao.IncorrectResultSizeDataAccessException(1);
|
throw new org.springframework.dao.IncorrectResultSizeDataAccessException(1);
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(searchHits).filter(SearchHits::hasSearchHits)
|
return Optional.of(searchHits).filter(SearchHits::hasSearchHits)
|
||||||
.map(result -> (List<S>) SearchHitSupport.unwrapSearchHits(result)).map(s -> s.get(0));
|
.map(result -> (List<S>) SearchHitSupport.unwrapSearchHits(result)).map(s -> s.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +39,11 @@ import org.springframework.util.MultiValueMapAdapter;
|
|||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class HttpHeaders implements MultiValueMap<String, String> {
|
public class HttpHeaders implements MultiValueMap<String, @Nullable String> {
|
||||||
|
|
||||||
public static final String AUTHORIZATION = "Authorization";
|
public static final String AUTHORIZATION = "Authorization";
|
||||||
|
|
||||||
private final MultiValueMap<String, String> delegate;
|
private final MultiValueMap<String, @Nullable String> delegate;
|
||||||
|
|
||||||
public HttpHeaders() {
|
public HttpHeaders() {
|
||||||
this.delegate = new MultiValueMapAdapter<>(new LinkedCaseInsensitiveMap<>(Locale.ENGLISH));
|
this.delegate = new MultiValueMapAdapter<>(new LinkedCaseInsensitiveMap<>(Locale.ENGLISH));
|
||||||
@ -62,12 +62,12 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAll(String key, List<? extends String> values) {
|
public void addAll(String key, List<? extends @Nullable String> values) {
|
||||||
delegate.addAll(key, values);
|
delegate.addAll(key, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAll(MultiValueMap<String, String> values) {
|
public void addAll(MultiValueMap<String, @Nullable String> values) {
|
||||||
delegate.addAll(values);
|
delegate.addAll(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +77,12 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAll(Map<String, String> values) {
|
public void setAll(Map<String, @Nullable String> values) {
|
||||||
delegate.setAll(values);
|
delegate.setAll(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> toSingleValueMap() {
|
public Map<String, @Nullable String> toSingleValueMap() {
|
||||||
return delegate.toSingleValueMap();
|
return delegate.toSingleValueMap();
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
@ -110,18 +110,18 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public List<String> get(Object key) {
|
public List<@Nullable String> get(Object key) {
|
||||||
return delegate.get(key);
|
return delegate.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public List<String> put(String key, List<String> value) {
|
public List<@Nullable String> put(String key, List<@Nullable String> value) {
|
||||||
return delegate.put(key, value);
|
return delegate.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> remove(Object key) {
|
public List<@Nullable String> remove(Object key) {
|
||||||
return delegate.remove(key);
|
return delegate.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +92,17 @@ public final class VersionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a String to use in a header, containing the versions of Spring Data Elasticsearch and the Elasticsearch
|
||||||
|
* library
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
public static String clientVersions() {
|
||||||
|
return String.format("spring-data-elasticsearch %s / elasticsearch client %s",
|
||||||
|
versionProperties.getProperty(VERSION_SPRING_DATA_ELASTICSEARCH),
|
||||||
|
versionProperties.getProperty(VERSION_ELASTICSEARCH_CLIENT));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the version properties from the classpath resource.
|
* gets the version properties from the classpath resource.
|
||||||
*
|
*
|
||||||
|
@ -24,4 +24,4 @@ import org.springframework.data.repository.kotlin.CoroutineSortingRepository
|
|||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
@NoRepositoryBean
|
@NoRepositoryBean
|
||||||
interface CoroutineElasticsearchRepository<T, ID> : CoroutineCrudRepository<T, ID>, CoroutineSortingRepository<T, ID>
|
interface CoroutineElasticsearchRepository<T : Any, ID : Any> : CoroutineCrudRepository<T, ID>, CoroutineSortingRepository<T, ID>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Spring Data Elasticsearch 6.0 M2 (2025.1.0)
|
Spring Data Elasticsearch 6.0 M4 (2025.1.0)
|
||||||
Copyright (c) [2013-2022] Pivotal Software, Inc.
|
Copyright (c) [2013-2022] Pivotal Software, Inc.
|
||||||
|
|
||||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||||
@ -9,3 +9,5 @@ separate copyright notices and license terms. Your use of the source
|
|||||||
code for the these subcomponents is subject to the terms and
|
code for the these subcomponents is subject to the terms and
|
||||||
conditions of the subcomponent's license, as noted in the LICENSE file.
|
conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2025 the original author or authors.
|
* Copyright 2019-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.client;
|
package org.springframework.data.elasticsearch.client;
|
||||||
|
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||||
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.*;
|
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.*;
|
||||||
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.*;
|
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.*;
|
||||||
@ -40,6 +41,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
|
||||||
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
|
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
|
||||||
import org.springframework.data.elasticsearch.support.HttpHeaders;
|
import org.springframework.data.elasticsearch.support.HttpHeaders;
|
||||||
|
|
||||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||||
@ -100,8 +103,11 @@ public class RestClientsTest {
|
|||||||
defaultHeaders.add("def2", "def2-1");
|
defaultHeaders.add("def2", "def2-1");
|
||||||
|
|
||||||
AtomicInteger supplierCount = new AtomicInteger(1);
|
AtomicInteger supplierCount = new AtomicInteger(1);
|
||||||
AtomicInteger httpClientConfigurerCount = new AtomicInteger(0);
|
|
||||||
AtomicInteger restClientConfigurerCount = new AtomicInteger(0);
|
AtomicInteger restClientConfigurerCount = new AtomicInteger(0);
|
||||||
|
AtomicInteger httpClientConfigurerCount = new AtomicInteger(0);
|
||||||
|
AtomicInteger connectionConfigurerCount = new AtomicInteger(0);
|
||||||
|
AtomicInteger connectionManagerConfigurerCount = new AtomicInteger(0);
|
||||||
|
AtomicInteger requestConfigurerCount = new AtomicInteger(0);
|
||||||
|
|
||||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||||
configurationBuilder //
|
configurationBuilder //
|
||||||
@ -115,25 +121,52 @@ public class RestClientsTest {
|
|||||||
return httpHeaders;
|
return httpHeaders;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (clientUnderTestFactory instanceof ELCUnderTestFactory) {
|
if (clientUnderTestFactory instanceof ELCRest5ClientUnderTestFactory) {
|
||||||
configurationBuilder.withClientConfigurer(
|
configurationBuilder.withClientConfigurer(
|
||||||
ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpClientBuilder -> {
|
Rest5Clients.ElasticsearchRest5ClientConfigurationCallback.from(rest5ClientBuilder -> {
|
||||||
|
restClientConfigurerCount.incrementAndGet();
|
||||||
|
return rest5ClientBuilder;
|
||||||
|
}));
|
||||||
|
configurationBuilder.withClientConfigurer(
|
||||||
|
Rest5Clients.ElasticsearchHttpClientConfigurationCallback.from(httpClientBuilder -> {
|
||||||
httpClientConfigurerCount.incrementAndGet();
|
httpClientConfigurerCount.incrementAndGet();
|
||||||
return httpClientBuilder;
|
return httpClientBuilder;
|
||||||
}));
|
}));
|
||||||
configurationBuilder.withClientConfigurer(
|
configurationBuilder.withClientConfigurer(
|
||||||
ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
Rest5Clients.ElasticsearchConnectionConfigurationCallback.from(connectionConfigBuilder -> {
|
||||||
|
connectionConfigurerCount.incrementAndGet();
|
||||||
|
return connectionConfigBuilder;
|
||||||
|
}));
|
||||||
|
configurationBuilder.withClientConfigurer(
|
||||||
|
Rest5Clients.ElasticsearchConnectionManagerCallback.from(connectionManagerBuilder -> {
|
||||||
|
connectionManagerConfigurerCount.incrementAndGet();
|
||||||
|
return connectionManagerBuilder;
|
||||||
|
}));
|
||||||
|
configurationBuilder.withClientConfigurer(
|
||||||
|
Rest5Clients.ElasticsearchRequestConfigCallback.from(requestConfigBuilder -> {
|
||||||
|
requestConfigurerCount.incrementAndGet();
|
||||||
|
return requestConfigBuilder;
|
||||||
|
}));
|
||||||
|
} else if (clientUnderTestFactory instanceof ELCRestClientUnderTestFactory) {
|
||||||
|
configurationBuilder.withClientConfigurer(
|
||||||
|
RestClients.ElasticsearchHttpClientConfigurationCallback.from(httpClientBuilder -> {
|
||||||
|
httpClientConfigurerCount.incrementAndGet();
|
||||||
|
return httpClientBuilder;
|
||||||
|
}));
|
||||||
|
configurationBuilder.withClientConfigurer(
|
||||||
|
RestClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
||||||
restClientConfigurerCount.incrementAndGet();
|
restClientConfigurerCount.incrementAndGet();
|
||||||
return restClientBuilder;
|
return restClientBuilder;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
} else if (clientUnderTestFactory instanceof ReactiveELCUnderTestFactory) {
|
} else if (clientUnderTestFactory instanceof ReactiveELCUnderTestFactory) {
|
||||||
configurationBuilder
|
configurationBuilder
|
||||||
.withClientConfigurer(ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(webClient -> {
|
.withClientConfigurer(RestClients.ElasticsearchHttpClientConfigurationCallback.from(webClient -> {
|
||||||
httpClientConfigurerCount.incrementAndGet();
|
httpClientConfigurerCount.incrementAndGet();
|
||||||
return webClient;
|
return webClient;
|
||||||
}));
|
}));
|
||||||
configurationBuilder.withClientConfigurer(
|
configurationBuilder.withClientConfigurer(
|
||||||
ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
RestClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
|
||||||
restClientConfigurerCount.incrementAndGet();
|
restClientConfigurerCount.incrementAndGet();
|
||||||
return restClientBuilder;
|
return restClientBuilder;
|
||||||
}));
|
}));
|
||||||
@ -155,19 +188,24 @@ public class RestClientsTest {
|
|||||||
.withHeader("def2", new EqualToPattern("def2-1")) //
|
.withHeader("def2", new EqualToPattern("def2-1")) //
|
||||||
.withHeader("supplied", new EqualToPattern("val0")) //
|
.withHeader("supplied", new EqualToPattern("val0")) //
|
||||||
// on the first call Elasticsearch does the version check and thus already increments the counter
|
// on the first call Elasticsearch does the version check and thus already increments the counter
|
||||||
.withHeader("supplied", new EqualToPattern("val" + (i))) //
|
.withHeader("supplied", new EqualToPattern("val" + i)) //
|
||||||
);
|
.withHeader("supplied", including("val0", "val" + i)));
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertThat(restClientConfigurerCount).hasValue(clientUnderTestFactory.getExpectedRestClientConfigurerCalls());
|
||||||
assertThat(httpClientConfigurerCount).hasValue(1);
|
assertThat(httpClientConfigurerCount).hasValue(1);
|
||||||
assertThat(restClientConfigurerCount).hasValue(clientUnderTestFactory.getExpectedRestClientConfigCalls());
|
assertThat(connectionConfigurerCount).hasValue(clientUnderTestFactory.getExpectedConnectionConfigurerCalls());
|
||||||
|
assertThat(connectionManagerConfigurerCount)
|
||||||
|
.hasValue(clientUnderTestFactory.getExpectedConnectionManagerConfigurerCalls());
|
||||||
|
assertThat(requestConfigurerCount).hasValue(clientUnderTestFactory.getExpectedRequestConfigurerCalls());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest // #2088
|
@ParameterizedTest // #2088
|
||||||
@MethodSource("clientUnderTestFactorySource")
|
@MethodSource("clientUnderTestFactorySource")
|
||||||
@DisplayName("should set compatibility headers")
|
@DisplayName("should set explicit compatibility headers")
|
||||||
void shouldSetCompatibilityHeaders(ClientUnderTestFactory clientUnderTestFactory) {
|
void shouldSetExplicitCompatibilityHeaders(ClientUnderTestFactory clientUnderTestFactory) {
|
||||||
|
|
||||||
wireMockServer(server -> {
|
wireMockServer(server -> {
|
||||||
|
|
||||||
@ -190,7 +228,8 @@ public class RestClientsTest {
|
|||||||
}
|
}
|
||||||
""" //
|
""" //
|
||||||
, 201) //
|
, 201) //
|
||||||
.withHeader("Content-Type", "application/vnd.elasticsearch+json;compatible-with=7") //
|
.withHeader("Content-Type",
|
||||||
|
"application/vnd.elasticsearch+json;compatible-with=7") //
|
||||||
.withHeader("X-Elastic-Product", "Elasticsearch")));
|
.withHeader("X-Elastic-Product", "Elasticsearch")));
|
||||||
|
|
||||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||||
@ -198,8 +237,8 @@ public class RestClientsTest {
|
|||||||
.connectedTo("localhost:" + server.port()) //
|
.connectedTo("localhost:" + server.port()) //
|
||||||
.withHeaders(() -> {
|
.withHeaders(() -> {
|
||||||
HttpHeaders defaultCompatibilityHeaders = new HttpHeaders();
|
HttpHeaders defaultCompatibilityHeaders = new HttpHeaders();
|
||||||
defaultCompatibilityHeaders.add("Accept", "application/vnd.elasticsearch+json;compatible-with=7");
|
defaultCompatibilityHeaders.add("Accept", "application/vnd.elasticsearch+json; compatible-with=7");
|
||||||
defaultCompatibilityHeaders.add("Content-Type", "application/vnd.elasticsearch+json;compatible-with=7");
|
defaultCompatibilityHeaders.add("Content-Type", "application/vnd.elasticsearch+json; compatible-with=7");
|
||||||
return defaultCompatibilityHeaders;
|
return defaultCompatibilityHeaders;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -214,11 +253,66 @@ public class RestClientsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clientUnderTest.index(new Foo("42"));
|
clientUnderTest.index(new Foo("42"));
|
||||||
|
|
||||||
verify(putRequestedFor(urlMatching(urlPattern)) //
|
verify(putRequestedFor(urlMatching(urlPattern)) //
|
||||||
.withHeader("Accept", new EqualToPattern("application/vnd.elasticsearch+json;compatible-with=7")) //
|
.withHeader("Accept", new EqualToPattern("application/vnd.elasticsearch+json; compatible-with=7"))
|
||||||
.withHeader("Content-Type", new EqualToPattern("application/vnd.elasticsearch+json;compatible-with=7")) //
|
.withHeader("Content-Type", new EqualToPattern("application/vnd.elasticsearch+json; compatible-with=7")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("clientUnderTestFactorySource")
|
||||||
|
@DisplayName("should set implicit compatibility headers")
|
||||||
|
void shouldSetImplicitCompatibilityHeaders(ClientUnderTestFactory clientUnderTestFactory) {
|
||||||
|
|
||||||
|
wireMockServer(server -> {
|
||||||
|
|
||||||
|
String urlPattern = "^/index/_doc/42(\\?.*)?$";
|
||||||
|
var elasticsearchMajorVersion = clientUnderTestFactory.getElasticsearchMajorVersion();
|
||||||
|
stubFor(put(urlMatching(urlPattern)) //
|
||||||
|
.willReturn(jsonResponse("""
|
||||||
|
{
|
||||||
|
"_id": "42",
|
||||||
|
"_index": "test",
|
||||||
|
"_primary_term": 1,
|
||||||
|
"_seq_no": 0,
|
||||||
|
"_shards": {
|
||||||
|
"failed": 0,
|
||||||
|
"successful": 1,
|
||||||
|
"total": 2
|
||||||
|
},
|
||||||
|
"_type": "_doc",
|
||||||
|
"_version": 1,
|
||||||
|
"result": "created"
|
||||||
|
}
|
||||||
|
""" //
|
||||||
|
, 201) //
|
||||||
|
.withHeader("Content-Type",
|
||||||
|
"application/vnd.elasticsearch+json; compatible-with=" + elasticsearchMajorVersion) //
|
||||||
|
.withHeader("X-Elastic-Product", "Elasticsearch")));
|
||||||
|
|
||||||
|
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||||
|
configurationBuilder.connectedTo("localhost:" + server.port());
|
||||||
|
|
||||||
|
ClientConfiguration clientConfiguration = configurationBuilder.build();
|
||||||
|
ClientUnderTest clientUnderTest = clientUnderTestFactory.create(clientConfiguration);
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public final String id;
|
||||||
|
|
||||||
|
Foo(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientUnderTest.index(new Foo("42"));
|
||||||
|
|
||||||
|
verify(putRequestedFor(urlMatching(urlPattern)) //
|
||||||
|
.withHeader("Accept",
|
||||||
|
new EqualToPattern("application/vnd.elasticsearch+json; compatible-with=" + elasticsearchMajorVersion)) //
|
||||||
|
.withHeader("Content-Type",
|
||||||
|
new EqualToPattern("application/vnd.elasticsearch+json; compatible-with=" + elasticsearchMajorVersion)) //
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -280,6 +374,7 @@ public class RestClientsTest {
|
|||||||
private void wireMockServer(WiremockConsumer consumer) {
|
private void wireMockServer(WiremockConsumer consumer) {
|
||||||
WireMockServer wireMockServer = new WireMockServer(options() //
|
WireMockServer wireMockServer = new WireMockServer(options() //
|
||||||
.dynamicPort() //
|
.dynamicPort() //
|
||||||
|
// .notifier(new ConsoleNotifier(true)) // for debugging output
|
||||||
.usingFilesUnderDirectory("src/test/resources/wiremock-mappings")); // needed, otherwise Wiremock goes to
|
.usingFilesUnderDirectory("src/test/resources/wiremock-mappings")); // needed, otherwise Wiremock goes to
|
||||||
// test/resources/mappings
|
// test/resources/mappings
|
||||||
try {
|
try {
|
||||||
@ -295,7 +390,7 @@ public class RestClientsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The client to be tested. Abstraction to be able to test reactive and non-reactive clients.
|
* The client to be tested. Abstraction to be able to test reactive and non-reactive clients in different versions.
|
||||||
*/
|
*/
|
||||||
interface ClientUnderTest {
|
interface ClientUnderTest {
|
||||||
/**
|
/**
|
||||||
@ -329,26 +424,61 @@ public class RestClientsTest {
|
|||||||
|
|
||||||
protected abstract String getDisplayName();
|
protected abstract String getDisplayName();
|
||||||
|
|
||||||
protected Integer getExpectedRestClientConfigCalls() {
|
protected Integer getExpectedRestClientConfigurerCalls() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int getElasticsearchMajorVersion();
|
||||||
|
|
||||||
|
public Integer getExpectedConnectionConfigurerCalls() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getExpectedConnectionManagerConfigurerCalls() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getExpectedRequestConfigurerCalls() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ClientUnderTestFactory} implementation for the {@link co.elastic.clients.elasticsearch.ElasticsearchClient}.
|
* {@link ClientUnderTestFactory} implementation for the {@link co.elastic.clients.elasticsearch.ElasticsearchClient}
|
||||||
|
* using the Rest5_Client.
|
||||||
*/
|
*/
|
||||||
static class ELCUnderTestFactory extends ClientUnderTestFactory {
|
static class ELCRest5ClientUnderTestFactory extends ClientUnderTestFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDisplayName() {
|
protected String getDisplayName() {
|
||||||
return "ElasticsearchClient";
|
return "ElasticsearchRest5Client";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Integer getExpectedRestClientConfigCalls() {
|
protected Integer getExpectedRestClientConfigurerCalls() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getExpectedConnectionConfigurerCalls() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getExpectedConnectionManagerConfigurerCalls() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getExpectedRequestConfigurerCalls() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getElasticsearchMajorVersion() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ClientUnderTest create(ClientConfiguration clientConfiguration) {
|
ClientUnderTest create(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
@ -372,6 +502,51 @@ public class RestClientsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ClientUnderTestFactory} implementation for the {@link co.elastic.clients.elasticsearch.ElasticsearchClient}
|
||||||
|
* using the old Rest_Client.
|
||||||
|
*/
|
||||||
|
static class ELCRestClientUnderTestFactory extends ClientUnderTestFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDisplayName() {
|
||||||
|
return "ElasticsearchRestClient";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer getExpectedRestClientConfigurerCalls() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getElasticsearchMajorVersion() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ClientUnderTest create(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
|
var restClient = RestClients.getRestClient(clientConfiguration);
|
||||||
|
ElasticsearchClient client = ElasticsearchClients.createImperative(restClient);
|
||||||
|
return new ClientUnderTest() {
|
||||||
|
@Override
|
||||||
|
public boolean ping() throws Exception {
|
||||||
|
return client.ping().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean usesInitialRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void index(T entity) throws IOException {
|
||||||
|
client.index(ir -> ir.index("index").id("42").document(entity));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ClientUnderTestFactory} implementation for the {@link ReactiveElasticsearchClient}.
|
* {@link ClientUnderTestFactory} implementation for the {@link ReactiveElasticsearchClient}.
|
||||||
*/
|
*/
|
||||||
@ -383,10 +558,15 @@ public class RestClientsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Integer getExpectedRestClientConfigCalls() {
|
protected Integer getExpectedRestClientConfigurerCalls() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getElasticsearchMajorVersion() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ClientUnderTest create(ClientConfiguration clientConfiguration) {
|
ClientUnderTest create(ClientConfiguration clientConfiguration) {
|
||||||
|
|
||||||
@ -416,8 +596,9 @@ public class RestClientsTest {
|
|||||||
* @return stream of factories
|
* @return stream of factories
|
||||||
*/
|
*/
|
||||||
static Stream<ClientUnderTestFactory> clientUnderTestFactorySource() {
|
static Stream<ClientUnderTestFactory> clientUnderTestFactorySource() {
|
||||||
return Stream.of( //
|
return Stream.of(
|
||||||
new ELCUnderTestFactory(), //
|
new ELCRestClientUnderTestFactory(),
|
||||||
|
new ELCRest5ClientUnderTestFactory(),
|
||||||
new ReactiveELCUnderTestFactory());
|
new ReactiveELCUnderTestFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
|||||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||||
import co.elastic.clients.transport.TransportOptions;
|
import co.elastic.clients.transport.TransportOptions;
|
||||||
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
import co.elastic.clients.transport.rest_client.RestClientOptions;
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest5_client.Rest5Clients;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.rest_client.RestClients;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -85,7 +88,7 @@ public class DevTests {
|
|||||||
private final ReactiveElasticsearchClient reactiveElasticsearchClient = ElasticsearchClients
|
private final ReactiveElasticsearchClient reactiveElasticsearchClient = ElasticsearchClients
|
||||||
.createReactive(clientConfiguration(), transportOptions);
|
.createReactive(clientConfiguration(), transportOptions);
|
||||||
private final ElasticsearchClient imperativeElasticsearchClient = ElasticsearchClients
|
private final ElasticsearchClient imperativeElasticsearchClient = ElasticsearchClients
|
||||||
.createImperative(ElasticsearchClients.getRestClient(clientConfiguration()), transportOptions, jsonpMapper);
|
.createImperative(Rest5Clients.getRest5Client(clientConfiguration()), transportOptions, jsonpMapper);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void someTest() throws IOException {
|
void someTest() throws IOException {
|
||||||
|
@ -23,6 +23,7 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.assertj.core.api.SoftAssertions;
|
import org.assertj.core.api.SoftAssertions;
|
||||||
import org.assertj.core.data.Offset;
|
import org.assertj.core.data.Offset;
|
||||||
@ -144,17 +145,17 @@ class DocumentAdaptersUnitTests {
|
|||||||
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
|
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
|
||||||
.index("index") //
|
.index("index") //
|
||||||
.id("42") //
|
.id("42") //
|
||||||
.matchedQueries("query1", "query2") //
|
.matchedQueries("query1", 1D) //
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
|
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
|
||||||
|
|
||||||
SoftAssertions softly = new SoftAssertions();
|
SoftAssertions softly = new SoftAssertions();
|
||||||
|
|
||||||
List<String> matchedQueries = searchDocument.getMatchedQueries();
|
Map<String, Double> matchedQueries = searchDocument.getMatchedQueries();
|
||||||
softly.assertThat(matchedQueries).isNotNull();
|
softly.assertThat(matchedQueries).isNotNull();
|
||||||
softly.assertThat(matchedQueries).hasSize(2);
|
softly.assertThat(matchedQueries).hasSize(1);
|
||||||
softly.assertThat(matchedQueries).isEqualTo(Arrays.asList("query1", "query2"));
|
softly.assertThat(matchedQueries).isEqualTo(Map.of("query1",1D));
|
||||||
softly.assertAll();
|
softly.assertAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.client.elc;
|
package org.springframework.data.elasticsearch.client.elc.rest5_client;
|
||||||
|
|
||||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||||
@ -29,6 +29,7 @@ import org.springframework.data.annotation.Id;
|
|||||||
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.client.ClientConfiguration;
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
|
||||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("UastIncorrectHttpHeaderInspection")
|
@SuppressWarnings("UastIncorrectHttpHeaderInspection")
|
||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
public class ELCWiremockTests {
|
public class ELCRest5ClientWiremockTests {
|
||||||
|
|
||||||
@RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance()
|
@RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance()
|
||||||
.options(wireMockConfig()
|
.options(wireMockConfig()
|
||||||
@ -69,7 +70,7 @@ public class ELCWiremockTests {
|
|||||||
wireMock.stubFor(put(urlPathEqualTo("/null-fields/_doc/42"))
|
wireMock.stubFor(put(urlPathEqualTo("/null-fields/_doc/42"))
|
||||||
.withRequestBody(equalToJson("""
|
.withRequestBody(equalToJson("""
|
||||||
{
|
{
|
||||||
"_class": "org.springframework.data.elasticsearch.client.elc.ELCWiremockTests$EntityWithNullFields",
|
"_class": "org.springframework.data.elasticsearch.client.elc.rest5_client.ELCRest5ClientWiremockTests$EntityWithNullFields",
|
||||||
"id": "42",
|
"id": "42",
|
||||||
"field1": null
|
"field1": null
|
||||||
}
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.client.elc.rest_client;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchLegacyRestClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that need to check the data produced by the Elasticsearch client
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UastIncorrectHttpHeaderInspection")
|
||||||
|
@Deprecated(since = "6.0", forRemoval = true)
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
public class ELCRestClientWiremockTests {
|
||||||
|
|
||||||
|
@RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance()
|
||||||
|
.options(wireMockConfig()
|
||||||
|
.dynamicPort()
|
||||||
|
// needed, otherwise Wiremock goes to test/resources/mappings
|
||||||
|
.usingFilesUnderDirectory("src/test/resources/wiremock-mappings"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class Config extends ElasticsearchLegacyRestClientConfiguration {
|
||||||
|
@Override
|
||||||
|
public ClientConfiguration clientConfiguration() {
|
||||||
|
return ClientConfiguration.builder()
|
||||||
|
.connectedTo("localhost:" + wireMock.getPort())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired ElasticsearchOperations operations;
|
||||||
|
|
||||||
|
@Test // #2839
|
||||||
|
@DisplayName("should store null values if configured")
|
||||||
|
void shouldStoreNullValuesIfConfigured() {
|
||||||
|
|
||||||
|
wireMock.stubFor(put(urlPathEqualTo("/null-fields/_doc/42"))
|
||||||
|
.withRequestBody(equalToJson("""
|
||||||
|
{
|
||||||
|
"_class": "org.springframework.data.elasticsearch.client.elc.rest_client.ELCRestClientWiremockTests$EntityWithNullFields",
|
||||||
|
"id": "42",
|
||||||
|
"field1": null
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.willReturn(
|
||||||
|
aResponse()
|
||||||
|
.withStatus(200)
|
||||||
|
.withHeader("X-elastic-product", "Elasticsearch")
|
||||||
|
.withHeader("content-type", "application/vnd.elasticsearch+json;compatible-with=8")
|
||||||
|
.withBody("""
|
||||||
|
{
|
||||||
|
"_index": "null-fields",
|
||||||
|
"_id": "42",
|
||||||
|
"_version": 1,
|
||||||
|
"result": "created",
|
||||||
|
"forced_refresh": true,
|
||||||
|
"_shards": {
|
||||||
|
"total": 2,
|
||||||
|
"successful": 1,
|
||||||
|
"failed": 0
|
||||||
|
},
|
||||||
|
"_seq_no": 1,
|
||||||
|
"_primary_term": 1
|
||||||
|
}
|
||||||
|
""")));
|
||||||
|
|
||||||
|
var entity = new EntityWithNullFields();
|
||||||
|
entity.setId("42");
|
||||||
|
|
||||||
|
operations.save(entity);
|
||||||
|
// no need to assert anything, if the field1:null is not sent, we run into a 404 error
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "null-fields")
|
||||||
|
static class EntityWithNullFields {
|
||||||
|
@Nullable
|
||||||
|
@Id private String id;
|
||||||
|
@Nullable
|
||||||
|
@Field(storeNullValue = true) private String field1;
|
||||||
|
@Nullable
|
||||||
|
@Field private String field2;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(@Nullable String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getField1() {
|
||||||
|
return field1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setField1(@Nullable String field1) {
|
||||||
|
this.field1 = field1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getField2() {
|
||||||
|
return field2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setField2(@Nullable String field2) {
|
||||||
|
this.field2 = field2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
@Deprecated(since = "6.0", forRemoval=true)
|
||||||
|
package org.springframework.data.elasticsearch.client.elc.rest_client;
|
@ -19,7 +19,8 @@ import static org.assertj.core.api.Assertions.*;
|
|||||||
|
|
||||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||||
|
|
||||||
import org.elasticsearch.client.RestClient;
|
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -50,7 +51,7 @@ public class ElasticsearchConfigurationELCTests {
|
|||||||
considerNestedRepositories = true)
|
considerNestedRepositories = true)
|
||||||
static class Config extends ElasticsearchConfiguration {
|
static class Config extends ElasticsearchConfiguration {
|
||||||
@Override
|
@Override
|
||||||
public ClientConfiguration clientConfiguration() {
|
public @NonNull ClientConfiguration clientConfiguration() {
|
||||||
return ClientConfiguration.builder() //
|
return ClientConfiguration.builder() //
|
||||||
.connectedTo("localhost:9200") //
|
.connectedTo("localhost:9200") //
|
||||||
.build();
|
.build();
|
||||||
@ -61,7 +62,7 @@ public class ElasticsearchConfigurationELCTests {
|
|||||||
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
|
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
|
||||||
* and just check that all the necessary beans are created.
|
* and just check that all the necessary beans are created.
|
||||||
*/
|
*/
|
||||||
@Autowired private RestClient restClient;
|
@Autowired private Rest5Client rest5Client;
|
||||||
@Autowired private ElasticsearchClient elasticsearchClient;
|
@Autowired private ElasticsearchClient elasticsearchClient;
|
||||||
@Autowired private ElasticsearchOperations elasticsearchOperations;
|
@Autowired private ElasticsearchOperations elasticsearchOperations;
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ public class ElasticsearchConfigurationELCTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providesRequiredBeans() {
|
public void providesRequiredBeans() {
|
||||||
assertThat(restClient).isNotNull();
|
assertThat(rest5Client).isNotNull();
|
||||||
assertThat(elasticsearchClient).isNotNull();
|
assertThat(elasticsearchClient).isNotNull();
|
||||||
assertThat(elasticsearchOperations).isNotNull();
|
assertThat(elasticsearchOperations).isNotNull();
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.config.configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||||
|
|
||||||
|
import org.elasticsearch.client.RestClient;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ElasticsearchLegacyRestClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||||
|
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||||
|
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ElasticsearchConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@ContextConfiguration
|
||||||
|
public class ElasticsearchLegacyRestClientConfigurationELCTests {
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.config.configuration" },
|
||||||
|
considerNestedRepositories = true)
|
||||||
|
static class Config extends ElasticsearchLegacyRestClientConfiguration {
|
||||||
|
@Override
|
||||||
|
public @NonNull ClientConfiguration clientConfiguration() {
|
||||||
|
return ClientConfiguration.builder() //
|
||||||
|
.connectedTo("localhost:9200") //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
|
||||||
|
* and just check that all the necessary beans are created.
|
||||||
|
*/
|
||||||
|
@Autowired private RestClient restClient;
|
||||||
|
@Autowired private ElasticsearchClient elasticsearchClient;
|
||||||
|
@Autowired private ElasticsearchOperations elasticsearchOperations;
|
||||||
|
|
||||||
|
@Autowired private CreateIndexFalseRepository repository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void providesRequiredBeans() {
|
||||||
|
assertThat(restClient).isNotNull();
|
||||||
|
assertThat(elasticsearchClient).isNotNull();
|
||||||
|
assertThat(elasticsearchOperations).isNotNull();
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "test-index-config-configuration", createIndex = false)
|
||||||
|
static class CreateIndexFalseEntity {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Id private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateIndexFalseRepository extends ElasticsearchRepository<CreateIndexFalseEntity, String> {}
|
||||||
|
}
|
@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.config.configuration;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
@ -49,7 +50,7 @@ public class ReactiveElasticsearchConfigurationELCTests {
|
|||||||
static class Config extends ReactiveElasticsearchConfiguration {
|
static class Config extends ReactiveElasticsearchConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientConfiguration clientConfiguration() {
|
public @NonNull ClientConfiguration clientConfiguration() {
|
||||||
return ClientConfiguration.builder() //
|
return ClientConfiguration.builder() //
|
||||||
.connectedTo("localhost:9200") //
|
.connectedTo("localhost:9200") //
|
||||||
.build();
|
.build();
|
||||||
@ -66,7 +67,6 @@ public class ReactiveElasticsearchConfigurationELCTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providesRequiredBeans() {
|
public void providesRequiredBeans() {
|
||||||
// assertThat(webClient).isNotNull();
|
|
||||||
assertThat(reactiveElasticsearchClient).isNotNull();
|
assertThat(reactiveElasticsearchClient).isNotNull();
|
||||||
assertThat(reactiveElasticsearchOperations).isNotNull();
|
assertThat(reactiveElasticsearchOperations).isNotNull();
|
||||||
assertThat(repository).isNotNull();
|
assertThat(repository).isNotNull();
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-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.config.configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.Document;
|
||||||
|
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchLegacyRestClientConfiguration;
|
||||||
|
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||||
|
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
|
||||||
|
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@ContextConfiguration
|
||||||
|
public class ReactiveElasticsearchLegacyRestClientConfigurationELCTests {
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableReactiveElasticsearchRepositories(
|
||||||
|
basePackages = { "org.springframework.data.elasticsearch.config.configuration" },
|
||||||
|
considerNestedRepositories = true)
|
||||||
|
static class Config extends ReactiveElasticsearchLegacyRestClientConfiguration {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull ClientConfiguration clientConfiguration() {
|
||||||
|
return ClientConfiguration.builder() //
|
||||||
|
.connectedTo("localhost:9200") //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* using a repository with an entity that is set to createIndex = false as we have no elastic running for this test
|
||||||
|
* and just check that all the necessary beans are created.
|
||||||
|
*/
|
||||||
|
@Autowired private ReactiveElasticsearchClient reactiveElasticsearchClient;
|
||||||
|
@Autowired private ReactiveElasticsearchOperations reactiveElasticsearchOperations;
|
||||||
|
@Autowired private CreateIndexFalseRepository repository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void providesRequiredBeans() {
|
||||||
|
// assertThat(webClient).isNotNull();
|
||||||
|
assertThat(reactiveElasticsearchClient).isNotNull();
|
||||||
|
assertThat(reactiveElasticsearchOperations).isNotNull();
|
||||||
|
assertThat(repository).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Document(indexName = "test-index-config-configuration", createIndex = false)
|
||||||
|
static class CreateIndexFalseEntity {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Id private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateIndexFalseRepository extends ReactiveElasticsearchRepository<CreateIndexFalseEntity, String> {}
|
||||||
|
}
|
@ -71,10 +71,10 @@ public abstract class EnableNestedRepositoriesIntegrationTests {
|
|||||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private Double scriptedRate;
|
@ScriptedField private Double scriptedRate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private String highlightedMessage;
|
@Nullable private String highlightedMessage;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -114,10 +114,10 @@ public abstract class EnableRepositoriesIntegrationTests implements ApplicationC
|
|||||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private Double scriptedRate;
|
@ScriptedField private Double scriptedRate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private String highlightedMessage;
|
@Nullable private String highlightedMessage;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -208,10 +208,10 @@ public abstract class EnableRepositoriesIntegrationTests implements ApplicationC
|
|||||||
@Nullable private String type;
|
@Nullable private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = FieldType.Text, fielddata = true) private String message;
|
@Field(type = FieldType.Text, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private Long scriptedRate;
|
@ScriptedField private Long scriptedRate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private String highlightedMessage;
|
@Nullable private String highlightedMessage;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -46,7 +46,6 @@ import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
|||||||
import org.springframework.data.elasticsearch.core.query.Query;
|
import org.springframework.data.elasticsearch.core.query.Query;
|
||||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptType;
|
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||||
@ -183,7 +182,7 @@ public class ElasticsearchELCIntegrationTests extends ElasticsearchIntegrationTe
|
|||||||
|
|
||||||
return nativeQueryBuilder.withScriptedField(new ScriptedField( //
|
return nativeQueryBuilder.withScriptedField(new ScriptedField( //
|
||||||
fieldName, //
|
fieldName, //
|
||||||
new ScriptData(ScriptType.INLINE, "expression", script, null, params))) //
|
new ScriptData( "expression", script, null, params))) //
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +104,6 @@ import org.springframework.data.util.StreamUtils;
|
|||||||
@SpringIntegrationTest
|
@SpringIntegrationTest
|
||||||
public abstract class ElasticsearchIntegrationTests {
|
public abstract class ElasticsearchIntegrationTests {
|
||||||
|
|
||||||
static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
|
|
||||||
|
|
||||||
private static final String MULTI_INDEX_PREFIX = "test-index";
|
private static final String MULTI_INDEX_PREFIX = "test-index";
|
||||||
private static final String MULTI_INDEX_ALL = MULTI_INDEX_PREFIX + "*";
|
private static final String MULTI_INDEX_ALL = MULTI_INDEX_PREFIX + "*";
|
||||||
private static final String MULTI_INDEX_1_NAME = MULTI_INDEX_PREFIX + "-1";
|
private static final String MULTI_INDEX_1_NAME = MULTI_INDEX_PREFIX + "-1";
|
||||||
@ -1630,12 +1628,13 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
final Query query = operations.matchAllQuery();
|
final Query query = operations.matchAllQuery();
|
||||||
|
|
||||||
final UpdateQuery updateQuery = UpdateQuery.builder(query)
|
final UpdateQuery updateQuery = UpdateQuery.builder(query)
|
||||||
.withScriptType(ScriptType.INLINE)
|
|
||||||
.withScript("ctx._source['message'] = params['newMessage']").withLang("painless")
|
.withScript("ctx._source['message'] = params['newMessage']").withLang("painless")
|
||||||
.withParams(Collections.singletonMap("newMessage", messageAfterUpdate)).withAbortOnVersionConflict(true)
|
.withParams(Collections.singletonMap("newMessage", messageAfterUpdate)).withAbortOnVersionConflict(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
operations.updateByQuery(updateQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
var byQueryResponse = operations.updateByQuery(updateQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||||
|
|
||||||
|
assertThat(byQueryResponse.getUpdated()).isEqualTo(1);
|
||||||
|
|
||||||
SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class,
|
SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class,
|
||||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||||
@ -3901,10 +3900,10 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private Double scriptedRate;
|
@ScriptedField private Double scriptedRate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Version private Long version;
|
@Version private Long version;
|
||||||
@ -3919,7 +3918,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
@Nullable private String type;
|
@Nullable private String type;
|
||||||
@Nullable private String message;
|
@Nullable private String message;
|
||||||
@Nullable private Long version;
|
@Nullable private Long version;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
|
|
||||||
public Builder id(String id) {
|
public Builder id(String id) {
|
||||||
@ -4116,10 +4115,10 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
@Nullable private String type;
|
@Nullable private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = FieldType.Text, fielddata = true) private String message;
|
@Field(type = FieldType.Text, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private Long scriptedRate;
|
@ScriptedField private Long scriptedRate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Version private Long version;
|
@Version private Long version;
|
||||||
@ -4807,8 +4806,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
@Document(indexName = "#{@indexNameProvider.indexName()}-immutable-scripted")
|
@Document(indexName = "#{@indexNameProvider.indexName()}-immutable-scripted")
|
||||||
public static final class ImmutableWithScriptedEntity {
|
public static final class ImmutableWithScriptedEntity {
|
||||||
@Id private final String id;
|
@Id private final String id;
|
||||||
@Field(type = Integer)
|
@Field(type = Integer) private final int rate;
|
||||||
@Nullable private final int rate;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ScriptedField private final Double scriptedRate;
|
@ScriptedField private final Double scriptedRate;
|
||||||
|
|
||||||
@ -4864,9 +4862,11 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
|
|
||||||
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
||||||
static class ReadonlyIdEntity {
|
static class ReadonlyIdEntity {
|
||||||
@Field(type = FieldType.Keyword) private String part1;
|
@Field(type = FieldType.Keyword)
|
||||||
|
@Nullable private String part1;
|
||||||
|
|
||||||
@Field(type = FieldType.Keyword) private String part2;
|
@Field(type = FieldType.Keyword)
|
||||||
|
@Nullable private String part2;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@WriteOnlyProperty
|
@WriteOnlyProperty
|
||||||
@ -4875,7 +4875,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
return part1 + '-' + part2;
|
return part1 + '-' + part2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPart1() {
|
public @Nullable String getPart1() {
|
||||||
return part1;
|
return part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4883,7 +4883,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
this.part1 = part1;
|
this.part1 = part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPart2() {
|
public @Nullable String getPart2() {
|
||||||
return part2;
|
return part2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5007,9 +5007,11 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
private static class RootEntity {
|
private static class RootEntity {
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
|
|
||||||
@Field(type = FieldType.Object) private Child child;
|
@Field(type = FieldType.Object)
|
||||||
|
@Nullable private Child child;
|
||||||
|
|
||||||
@Field(type = FieldType.Object) private Parent parent;
|
@Field(type = FieldType.Object)
|
||||||
|
@Nullable private Parent parent;
|
||||||
|
|
||||||
@JoinTypeRelations(relations = {
|
@JoinTypeRelations(relations = {
|
||||||
@JoinTypeRelation(parent = "parent", children = { "child" })
|
@JoinTypeRelation(parent = "parent", children = { "child" })
|
||||||
@ -5031,7 +5033,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Child getChild() {
|
public @Nullable Child getChild() {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5039,7 +5041,7 @@ public abstract class ElasticsearchIntegrationTests {
|
|||||||
this.child = child;
|
this.child = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Parent getParent() {
|
public @Nullable Parent getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ class EntityOperationsUnitTests {
|
|||||||
@IndexedIndexName
|
@IndexedIndexName
|
||||||
@Nullable private String indexName;
|
@Nullable private String indexName;
|
||||||
|
|
||||||
public String getId() {
|
public @Nullable String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ public abstract class LogEntityIntegrationTests {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
@Nullable private String action;
|
@Nullable private String action;
|
||||||
@Nullable private long sequenceCode;
|
private long sequenceCode;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Ip) private String ip;
|
@Field(type = Ip) private String ip;
|
||||||
@Field(type = Date, format = DateFormat.date_time) private java.util.@Nullable Date date;
|
@Field(type = Date, format = DateFormat.date_time) private java.util.@Nullable Date date;
|
||||||
@ -149,7 +149,7 @@ public abstract class LogEntityIntegrationTests {
|
|||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public @Nullable String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ public abstract class LogEntityIntegrationTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAction() {
|
public @Nullable String getAction() {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ public abstract class LogEntityIntegrationTests {
|
|||||||
this.sequenceCode = sequenceCode;
|
this.sequenceCode = sequenceCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIp() {
|
public @Nullable String getIp() {
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ public abstract class LogEntityIntegrationTests {
|
|||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
public java.util.Date getDate() {
|
public java.util.@Nullable Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1298,7 +1298,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
|
|||||||
@Id private String id;
|
@Id private String id;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Version private Long version;
|
@Version private Long version;
|
||||||
|
|
||||||
@ -1568,9 +1568,9 @@ public abstract class ReactiveElasticsearchIntegrationTests {
|
|||||||
|
|
||||||
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
||||||
static class ReadonlyIdEntity {
|
static class ReadonlyIdEntity {
|
||||||
@Field(type = FieldType.Keyword) private String part1;
|
@Field(type = FieldType.Keyword) private @Nullable String part1;
|
||||||
|
|
||||||
@Field(type = FieldType.Keyword) private String part2;
|
@Field(type = FieldType.Keyword) private @Nullable String part2;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@WriteOnlyProperty
|
@WriteOnlyProperty
|
||||||
@ -1579,7 +1579,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
|
|||||||
return part1 + '-' + part2;
|
return part1 + '-' + part2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPart1() {
|
public @Nullable String getPart1() {
|
||||||
return part1;
|
return part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1587,7 +1587,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
|
|||||||
this.part1 = part1;
|
this.part1 = part1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPart2() {
|
public @Nullable String getPart2() {
|
||||||
return part2;
|
return part2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ public abstract class AggregationIntegrationTests {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Integer, store = true) private List<Integer> publishedYears = new ArrayList<>();
|
@Field(type = Integer, store = true) private List<Integer> publishedYears = new ArrayList<>();
|
||||||
|
|
||||||
@Nullable private int score;
|
private int score;
|
||||||
|
|
||||||
public ArticleEntity(@Nullable String id) {
|
public ArticleEntity(@Nullable String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -25,7 +25,15 @@ import java.time.LocalTime;
|
|||||||
import java.time.OffsetTime;
|
import java.time.OffsetTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.intellij.lang.annotations.Language;
|
import org.intellij.lang.annotations.Language;
|
||||||
@ -128,7 +136,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
public void init() {
|
public void init() {
|
||||||
|
|
||||||
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
|
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
|
||||||
mappingContext.setInitialEntitySet(Collections.singleton(Rifle.class));
|
mappingContext.setInitialEntitySet(singleton(Rifle.class));
|
||||||
mappingContext.afterPropertiesSet();
|
mappingContext.afterPropertiesSet();
|
||||||
|
|
||||||
mappingElasticsearchConverter = new MappingElasticsearchConverter(mappingContext, new GenericConversionService());
|
mappingElasticsearchConverter = new MappingElasticsearchConverter(mappingContext, new GenericConversionService());
|
||||||
@ -389,7 +397,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void readListOfConcreteTypesCorrectly() {
|
public void readListOfConcreteTypesCorrectly() {
|
||||||
|
|
||||||
sarahAsMap.put("coWorkers", Collections.singletonList(kyleAsMap));
|
sarahAsMap.put("coWorkers", singletonList(kyleAsMap));
|
||||||
|
|
||||||
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
||||||
|
|
||||||
@ -414,7 +422,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
|
|
||||||
Map<String, Object> target = writeToMap(sarahConnor);
|
Map<String, Object> target = writeToMap(sarahConnor);
|
||||||
assertThat(target.get("shippingAddresses")).isInstanceOf(Map.class);
|
assertThat(target.get("shippingAddresses")).isInstanceOf(Map.class);
|
||||||
assertThat(target.get("shippingAddresses")).isEqualTo(Collections.singletonMap("home", gratiotAveAsMap));
|
assertThat(target.get("shippingAddresses")).isEqualTo(singletonMap("home", gratiotAveAsMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
@ -433,7 +441,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void readConcreteMapCorrectly() {
|
public void readConcreteMapCorrectly() {
|
||||||
|
|
||||||
sarahAsMap.put("shippingAddresses", Collections.singletonMap("home", gratiotAveAsMap));
|
sarahAsMap.put("shippingAddresses", singletonMap("home", gratiotAveAsMap));
|
||||||
|
|
||||||
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
||||||
|
|
||||||
@ -443,7 +451,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void readInterfaceMapCorrectly() {
|
public void readInterfaceMapCorrectly() {
|
||||||
|
|
||||||
sarahAsMap.put("inventoryMap", Collections.singletonMap("glock19", gunAsMap));
|
sarahAsMap.put("inventoryMap", singletonMap("glock19", gunAsMap));
|
||||||
|
|
||||||
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
Person target = mappingElasticsearchConverter.read(Person.class, sarahAsMap);
|
||||||
|
|
||||||
@ -490,7 +498,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
public void readGenericListList() {
|
public void readGenericListList() {
|
||||||
|
|
||||||
Document source = Document.create();
|
Document source = Document.create();
|
||||||
source.put("objectList", Collections.singletonList(Arrays.asList(t800AsMap, gunAsMap)));
|
source.put("objectList", singletonList(Arrays.asList(t800AsMap, gunAsMap)));
|
||||||
|
|
||||||
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
||||||
|
|
||||||
@ -515,7 +523,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
public void readGenericMap() {
|
public void readGenericMap() {
|
||||||
|
|
||||||
Document source = Document.create();
|
Document source = Document.create();
|
||||||
source.put("objectMap", Collections.singletonMap("glock19", gunAsMap));
|
source.put("objectMap", singletonMap("glock19", gunAsMap));
|
||||||
|
|
||||||
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
||||||
|
|
||||||
@ -527,23 +535,23 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
|
|
||||||
Skynet skynet = new Skynet();
|
Skynet skynet = new Skynet();
|
||||||
skynet.objectMap = new LinkedHashMap<>();
|
skynet.objectMap = new LinkedHashMap<>();
|
||||||
skynet.objectMap.put("inventory", Collections.singletonMap("glock19", gun));
|
skynet.objectMap.put("inventory", singletonMap("glock19", gun));
|
||||||
|
|
||||||
Map<String, Object> target = writeToMap(skynet);
|
Map<String, Object> target = writeToMap(skynet);
|
||||||
|
|
||||||
assertThat((Map<String, Object>) target.get("objectMap")).containsEntry("inventory",
|
assertThat((Map<String, Object>) target.get("objectMap")).containsEntry("inventory",
|
||||||
Collections.singletonMap("glock19", gunAsMap));
|
singletonMap("glock19", gunAsMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void readGenericMapMap() {
|
public void readGenericMapMap() {
|
||||||
|
|
||||||
Document source = Document.create();
|
Document source = Document.create();
|
||||||
source.put("objectMap", Collections.singletonMap("inventory", Collections.singletonMap("glock19", gunAsMap)));
|
source.put("objectMap", singletonMap("inventory", singletonMap("glock19", gunAsMap)));
|
||||||
|
|
||||||
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
Skynet target = mappingElasticsearchConverter.read(Skynet.class, source);
|
||||||
|
|
||||||
assertThat(target.getObjectMap()).containsEntry("inventory", Collections.singletonMap("glock19", gun));
|
assertThat(target.getObjectMap()).containsEntry("inventory", singletonMap("glock19", gun));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
@ -575,7 +583,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void writesNestedAliased() {
|
public void writesNestedAliased() {
|
||||||
|
|
||||||
t800.inventoryList = Collections.singletonList(rifle);
|
t800.inventoryList = singletonList(rifle);
|
||||||
Map<String, Object> target = writeToMap(t800);
|
Map<String, Object> target = writeToMap(t800);
|
||||||
|
|
||||||
assertThat((List<Document>) target.get("inventoryList")).contains(rifleAsMap);
|
assertThat((List<Document>) target.get("inventoryList")).contains(rifleAsMap);
|
||||||
@ -589,7 +597,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Test // DATAES-530
|
@Test // DATAES-530
|
||||||
public void readsNestedAliased() {
|
public void readsNestedAliased() {
|
||||||
|
|
||||||
t800AsMap.put("inventoryList", Collections.singletonList(rifleAsMap));
|
t800AsMap.put("inventoryList", singletonList(rifleAsMap));
|
||||||
|
|
||||||
assertThat(mappingElasticsearchConverter.read(Person.class, t800AsMap).getInventoryList()).containsExactly(rifle);
|
assertThat(mappingElasticsearchConverter.read(Person.class, t800AsMap).getInventoryList()).containsExactly(rifle);
|
||||||
}
|
}
|
||||||
@ -911,12 +919,12 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
void shouldWriteMapContainingCollectionContainingMap() throws JSONException {
|
void shouldWriteMapContainingCollectionContainingMap() throws JSONException {
|
||||||
|
|
||||||
class EntityWithMapCollectionMap {
|
class EntityWithMapCollectionMap {
|
||||||
Map<String, Object> map;
|
Map<String, Object> map = Map.of();
|
||||||
}
|
}
|
||||||
class InnerEntity {
|
class InnerEntity {
|
||||||
String prop1;
|
@Nullable String prop1;
|
||||||
|
|
||||||
String prop2;
|
@Nullable String prop2;
|
||||||
|
|
||||||
public InnerEntity() {}
|
public InnerEntity() {}
|
||||||
|
|
||||||
@ -928,8 +936,8 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var entity = new EntityWithMapCollectionMap();
|
var entity = new EntityWithMapCollectionMap();
|
||||||
entity.map = Collections.singletonMap("collection",
|
entity.map = singletonMap("collection",
|
||||||
Collections.singletonList(Collections.singletonMap("destination", new InnerEntity("prop1", "prop2"))));
|
singletonList(singletonMap("destination", new InnerEntity("prop1", "prop2"))));
|
||||||
|
|
||||||
var expected = """
|
var expected = """
|
||||||
{
|
{
|
||||||
@ -1069,67 +1077,79 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
class RangeEntity {
|
class RangeEntity {
|
||||||
|
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
@Field(type = FieldType.Integer_Range) private Range<Integer> integerRange;
|
@Field(type = FieldType.Integer_Range)
|
||||||
@Field(type = FieldType.Float_Range) private Range<Float> floatRange;
|
@Nullable private Range<Integer> integerRange;
|
||||||
@Field(type = FieldType.Long_Range) private Range<Long> longRange;
|
@Field(type = FieldType.Float_Range)
|
||||||
@Field(type = FieldType.Double_Range) private Range<Double> doubleRange;
|
@Nullable private Range<Float> floatRange;
|
||||||
@Field(type = FieldType.Date_Range) private Range<Date> dateRange;
|
@Field(type = FieldType.Long_Range)
|
||||||
@Field(type = FieldType.Date_Range, format = DateFormat.year_month_day) private Range<LocalDate> localDateRange;
|
@Nullable private Range<Long> longRange;
|
||||||
|
@Field(type = FieldType.Double_Range)
|
||||||
|
@Nullable private Range<Double> doubleRange;
|
||||||
|
@Field(type = FieldType.Date_Range)
|
||||||
|
@Nullable private Range<Date> dateRange;
|
||||||
|
@Field(type = FieldType.Date_Range, format = DateFormat.year_month_day)
|
||||||
|
@Nullable private Range<LocalDate> localDateRange;
|
||||||
@Field(type = FieldType.Date_Range,
|
@Field(type = FieldType.Date_Range,
|
||||||
format = DateFormat.hour_minute_second_millis) private Range<LocalTime> localTimeRange;
|
format = DateFormat.hour_minute_second_millis)
|
||||||
|
@Nullable private Range<LocalTime> localTimeRange;
|
||||||
@Field(type = FieldType.Date_Range,
|
@Field(type = FieldType.Date_Range,
|
||||||
format = DateFormat.date_hour_minute_second_millis) private Range<LocalDateTime> localDateTimeRange;
|
format = DateFormat.date_hour_minute_second_millis)
|
||||||
@Field(type = FieldType.Date_Range, format = DateFormat.time) private Range<OffsetTime> offsetTimeRange;
|
@Nullable private Range<LocalDateTime> localDateTimeRange;
|
||||||
@Field(type = FieldType.Date_Range) private Range<ZonedDateTime> zonedDateTimeRange;
|
@Field(type = FieldType.Date_Range, format = DateFormat.time)
|
||||||
@Field(type = FieldType.Date_Range, storeNullValue = true) private Range<ZonedDateTime> nullRange;
|
@Nullable private Range<OffsetTime> offsetTimeRange;
|
||||||
|
@Field(type = FieldType.Date_Range)
|
||||||
|
@Nullable private Range<ZonedDateTime> zonedDateTimeRange;
|
||||||
|
@Field(type = FieldType.Date_Range, storeNullValue = true)
|
||||||
|
@Nullable private Range<ZonedDateTime> nullRange;
|
||||||
|
|
||||||
@Field(type = FieldType.Integer_Range) private List<Range<Integer>> integerRangeList;
|
@Field(type = FieldType.Integer_Range)
|
||||||
|
@Nullable private List<Range<Integer>> integerRangeList;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<Integer> getIntegerRange() {
|
public @Nullable Range<Integer> getIntegerRange() {
|
||||||
return integerRange;
|
return integerRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<Float> getFloatRange() {
|
public @Nullable Range<Float> getFloatRange() {
|
||||||
return floatRange;
|
return floatRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<Long> getLongRange() {
|
public @Nullable Range<Long> getLongRange() {
|
||||||
return longRange;
|
return longRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<Double> getDoubleRange() {
|
public @Nullable Range<Double> getDoubleRange() {
|
||||||
return doubleRange;
|
return doubleRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<Date> getDateRange() {
|
public @Nullable Range<Date> getDateRange() {
|
||||||
return dateRange;
|
return dateRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<LocalDate> getLocalDateRange() {
|
public @Nullable Range<LocalDate> getLocalDateRange() {
|
||||||
return localDateRange;
|
return localDateRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<LocalTime> getLocalTimeRange() {
|
public @Nullable Range<LocalTime> getLocalTimeRange() {
|
||||||
return localTimeRange;
|
return localTimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<LocalDateTime> getLocalDateTimeRange() {
|
public @Nullable Range<LocalDateTime> getLocalDateTimeRange() {
|
||||||
return localDateTimeRange;
|
return localDateTimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<OffsetTime> getOffsetTimeRange() {
|
public @Nullable Range<OffsetTime> getOffsetTimeRange() {
|
||||||
return offsetTimeRange;
|
return offsetTimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<ZonedDateTime> getZonedDateTimeRange() {
|
public @Nullable Range<ZonedDateTime> getZonedDateTimeRange() {
|
||||||
return zonedDateTimeRange;
|
return zonedDateTimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Range<ZonedDateTime> getNullRange() {
|
public @Nullable Range<ZonedDateTime> getNullRange() {
|
||||||
return nullRange;
|
return nullRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1181,7 +1201,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
this.nullRange = nullRange;
|
this.nullRange = nullRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Range<Integer>> getIntegerRangeList() {
|
public @Nullable List<Range<Integer>> getIntegerRangeList() {
|
||||||
return integerRangeList;
|
return integerRangeList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2639,8 +2659,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Nullable private GeoPoint pointB;
|
@Nullable private GeoPoint pointB;
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoPointField private String pointC;
|
@GeoPointField private String pointC;
|
||||||
@Nullable
|
@GeoPointField private double @Nullable [] pointD;
|
||||||
@GeoPointField private double[] pointD;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -2705,12 +2724,11 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
this.pointC = pointC;
|
this.pointC = pointC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public double @Nullable [] getPointD() {
|
||||||
public double[] getPointD() {
|
|
||||||
return pointD;
|
return pointD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPointD(@Nullable double[] pointD) {
|
public void setPointD(double @Nullable [] pointD) {
|
||||||
this.pointD = pointD;
|
this.pointD = pointD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3259,7 +3277,8 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
@Id
|
@Id
|
||||||
@Nullable private String id;
|
@Nullable private String id;
|
||||||
|
|
||||||
@Field(type = FieldType.Nested, name = "level-one") private List<Level1> level1Entries;
|
@Field(type = FieldType.Nested, name = "level-one")
|
||||||
|
@Nullable private List<Level1> level1Entries;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -3270,7 +3289,7 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Level1> getLevel1Entries() {
|
public @Nullable List<Level1> getLevel1Entries() {
|
||||||
return level1Entries;
|
return level1Entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3279,9 +3298,10 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class Level1 {
|
static class Level1 {
|
||||||
@Field(type = FieldType.Nested, name = "level-two") private List<Level2> level2Entries;
|
@Field(type = FieldType.Nested, name = "level-two")
|
||||||
|
@Nullable private List<Level2> level2Entries;
|
||||||
|
|
||||||
public List<Level2> getLevel2Entries() {
|
public @Nullable List<Level2> getLevel2Entries() {
|
||||||
return level2Entries;
|
return level2Entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3291,9 +3311,10 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class Level2 {
|
static class Level2 {
|
||||||
@Field(type = FieldType.Keyword, name = "key-word") private String keyWord;
|
@Field(type = FieldType.Keyword, name = "key-word")
|
||||||
|
@Nullable private String keyWord;
|
||||||
|
|
||||||
public String getKeyWord() {
|
public @Nullable String getKeyWord() {
|
||||||
return keyWord;
|
return keyWord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,12 +424,11 @@ public abstract class GeoIntegrationTests {
|
|||||||
@Nullable private String name;
|
@Nullable private String name;
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoPointField private String locationAsString;
|
@GeoPointField private String locationAsString;
|
||||||
@Nullable
|
@GeoPointField private double @Nullable [] locationAsArray;
|
||||||
@GeoPointField private double[] locationAsArray;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoPointField private String locationAsGeoHash;
|
@GeoPointField private String locationAsGeoHash;
|
||||||
|
|
||||||
public String getId() {
|
public @Nullable String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +436,7 @@ public abstract class GeoIntegrationTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public @Nullable String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +444,7 @@ public abstract class GeoIntegrationTests {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocationAsString() {
|
public @Nullable String getLocationAsString() {
|
||||||
return locationAsString;
|
return locationAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +460,7 @@ public abstract class GeoIntegrationTests {
|
|||||||
this.locationAsArray = locationAsArray;
|
this.locationAsArray = locationAsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocationAsGeoHash() {
|
public @Nullable String getLocationAsGeoHash() {
|
||||||
return locationAsGeoHash;
|
return locationAsGeoHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,8 +724,7 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
|
|||||||
static class DenseVectorEntity {
|
static class DenseVectorEntity {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
@Nullable
|
@Field(type = Dense_Vector, dims = 3) private float @Nullable [] dense_vector;
|
||||||
@Field(type = Dense_Vector, dims = 3) private float[] dense_vector;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -736,12 +735,11 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public float @Nullable [] getDense_vector() {
|
||||||
public float[] getDense_vector() {
|
|
||||||
return dense_vector;
|
return dense_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDense_vector(@Nullable float[] dense_vector) {
|
public void setDense_vector(float @Nullable [] dense_vector) {
|
||||||
this.dense_vector = dense_vector;
|
this.dense_vector = dense_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -915,7 +913,8 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
|
|
||||||
@Field(type = FieldType.Dense_Vector, dims = 42, knnSimilarity = KnnSimilarity.COSINE) private double[] denseVector;
|
@Field(type = FieldType.Dense_Vector, dims = 42,
|
||||||
|
knnSimilarity = KnnSimilarity.COSINE) private double @Nullable [] denseVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mapping(aliases = {
|
@Mapping(aliases = {
|
||||||
|
@ -1398,7 +1398,7 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
|
|
||||||
@Field("mapping-property")
|
@Field("mapping-property")
|
||||||
@Mapping(mappingPath = "/mappings/test-field-analyzed-mappings.json") //
|
@Mapping(mappingPath = "/mappings/test-field-analyzed-mappings.json") //
|
||||||
@Nullable private byte[] mappingProperty;
|
private byte @Nullable [] mappingProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ -1820,8 +1820,7 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
@Nullable private GeoPoint pointB;
|
@Nullable private GeoPoint pointB;
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoPointField private String pointC;
|
@GeoPointField private String pointC;
|
||||||
@Nullable
|
@GeoPointField private double @Nullable [] pointD;
|
||||||
@GeoPointField private double[] pointD;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoShapeField private String shape1;
|
@GeoShapeField private String shape1;
|
||||||
@ -1892,12 +1891,11 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
this.pointC = pointC;
|
this.pointC = pointC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public double @Nullable [] getPointD() {
|
||||||
public double[] getPointD() {
|
|
||||||
return pointD;
|
return pointD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPointD(@Nullable double[] pointD) {
|
public void setPointD(double @Nullable [] pointD) {
|
||||||
this.pointD = pointD;
|
this.pointD = pointD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2171,8 +2169,7 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
static class DenseVectorEntity {
|
static class DenseVectorEntity {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
@Nullable
|
@Field(type = FieldType.Dense_Vector, dims = 16) private float @Nullable [] my_vector;
|
||||||
@Field(type = FieldType.Dense_Vector, dims = 16) private float[] my_vector;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -2183,12 +2180,11 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public float @Nullable [] getMy_vector() {
|
||||||
public float[] getMy_vector() {
|
|
||||||
return my_vector;
|
return my_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMy_vector(@Nullable float[] my_vector) {
|
public void setMy_vector(float @Nullable [] my_vector) {
|
||||||
this.my_vector = my_vector;
|
this.my_vector = my_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2198,10 +2194,9 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Field(type = FieldType.Dense_Vector, dims = 16, elementType = FieldElementType.FLOAT,
|
@Field(type = FieldType.Dense_Vector, dims = 16, elementType = FieldElementType.FLOAT,
|
||||||
knnIndexOptions = @KnnIndexOptions(type = KnnAlgorithmType.HNSW, m = 16, efConstruction = 100),
|
knnIndexOptions = @KnnIndexOptions(type = KnnAlgorithmType.HNSW, m = 16, efConstruction = 100),
|
||||||
knnSimilarity = KnnSimilarity.DOT_PRODUCT) private float[] my_vector;
|
knnSimilarity = KnnSimilarity.DOT_PRODUCT) private float @Nullable [] my_vector;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -2212,12 +2207,11 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public float @Nullable [] getMy_vector() {
|
||||||
public float[] getMy_vector() {
|
|
||||||
return my_vector;
|
return my_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMy_vector(@Nullable float[] my_vector) {
|
public void setMy_vector(float @Nullable [] my_vector) {
|
||||||
this.my_vector = my_vector;
|
this.my_vector = my_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2277,7 +2271,7 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
|||||||
static class DenseVectorMisMatchConfidenceIntervalClass {
|
static class DenseVectorMisMatchConfidenceIntervalClass {
|
||||||
@Field(type = Dense_Vector, dims = 16, elementType = FieldElementType.FLOAT,
|
@Field(type = Dense_Vector, dims = 16, elementType = FieldElementType.FLOAT,
|
||||||
knnIndexOptions = @KnnIndexOptions(type = KnnAlgorithmType.HNSW, m = 16, confidenceInterval = 0.95F),
|
knnIndexOptions = @KnnIndexOptions(type = KnnAlgorithmType.HNSW, m = 16, confidenceInterval = 0.95F),
|
||||||
knnSimilarity = KnnSimilarity.DOT_PRODUCT) private float[] dense_vector;
|
knnSimilarity = KnnSimilarity.DOT_PRODUCT) private float @Nullable [] dense_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DisabledMappingProperty {
|
static class DisabledMappingProperty {
|
||||||
|
@ -110,10 +110,10 @@ public class MappingParametersTest extends MappingContextBaseTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class DenseVectorInvalidDimsClass {
|
static class DenseVectorInvalidDimsClass {
|
||||||
@Field(type = Dense_Vector, dims = 4097) private float[] dense_vector;
|
@Field(type = Dense_Vector, dims = 4097) private float @Nullable [] dense_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DenseVectorMissingDimsClass {
|
static class DenseVectorMissingDimsClass {
|
||||||
@Field(type = Dense_Vector) private float[] dense_vector;
|
@Field(type = Dense_Vector) private float @Nullable [] dense_vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,8 @@ public abstract class ReactiveIndexOperationsIntegrationTests {
|
|||||||
})
|
})
|
||||||
private static class EntityWithAliases {
|
private static class EntityWithAliases {
|
||||||
@Nullable private @Id String id;
|
@Nullable private @Id String id;
|
||||||
@Field(type = Text) private String type;
|
@Field(type = Text)
|
||||||
|
@Nullable private String type;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -455,7 +456,7 @@ public abstract class ReactiveIndexOperationsIntegrationTests {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public @Nullable String getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
|
|
||||||
static class FieldNamingStrategyEntity {
|
static class FieldNamingStrategyEntity {
|
||||||
@Nullable private String withoutCustomFieldName;
|
@Nullable private String withoutCustomFieldName;
|
||||||
@Field(name = "CUStomFIEldnAME") private String withCustomFieldName;
|
@Field(name = "CUStomFIEldnAME") @Nullable private String withCustomFieldName;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getWithoutCustomFieldName() {
|
public String getWithoutCustomFieldName() {
|
||||||
@ -336,7 +336,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
this.withoutCustomFieldName = withoutCustomFieldName;
|
this.withoutCustomFieldName = withoutCustomFieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWithCustomFieldName() {
|
public @Nullable String getWithCustomFieldName() {
|
||||||
return withCustomFieldName;
|
return withCustomFieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.core.query;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
import static org.springframework.data.elasticsearch.core.query.BaseQuery.*;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
class BaseQueryTests {
|
||||||
|
|
||||||
|
private static final String MATCH_ALL_QUERY = "{\"match_all\":{}}";
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with no Pageable and no maxResults requests 10 docs from 0")
|
||||||
|
void queryWithNoPageableAndNoMaxResultsRequests10DocsFrom0() {
|
||||||
|
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with a Pageable and no MaxResults request with values from Pageable")
|
||||||
|
void queryWithAPageableAndNoMaxResultsRequestWithValuesFromPageable() {
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.withPageable(Pageable.ofSize(42))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with no Pageable and maxResults requests maxResults")
|
||||||
|
void queryWithNoPageableAndMaxResultsRequestsMaxResults() {
|
||||||
|
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.withMaxResults(12_345)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(12_345);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with Pageable and maxResults requests with values from Pageable if Pageable is less than maxResults")
|
||||||
|
void queryWithPageableAndMaxResultsRequestsWithValuesFromPageableIfPageableIsLessThanMaxResults() {
|
||||||
|
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.withPageable(Pageable.ofSize(42))
|
||||||
|
.withMaxResults(123)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with Pageable and maxResults requests with values from maxResults if Pageable is more than maxResults")
|
||||||
|
void queryWithPageableAndMaxResultsRequestsWithValuesFromMaxResultsIfPageableIsMoreThanMaxResults() {
|
||||||
|
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.withPageable(Pageable.ofSize(420))
|
||||||
|
.withMaxResults(123)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #3127
|
||||||
|
@DisplayName("query with explicit unpaged request and no maxResults requests max request window size")
|
||||||
|
void queryWithExplicitUnpagedRequestAndNoMaxResultsRequestsMaxRequestWindowSize() {
|
||||||
|
|
||||||
|
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||||
|
.withPageable(Pageable.unpaged())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var requestSize = query.getRequestSize();
|
||||||
|
|
||||||
|
assertThat(requestSize).isEqualTo(INDEX_MAX_RESULT_WINDOW);
|
||||||
|
}
|
||||||
|
}
|
@ -715,7 +715,7 @@ public abstract class CriteriaQueryIntegrationTests {
|
|||||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Version private Long version;
|
@Version private Long version;
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilde
|
|||||||
import org.springframework.data.elasticsearch.core.query.Query;
|
import org.springframework.data.elasticsearch.core.query.Query;
|
||||||
import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptType;
|
|
||||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||||
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
|
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
|
||||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||||
@ -165,7 +164,6 @@ public abstract class ReactiveScriptedAndRuntimeFieldsIntegrationTests {
|
|||||||
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
||||||
fieldName,
|
fieldName,
|
||||||
ScriptData.of(b -> b
|
ScriptData.of(b -> b
|
||||||
.withType(ScriptType.INLINE)
|
|
||||||
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
||||||
.withParams(Map.of("factor", factor))));
|
.withParams(Map.of("factor", factor))));
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilde
|
|||||||
import org.springframework.data.elasticsearch.core.query.Query;
|
import org.springframework.data.elasticsearch.core.query.Query;
|
||||||
import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
||||||
import org.springframework.data.elasticsearch.core.query.ScriptType;
|
|
||||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||||
@ -314,7 +313,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
|||||||
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
||||||
fieldName,
|
fieldName,
|
||||||
ScriptData.of(b -> b
|
ScriptData.of(b -> b
|
||||||
.withType(ScriptType.INLINE)
|
|
||||||
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
||||||
.withParams(Map.of("factor", factor))));
|
.withParams(Map.of("factor", factor))));
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import java.util.Properties;
|
|||||||
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.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||||
@ -41,7 +40,7 @@ import org.testcontainers.utility.DockerImageName;
|
|||||||
*
|
*
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
public class ClusterConnection implements ExtensionContext.Store.CloseableResource {
|
public class ClusterConnection implements AutoCloseable {
|
||||||
|
|
||||||
private static final Log LOGGER = LogFactory.getLog(ClusterConnection.class);
|
private static final Log LOGGER = LogFactory.getLog(ClusterConnection.class);
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour
|
|||||||
LOGGER.warn("DATAES_ELASTICSEARCH_PORT does not contain a number");
|
LOGGER.warn("DATAES_ELASTICSEARCH_PORT does not contain a number");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ClusterConnectionInfo.builder().withIntegrationtestEnvironment(IntegrationtestEnvironment.get())
|
return ClusterConnectionInfo.builder(IntegrationtestEnvironment.get())
|
||||||
.withHostAndPort(host, port).build();
|
.withHostAndPort(host, port).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,11 +137,10 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour
|
|||||||
.withEnv(testcontainersProperties).withStartupTimeout(Duration.ofMinutes(2)).withReuse(true);
|
.withEnv(testcontainersProperties).withStartupTimeout(Duration.ofMinutes(2)).withReuse(true);
|
||||||
elasticsearchContainer.start();
|
elasticsearchContainer.start();
|
||||||
|
|
||||||
return ClusterConnectionInfo.builder() //
|
return ClusterConnectionInfo.builder(integrationtestEnvironment)
|
||||||
.withIntegrationtestEnvironment(integrationtestEnvironment)
|
|
||||||
.withHostAndPort(elasticsearchContainer.getHost(),
|
.withHostAndPort(elasticsearchContainer.getHost(),
|
||||||
elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_PORT)) //
|
elasticsearchContainer.getMappedPort(ELASTICSEARCH_DEFAULT_PORT))
|
||||||
.withElasticsearchContainer(elasticsearchContainer) //
|
.withElasticsearchContainer(elasticsearchContainer)
|
||||||
.build();
|
.build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Could not start Elasticsearch container", e);
|
LOGGER.error("Could not start Elasticsearch container", e);
|
||||||
|
@ -24,7 +24,7 @@ import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
|||||||
* The {@link #host}, {@link #httpPort} and {@link #useSsl} values specify the values needed to connect to the cluster
|
* The {@link #host}, {@link #httpPort} and {@link #useSsl} values specify the values needed to connect to the cluster
|
||||||
* with a rest client for both a local started cluster and for one defined by the cluster URL when creating the
|
* with a rest client for both a local started cluster and for one defined by the cluster URL when creating the
|
||||||
* {@link ClusterConnection}.<br/>
|
* {@link ClusterConnection}.<br/>
|
||||||
* The object must be created by using a {@link ClusterConnectionInfo.Builder}.
|
* The object must be created by using a {@link Builder}.
|
||||||
*
|
*
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
*/
|
*/
|
||||||
@ -36,8 +36,8 @@ public final class ClusterConnectionInfo {
|
|||||||
private final String clusterName;
|
private final String clusterName;
|
||||||
@Nullable private final ElasticsearchContainer elasticsearchContainer;
|
@Nullable private final ElasticsearchContainer elasticsearchContainer;
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder(IntegrationtestEnvironment integrationtestEnvironment) {
|
||||||
return new Builder();
|
return new Builder(integrationtestEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClusterConnectionInfo(IntegrationtestEnvironment integrationtestEnvironment, String host, int httpPort,
|
private ClusterConnectionInfo(IntegrationtestEnvironment integrationtestEnvironment, String host, int httpPort,
|
||||||
@ -84,13 +84,12 @@ public final class ClusterConnectionInfo {
|
|||||||
public static class Builder {
|
public static class Builder {
|
||||||
private IntegrationtestEnvironment integrationtestEnvironment;
|
private IntegrationtestEnvironment integrationtestEnvironment;
|
||||||
private boolean useSsl = false;
|
private boolean useSsl = false;
|
||||||
private String host;
|
private String host = "";
|
||||||
private int httpPort;
|
private int httpPort;
|
||||||
@Nullable private ElasticsearchContainer elasticsearchContainer;
|
@Nullable private ElasticsearchContainer elasticsearchContainer;
|
||||||
|
|
||||||
public Builder withIntegrationtestEnvironment(IntegrationtestEnvironment integrationtestEnvironment) {
|
public Builder(IntegrationtestEnvironment integrationtestEnvironment) {
|
||||||
this.integrationtestEnvironment = integrationtestEnvironment;
|
this.integrationtestEnvironment = integrationtestEnvironment;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withHostAndPort(String host, int httpPort) {
|
public Builder withHostAndPort(String host, int httpPort) {
|
||||||
|
@ -176,7 +176,7 @@ public class CdiRepositoryTests {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = FieldType.Float) private Float price;
|
@Field(type = FieldType.Float) private Float price;
|
||||||
@Nullable private Integer popularity;
|
@Nullable private Integer popularity;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private String location;
|
@Nullable private String location;
|
||||||
@Nullable private Date lastModified;
|
@Nullable private Date lastModified;
|
||||||
|
|
||||||
@ -293,11 +293,12 @@ public class CdiRepositoryTests {
|
|||||||
|
|
||||||
@Id private String id;
|
@Id private String id;
|
||||||
|
|
||||||
private String name;
|
@Nullable private String name;
|
||||||
|
|
||||||
@Field(type = FieldType.Nested) private List<Car> car;
|
@Field(type = FieldType.Nested)
|
||||||
|
@Nullable private List<Car> car;
|
||||||
|
|
||||||
@Field(type = FieldType.Nested, includeInParent = true) private List<Book> books;
|
@Field(type = FieldType.Nested, includeInParent = true) private @Nullable List<Book> books;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2443,8 +2443,8 @@ public abstract class CustomMethodRepositoryIntegrationTests {
|
|||||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Field(type = Keyword) private String keyword;
|
@Field(type = Keyword) private String keyword;
|
||||||
@Nullable private int rate;
|
private int rate;
|
||||||
@Nullable private boolean available;
|
private boolean available;
|
||||||
@Nullable private GeoPoint location;
|
@Nullable private GeoPoint location;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Version private Long version;
|
@Version private Long version;
|
||||||
|
@ -116,8 +116,7 @@ public abstract class GeoRepositoryIntegrationTests {
|
|||||||
@Nullable private GeoPoint pointB;
|
@Nullable private GeoPoint pointB;
|
||||||
@Nullable
|
@Nullable
|
||||||
@GeoPointField private String pointC;
|
@GeoPointField private String pointC;
|
||||||
@Nullable
|
@GeoPointField private double @Nullable [] pointD;
|
||||||
@GeoPointField private double[] pointD;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -182,12 +181,11 @@ public abstract class GeoRepositoryIntegrationTests {
|
|||||||
this.pointC = pointC;
|
this.pointC = pointC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public double @Nullable [] getPointD() {
|
||||||
public double[] getPointD() {
|
|
||||||
return pointD;
|
return pointD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPointD(@Nullable double[] pointD) {
|
public void setPointD(double @Nullable [] pointD) {
|
||||||
this.pointD = pointD;
|
this.pointD = pointD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,12 +165,11 @@ public abstract class KnnSearchIntegrationTests {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public float @Nullable [] getVector() {
|
||||||
public float[] getVector() {
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVector(@Nullable float[] vector) {
|
public void setVector(float @Nullable [] vector) {
|
||||||
this.vector = vector;
|
this.vector = vector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user