1
0
mirror of synced 2026-07-06 18:20:01 +00:00

Compare commits

..

87 Commits

Author SHA1 Message Date
Spring Builds bad6afdecc Release version 5.0 M6 (2022.0.0).
See #2222
2022-09-19 14:15:27 +00:00
Spring Builds a6a5e3f7f5 Prepare 5.0 M6 (2022.0.0).
See #2222
2022-09-19 14:13:03 +00:00
Peter-Josef Meisch b3a7a44db1 Adapt xml-namespace setup to the new Elasticsearch client.
Original Pull Request #2295
Closes #21294
2022-09-10 17:05:32 +02:00
Peter-Josef Meisch ccf136c8e2 CDI implementation uses the new Elasticsearch client.
Original Pull Request #2293
Closes #2292
2022-09-10 11:09:43 +02:00
Peter-Josef Meisch 77da22598a Fix NPE in RequestFactory when language in UpdateQuery is not set (4.4.x).
Original Pull Request #2288
Closes #2287

(cherry picked from commit 988736dd41)
2022-09-03 08:09:19 +02:00
Peter-Josef Meisch 86634ceb38 Fix mapping of property values into a collection.
When reading from Elasticsearch into a property of type Collection<T> (List<T> or Set<T>) the MappingElasticsearchConverter now can read both from the returned JSON:

    an array of T objects - will put the objects in a corresponding collection
    a single T object will put the single object into a corrsponding colletcion

This is implemented and tested for both: entities where the properties have setters and immutable classes that only provide an all-args constructor.

Original Pull Request #2282
Closes #2280
2022-08-29 20:14:11 +02:00
Peter-Josef Meisch 8377f64a8a Fix update call in reactive client (Elasticsearch 7 client)
Original Pull Request #2281 
Closes #2276
2022-08-26 08:06:37 +02:00
Peter-Josef Meisch b549601d34 Polishing documentation.
Adding missing documentation about the new HttpHeaders class

Original Pull Request #2279
See #2277
2022-08-24 18:37:54 +02:00
Peter-Josef Meisch 3298ba21ce Use custom HttpHeaders.
Original Pull Request #2278
Closes #2277
2022-08-24 13:10:27 +02:00
Peter-Josef Meisch 18be558740 Reactive implementation of the point in time API.
This PR adds the reactive implementation for the point in time API that was missing in #2273.

Original Pull Request #2275
Closes #2274
2022-08-20 18:23:50 +02:00
Peter-Josef Meisch 46cd4cd59e Implement the point in time API.
Original Pull Request #2273
Closes #1684
2022-08-17 13:11:11 +02:00
Peter-Josef Meisch a4ed7300d1 Code cleanup (Java 17).
Original Pull Request #2271
Closes #2270
2022-08-13 15:07:47 +02:00
Peter-Josef Meisch b511756b2b Polishing.
See #2265
2022-08-12 07:27:07 +02:00
Peter-Josef Meisch 68ba4cd39b Polishing.
See #2267
2022-08-11 06:53:07 +02:00
cdalexndr 6b39b39d35 support RuntimeField with no script.
Original Pull Request #2268
Closes #2267
2022-08-11 06:31:17 +02:00
Peter-Josef Meisch 4f4c99ec1f Fix javadoc error.
See #2265
2022-08-10 21:48:05 +02:00
Sascha Woo 6cfb8eeea6 [elc] add support for sorting results by SortOptions to NativeQuery.
Original Pull Request #2264
Closes #2263
2022-08-10 21:34:17 +02:00
Peter-Josef Meisch c4910bf399 Polishing.
See #2265
2022-08-10 21:19:46 +02:00
Sascha Woo c93039410a [elc] add convenient methods to improve access to aggregations.
Original Pull Request #2266
Closes #2265
2022-08-10 21:10:17 +02:00
Peter-Josef Meisch 33c9180ccd Reenable test for the new Elasticsearch Client.
Original Pull Request #2261
Closes #2260
2022-08-07 20:33:07 +02:00
Peter-Josef Meisch df5fd0b97c sourcefilter documentation.
Original Pull Request #2258
Closes #2257
2022-08-07 12:22:46 +02:00
Peter-Josef Meisch 44a79093ce Remove deprecated code.
Original Pull request #2256
Closes #2255
2022-08-06 23:16:38 +02:00
Peter-Josef Meisch cf135f4cdd Implement SourceFilters annotation.
Original Pull Request #2254
Closes #1280
Closes #2062
Closes #2146
Closes #2147
Closes #2151
2022-08-06 21:04:13 +02:00
Peter-Josef Meisch 954d8b0f97 Prefix bean names created by the configuration with "elasticsearch".
Original Pull Request #2253
Closes #2252
2022-08-05 21:14:06 +02:00
Peter-Josef Meisch e71c66949a Add missing properties to BaseQueryBuilder.
Original Pull Request #2251
Closes #2250
2022-08-04 17:50:37 +02:00
Peter-Josef Meisch acf02a1dc9 Don't try to write non-writeable properties.
Original Pull Request #2249
Closes #2230
2022-08-04 08:04:19 +02:00
Peter-Josef Meisch 6ad449c9f4 Polishing.
See #2247
2022-08-01 21:37:06 +02:00
lanicc 94ebc40582 Fix the JDK version required to build from the source code.
Original Pull Request #2248
Closes #2247
2022-08-01 21:25:10 +02:00
Peter-Josef Meisch ca39c1d24c Adapt to changes in Elasticsearch client code. (#2246)
Closes #2211
2022-07-31 18:33:43 +02:00
Peter-Josef Meisch 2049726bcb Dependency updates.
Original Pull Request #2245
Closes #2243
2022-07-30 20:42:12 +02:00
Peter-Josef Meisch 7620222f89 Upgrade to Elasticsearch 8.3.3.
Original Pull Request #2244
Closes #2242
2022-07-30 18:34:22 +02:00
Peter-Josef Meisch 1f1076aa8b Don't rely on collection to object conversion in spring-data-commons.
Original Pull Request #2241
Closes #2152
2022-07-28 07:41:07 +02:00
Peter-Josef Meisch 3e950b8053 SimpleElasticsearchRepository still uses erhlc code.
Original Pull Request #2239
Closes #2238
2022-07-24 22:03:01 +02:00
Peter-Josef Meisch 1c31a31e11 Refine the configuration callbacks in the ClientConfiguration.
Original Pull Request #2237
Closes #2149
2022-07-23 15:32:24 +02:00
Peter-Josef Meisch 373be49f97 Fix exists query for imperative repository implementation.
Original Pull Request #2236
Closes #2162
2022-07-22 21:56:24 +02:00
Peter-Josef Meisch 12b332223f Replace ClassTypeInformation usages with TypeInformation.
Original Pull Request #2235
Closes #2234
2022-07-19 22:37:05 +02:00
Peter-Josef Meisch 47c0e186ec Adapt to spring-data-commons changes.
Original Pull Request #2233
Closes #2232
2022-07-19 22:12:56 +02:00
Peter-Josef Meisch 8b69a8a030 Complete searchMsearchRequest for the new client.
Original Pull Request #2231
Closes #2156
2022-07-19 21:31:14 +02:00
Mark Paluch 8bb0d8cb41 Polishing.
Consistently use credential properties.

See #2222
2022-07-18 09:19:36 +02:00
Peter-Josef Meisch 26a2f54c0f Change visibility of InetSocketAddressParser.
Original Pull Request #2227
Closes #2226
2022-07-17 20:15:03 +02:00
Peter-Josef Meisch 1754f59e70 Upgrade to Elasticsearch 8.3.2.
Original Pull Request #2225
Closes #2214
2022-07-17 08:02:23 +02:00
Patrick Baumgartner 8a166a1a40 Fixing Typo.
Original Pull Request #2219
Closes #2223
2022-07-16 17:55:45 +02:00
Christoph Strobl 5c029602b5 After release cleanups.
See #2167
2022-07-15 15:30:55 +02:00
Christoph Strobl 82a545912e Prepare next development iteration.
See #2167
2022-07-15 15:30:47 +02:00
Christoph Strobl 587dd2edda Release version 5.0 M5 (2022.0.0).
See #2167
2022-07-15 15:18:31 +02:00
Christoph Strobl 451a22614d Prepare 5.0 M5 (2022.0.0).
See #2167
2022-07-15 15:17:57 +02:00
Peter-Josef Meisch 688f1722bd make ReactiveElasticsearchTemplate constructor public.
Original Pull Request #2217
Closes #2216
2022-07-12 17:47:55 +02:00
Peter-Josef Meisch b9dacc9a5e implement getAlias variants.
Original Pull Request #2213
Closes #2209
2022-07-09 23:40:34 +02:00
Peter-Josef Meisch e0acc5a2f9 Upgrade to Elasticsearch 8.3.1.
Original Pull Request #2212
Closes #2210
2022-07-06 22:21:47 +02:00
Peter-Josef Meisch 094e79d601 Make the new client the default.
Original Pull Request #2208
Closes #2159
2022-07-03 21:56:21 +02:00
Peter-Josef Meisch 81e2613669 Documentation fixes.
Original Pull Request #2207
Closes #2206
2022-06-28 22:06:00 +02:00
Peter-Josef Meisch d974788ab8 no more additions currently, the new client has the same functionality as the old.
Original Pull Request #2205
Closes #2155
2022-06-28 21:57:33 +02:00
Shijie.Ma 0109a68c32 Fix document about StringQuery.
Original Pull Request #2202
Closes #2204
2022-06-28 06:44:48 +02:00
diamondT 259c43af19 Fix handling of array-of-strings parameters for @Query-annotated queries.
Original Pull Request  #2182
Closes #2135
2022-06-27 20:35:06 +02:00
Peter-Josef Meisch a2802ce716 Upgrade to Elasticsearch 8.2.3.
Original Pull Request #2200
Closes #2198
2022-06-25 20:54:47 +02:00
Peter-Josef Meisch f901380766 Fix updatebyquery request.
Original Pull Request #2197
Closes #2191
2022-06-25 19:55:22 +02:00
Peter-Josef Meisch 1e4b70ba6d Refactor code using Elasticsearch libs.
Original Pull Request #2196
Closes #2157
2022-06-25 18:29:34 +02:00
Peter-Josef Meisch f917fb7a65 Update version documentation.
Original Pull Request #2195
Closes #2194
2022-06-25 17:40:07 +02:00
Peter-Josef Meisch bfc68699e0 Remove deprecated suggest calls from operations.
Original Pull Request #2193 
Closes #2192
2022-06-24 12:51:58 +02:00
Peter-Josef Meisch 96d0781f24 Parse returned suggest data using the new client.
Original Pull Request #2187
Closes #2154
2022-06-22 18:34:47 +02:00
Gonçalo Montalvão Marques 0a0fc75faa fix typos in migration guides documentation.
Original Pull Request #2181
Closes #2180
2022-06-03 19:35:40 +02:00
Mark Paluch ef880e6d3b Update CI properties.
See #2167
2022-06-03 09:34:20 +02:00
Mark Paluch 3835e8e363 Upgrade to Maven Wrapper 3.8.5.
See #2177
2022-06-03 09:32:51 +02:00
Peter-Josef Meisch 97d0566e94 Fix msearch setup after ES fixed it's issue.
Original Pull Request #2176
Closes #2138
2022-06-01 14:48:46 +02:00
Peter-Josef Meisch ac64a6a733 Use Elasticsearch 8.2.2.
Original Pull Request #2174
Closes #2158
2022-05-31 22:13:51 +02:00
panzhenchao c826adb152 Fix incorrect argument check asserts.
Original Pull Request #2169 
Closes #2170
2022-05-27 16:40:38 +02:00
Christoph Strobl 1eaebdec33 Fix pom.xml formatting.
This commit reverts formatting changes introduced via 7cb46b6ab0.

See #2122
2022-05-13 13:46:12 +02:00
Christoph Strobl d37ca4d533 After release cleanups.
See #2122
2022-05-13 10:53:27 +02:00
Christoph Strobl d676ffe066 Prepare next development iteration.
See #2122
2022-05-13 10:53:24 +02:00
Christoph Strobl 1bff5dd749 Release version 5.0 M4 (2022.0.0).
See #2122
2022-05-13 10:44:00 +02:00
Christoph Strobl 7cb46b6ab0 Prepare 5.0 M4 (2022.0.0).
See #2122
2022-05-13 10:43:21 +02:00
Peter-Josef Meisch a86658c397 Add new Elasticsearch client as an alternative to the existing REST client.
Original Pull Request #2160
Closes #1973

(cherry picked from commit c0b26a51f1)
2022-05-12 07:38:44 +02:00
Peter-Josef Meisch 0950dd6c7a Update to Elasticsearch 7.17.3.
Original Pull Request #2145
Closes #2144
2022-04-24 11:53:19 +02:00
Peter-Josef Meisch e1c926e134 Use Range class from spring-data-commons.
Original Pull Request #2137
Closes #2098
2022-04-15 08:42:26 +02:00
Peter-Josef Meisch 6a201b4e34 Add more implementations using the new client.
Original Pull Request #2136
See #1973

(cherry picked from commit 8cef50347e)
2022-04-13 22:34:44 +02:00
Oliver Drotbohm 48e2e9dea7 Adapt to changes in Spring Framework 6.0 HttpStatus.
Fixes #2134.
2022-04-11 17:00:59 +02:00
Peter-Josef Meisch a60f3059e1 Upgrade to Elasticsearch 7.17.2.
Original Pull Request #2131
Closes #2130
2022-04-02 21:14:20 +02:00
Peter-Josef Meisch 3154c74f94 Add info to readme about Elasticsearch versions.
Original Pull Request #2128
Closes #2127
2022-03-31 07:39:19 +02:00
Jing Wei 1407d4d3d6 Upgrade pitest version and add jvmArg.
Original Pull Request: #2126
Closes #2125
2022-03-27 18:57:54 +02:00
Peter-Josef Meisch acd7990fac Default Refresh policy for ReactiveElasticsearchTemplate.
Original Pull Request #2124
Closes #2110
2022-03-24 21:07:55 +01:00
Mark Paluch b10eb75967 Update Jenkinsfile according to Java 17 build pipeline configuration.
See #2122
2022-03-24 09:29:06 +01:00
Mark Paluch a4952a90f8 After release cleanups.
See #2121
2022-03-22 14:07:39 +01:00
Mark Paluch 50d241fcb4 Prepare next development iteration.
See #2121
2022-03-22 14:07:37 +01:00
Mark Paluch d1412f06a4 Release version 5.0 M3 (2022.0.0).
See #2121
2022-03-22 14:00:24 +01:00
Mark Paluch 6bdeca64e2 Prepare 5.0 M3 (2022.0.0).
See #2121
2022-03-22 14:00:02 +01:00
Mark Paluch 9ef35ee46f After release cleanups.
See #2058
2022-03-21 16:44:42 +01:00
Mark Paluch 102d654b92 Prepare next development iteration.
See #2058
2022-03-21 16:44:40 +01:00
326 changed files with 11110 additions and 5441 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
#Tue Feb 22 13:59:12 CET 2022
#Fri Jun 03 09:32:51 CEST 2022
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.8.4/apache-maven-3.8.4-bin.zip
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
Vendored
+9 -93
View File
@@ -9,7 +9,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/3.0.x", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
}
options {
@@ -20,8 +20,9 @@ pipeline {
stages {
stage("test: baseline (Java 17)") {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -31,13 +32,13 @@ pipeline {
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
@@ -48,68 +49,11 @@ pipeline {
}
}
stage("Test other configurations") {
when {
allOf {
branch 'main'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: baseline (next)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
stage("test: baseline (LTS)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.lts.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
}
}
stage('Release to artifactory') {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -119,7 +63,7 @@ pipeline {
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
@@ -139,34 +83,6 @@ pipeline {
}
}
}
stage('Publish documentation') {
when {
branch 'main'
}
agent {
label 'data'
}
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.distribution-repository=temp-private-local " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
}
}
}
post {
+26 -3
View File
@@ -12,13 +12,35 @@ This project is lead and maintained by the community.
== Features
* Spring configuration support using Java based `@Configuration` classes or an XML namespace for a ES clients instances.
* `ElasticsearchRestTemplate` helper class that increases productivity performing common ES operations.
* `ElasticsearchOperations` class and implementations that increases productivity performing common ES operations.
Includes integrated object mapping between documents and POJOs.
* Feature Rich Object Mapping integrated with Springs Conversion Service
* Annotation based mapping metadata
* Automatic implementation of `Repository` interfaces including support for custom search methods.
* CDI support for repositories
== About Elasticsearch versions and clients
=== Elasticsearch 7.17 client libraries
At the end of 2021 Elasticsearch with version 7.17 released the new version of their Java client and deprecated the `RestHighLevelCLient` which was the default way to access Elasticsearch up to then.
Spring Data Elasticsearch will in version 4.4 offer the possibility to optionally use the new client as an alternative to the existing setup using the `RestHighLevelCLient`.
The default client that is used still is the `RestHighLevelCLient`, first because the integration of the new client is not yet complete, the new client still has features missing and bugs which will hopefully be resolved soon.
Second, and more important, the new Elasticsearch client forces users to switch from using `javax.json.spi.JsonProvider` to `jakarta.json.spi.JsonProvider`.
Spring Data Elasticsearch cannot enforce this switch; Spring Boot will switch to `jakarta` with version 3 and then it's safe for Spring Data Elasticsearch to switch to the new client.
So for version 4.4 Spring Data Elasticsearch will keep using the `RestHighLevelCLient` in version 7.17.x (as long as this will be available).
=== Elasticsearch 8 client libraries
In Elasticsearch 8, the `RestHighLevelCLient` has been removed.
This means that a switch to this client version can only be done with the next major upgrade which will be Spring Data Elasticsearch 5, based on Spring Data 3, used by Spring Boot 3, based on Spring 6 and Java 17.
=== Elasticsearch 8 cluster
It should be possible to use the Elasticsearch 7 client to access a cluster running version 8 by setting the appropriate compatibility headers (see the documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration). but I encountered and heard of cases where the response from the server is not parseable by the client although the headers are set, so use with care.
== Code of Conduct
This project is governed by the https://github.com/spring-projects/.github/blob/e3cc2ff230d8f1dca06535aa6b5a4a23815861d4/CODE_OF_CONDUCT.md[Spring Code of Conduct].
@@ -168,7 +190,9 @@ Attach a link to your code or a compressed file containing your code.
== Building from Source
You dont need to build from source to use Spring Data (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Data can be easily built with the https://github.com/takari/maven-wrapper[maven wrapper].
You also need JDK 1.8.
You need JDK 17 or above to build the _main_ branch.
For the branches up to and including release 4.4, JDK 8 is required.
[source,bash]
----
@@ -182,7 +206,6 @@ _Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull r
IMPORTANT: When contributing, please make sure an issue exists in https://github.com/spring-projects/spring-data-elasticsearch/issues[issue tracker] and comment on this issue with how you want to address it.
By this we not only know that someone is working on an issue, we can also align architectural questions and possible solutions before work is invested . We so can prevent that much work is put into Pull Requests that have little or no chances of being merged.
=== Building reference documentation
Building the documentation builds also the project without running tests.
+1 -2
View File
@@ -1,11 +1,10 @@
# Java versions
java.main.tag=17.0.2_8-jdk
java.main.tag=17.0.3_7-jdk
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
# Supported versions of MongoDB
docker.mongodb.4.0.version=4.0.28
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
+29 -16
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>5.0.0-M2</version>
<version>5.0.0-M6</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.0.0-M2</version>
<version>3.0.0-M6</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -18,13 +18,22 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<elasticsearch>7.17.1</elasticsearch>
<elasticsearch-java>7.17.1</elasticsearch-java>
<log4j>2.17.1</log4j>
<springdata.commons>3.0.0-M6</springdata.commons>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.5</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<elasticsearch-java>8.3.3</elasticsearch-java>
<log4j>2.18.0</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>3.0.0-M2</springdata.commons>
<testcontainers>1.16.2</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<hoverfly>0.14.2</hoverfly>
<jsonassert>1.5.1</jsonassert>
<testcontainers>1.17.3</testcontainers>
<wiremock>2.33.2</wiremock>
<java-module-name>spring.data.elasticsearch</java-module-name>
<!--
@@ -135,11 +144,12 @@
<scope>test</scope>
</dependency>
<!-- Elasticsearch RestHighLevelClient, will be removed probably in SDE 5 -->
<!-- optional Elasticsearch RestHighLevelClient, deprecated in SDE 5.0 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch}</version>
<version>${elasticsearch-rhlc}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -163,7 +173,7 @@
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>${elasticsearch}</version>
<version>${elasticsearch-java}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -297,14 +307,14 @@
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
<version>${jsonassert}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.32.0</version>
<version>${wiremock}</version>
<scope>test</scope>
<exclusions>
<!-- these exclusions are needed because of Elasticsearch JarHell-->
@@ -322,7 +332,7 @@
<dependency>
<groupId>io.specto</groupId>
<artifactId>hoverfly-java-junit5</artifactId>
<version>0.14.1</version>
<version>${hoverfly}</version>
<scope>test</scope>
</dependency>
@@ -435,7 +445,7 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.5.2</version>
<version>1.7.5</version>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
@@ -444,6 +454,9 @@
</dependency>
</dependencies>
<configuration>
<jvmArgs>
<jvmArg>-XX:+AllowRedefinitionToAddDeleteMethods</jvmArg>
</jvmArgs>
<excludedGroups>integration-test</excludedGroups>
<targetClasses>
<param>org.springframework.data.elasticsearch.core.geo.*</param>
@@ -460,7 +473,7 @@
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
</plugins>
</build>
<profiles>
@@ -522,7 +535,7 @@
<id>local-maven-repo</id>
<url>file:///${project.basedir}/src/test/resources/local-maven-repo</url>
</repository>
</repositories>
<pluginRepositories>
+10 -5
View File
@@ -29,15 +29,19 @@ Requires an installation of https://www.elastic.co/products/elasticsearch[Elasti
[[preface.versions]]
=== Versions
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring Data release train:
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of
Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring
Data release train. The Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
built and tested.
[cols="^,^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
| 2022.0 (Raj) | 4.4.x | 7.17.1 | 5.3.x | 2.7.x
| 2022.0 (Turing) | 5.0.x | 8.3.3 | 6.0.x | 3.0.x?
| 2021.2 (Raj) | 4.4.x | 7.17.3 | 5.3.x | 2.7.x
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
| 2021.0 (Pascal) | 4.2.x | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham)footnote:oom[Out of maintenance] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
| 2021.0 (Pascal) | 4.2.xfootnote:oom[Out of maintenance] | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham)footnote:oom[] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
| Neumannfootnote:oom[] | 4.0.xfootnote:oom[] | 7.6.2 | 5.2.12 |2.3.x
| Moorefootnote:oom[] | 3.2.xfootnote:oom[] |6.8.12 | 5.2.12| 2.2.x
| Lovelacefootnote:oom[] | 3.1.xfootnote:oom[] | 6.2.2 | 5.1.19 |2.1.x
@@ -45,4 +49,5 @@ The following table shows the Elasticsearch versions that are used by Spring Dat
| Ingallsfootnote:oom[] | 2.1.xfootnote:oom[] | 2.4.0 | 4.3.25 | 1.5.x
|===
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming the usage of the <<elasticsearch.clients.rest,high-level REST client>>.
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming
the usage of the <<elasticsearch.operations,ElasticsearchOperations interface>>.
@@ -6,15 +6,106 @@ This chapter illustrates configuration and usage of supported Elasticsearch clie
Spring Data Elasticsearch operates upon an Elasticsearch client that is connected to a single Elasticsearch node or a cluster.
Although the Elasticsearch Client can be used to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
[[elasticsearch.clients.rest]]
== High Level REST Client
[[elasticsearch.clients.restclient]]
== Imperative Rest Client
The Java High Level REST Client is the default client of Elasticsearch, it is configured like shown:
To use the imperative (non-reactive) client, a configuration bean must be configured like this:
.High Level REST Client
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
@Configuration
public class MyClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
// ...
@Autowired
ElasticsearchOperations operations; <.>
@Autowired
ElasticsearchClient elasticsearchClient; <.>
@Autowired
RestClient restClient; <.>
----
the following can be injected:
<.> an implementation of `ElasticsearchOperations`
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
This is new Elasticsearch client implementation.
<.> the low level `RestClient` from the Elasticsearch libraries
====
Basically one should just use the `ElasticsearchOperations` to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.reactiverestclient]]
== Reactive Rest Client
When working with the reactive stack, the configuration must be derived from a different class:
====
[source,java]
----
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
@Configuration
public class MyClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
// ...
@Autowired
ReactiveElasticsearchOperations operations; <.>
@Autowired
ReactiveElasticsearchClient elasticsearchClient; <.>
@Autowired
RestClient restClient; <.>
----
the following can be injected:
<.> an implementation of `ElasticsearchOperations`
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
This is based on the new Elasticsearch client implementation.
<.> the low level `RestClient` from the Elasticsearch libraries
====
Basically one should just use the `ReactiveElasticsearchOperations` to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.resthighlevelclient]]
== High Level REST Client (deprecated)
The Java RestHighLevelClient is deprecated, but still can be configured like shown (read
<<elasticsearch-migration-guide-4.4-5.0.old-client>> as well):
.RestHighLevelClient
====
[source,java]
----
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@@ -53,16 +144,21 @@ IndexResponse response = highLevelClient.index(request,RequestOptions.DEFAULT);
====
[[elasticsearch.clients.reactive]]
== Reactive Client
== Reactive Client (deprecated)
The `ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
The `org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
It uses the request/response objects provided by the Elasticsearch core project.
Calls are directly operated on the reactive stack, **not** wrapping async (thread pool bound) responses into reactive types.
.Reactive REST Client
This was the first reactive implementation Spring Data Elasticsearch provided, but now is deprecated in favour of the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient`
which uses the functionality offered by the new Elasticsearch client libraries.
.Reactive REST Client (deprecated)
====
[source,java]
----
import org.springframework.data.elasticsearch.client.erhlc.AbstractReactiveElasticsearchConfiguration;
@Configuration
public class ReactiveRestClientConfig extends AbstractReactiveElasticsearchConfiguration {
@@ -89,8 +185,6 @@ Mono<IndexResponse> response = client.index(request ->
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
====
NOTE: The ReactiveClient response, especially for search operations, is bound to the `from` (offset) & `size` (limit) options of the request.
[[elasticsearch.clients.configuration]]
== Client Configuration
@@ -118,12 +212,7 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
return headers;
})
.withClientConfigurer( <.>
ReactiveRestClients.WebClientConfigurationCallback.from(webClient -> {
// ...
return webClient;
}))
.withClientConfigurer( <.>
RestClients.RestClientConfigurationCallback.from(clientBuilder -> {
ElasticsearchClients.ElasticsearchClientConfigurationCallback.from(clientBuilder -> {
// ...
return clientBuilder;
}))
@@ -144,16 +233,52 @@ Default is 5 sec.
<.> Optionally set headers.
<.> Add basic authentication.
<.> A `Supplier<Header>` function can be specified which is called every time before a request is sent to Elasticsearch - here, as an example, the current time is written in a header.
<.> for reactive setup a function configuring the `WebClient`
<.> for non-reactive setup a function configuring the REST client
<.> a function to configure the created client (see <<elasticsearch.clients.configuration.callbacks>>), can be added multiple times.
====
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens.
If this is used in the reactive setup, the supplier function *must not* block!
[[elasticsearch.clients.configuration.callbacks]]
=== Client configuration callbacks
The `ClientConfiguration` class offers the most common parameters to configure the client. In the case this is not
enough, the user can add callback functions by using the `withClientConfigurer(ClientConfigurationCallback<?>)` method.
The following callbacks are provided:
==== Configuration of the low level Elasticsearch `RestClient`:
====
[source,java]
----
ClientConfiguration.builder()
.withClientConfigurer(ElasticsearchClients.ElasticsearchRestClientConfigurationCallback.from(restClientBuilder -> {
// configure the Elasticsearch RestClient
return restClientBuilder;
}))
.build();
----
====
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `RestClient`:
====
[source,java]
----
ClientConfiguration.builder()
.withClientConfigurer(ElasticsearchClients.ElasticsearchHttpClientConfigurationCallback.from(httpAsyncClientBuilder -> {
// configure the HttpAsyncClient
return httpAsyncClientBuilder;
}))
.build();
----
====
=== Elasticsearch 7 compatibility headers
When using Spring Data Elasticsearch 4 - which uses the Elasticsearch 7 client libraries - and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
When using the deprecated `RestHighLevelClient` and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html[see Elasticsearch
documentation].
@@ -211,7 +211,7 @@ void init() {
== Retrieving data
Currently native search queries must be used to query the data, so there is no support from standard repository methods. <<repositories.custom-implementations>> can be used instead.
Currently native queries must be used to query the data, so there is no support from standard repository methods. <<repositories.custom-implementations>> can be used instead.
The following code shows as an example how to retrieve all entries that have a _vote_ (which must be _answers_, because only answers can have a vote) using an `ElasticsearchOperations` instance:
@@ -219,11 +219,17 @@ The following code shows as an example how to retrieve all entries that have a _
[source,java]
----
SearchHits<Statement> hasVotes() {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(hasChildQuery("vote", matchAllQuery(), ScoreMode.None))
.build();
return operations.search(query, Statement.class);
Query query = NativeQuery.builder()
.withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query.of(qb -> qb //
.hasChild(hc -> hc
.queryName("vote") //
.query(matchAllQueryAsQuery()) //
.scoreMode(ChildScoreMode.None)//
)))
.build();
return operations.search(query, Statement.class);
}
----
====
@@ -14,7 +14,7 @@ The parameters of the `@Document` annotation that are relevant for the index se
== Removals
The `@Score` annotation that was used to set the score return value in an entity was deprecated in version 4.0 and has been removed.
Scroe values are returned in the `SearchHit` instances that encapsulate the returned entities.
Score values are returned in the `SearchHit` instances that encapsulate the returned entities.
The `org.springframework.data.elasticsearch.ElasticsearchException` class has been removed.
The remaining usages have been replaced with `org.springframework.data.mapping.MappingException` and `org.springframework.dao.InvalidDataAccessApiUsageException`.
@@ -28,6 +28,14 @@ Connections to Elasticsearch must be made using either the imperative `Elasticse
In 4.3 two classes (`ElasticsearchAggregations` and `ElasticsearchAggregation`) had been moved to the `org.springframework.data.elasticsearch.core.clients.elasticsearch7` package in preparation for the integration of the new Elasticsearch client.
The were moved back to the `org.springframework.data.elasticsearch.core` package as we keep the classes use the old Elasticsearch client where they were.
=== Behaviour change
The `ReactiveElasticsearchTemplate`, when created directly or by Spring Boot configuration had a default refresh policy of IMMEDIATE.
This could cause performance issues on heavy indexing and was different than the default behaviour of Elasticsearch.
This has been changed to that now the default refresh policy is NONE.
When the
`ReactiveElasticsearchTemplate` was provided by using the configuration like described in <<elasticsearch.clients.reactive>> the default refresh policy already was set to NONE.
[[elasticsearch-migration-guide-4.3-4.4.new-clients]]
== New Elasticsearch client
@@ -61,7 +69,7 @@ public class SpringdataElasticTestApplication {
====
Remove Spring Data Elasticsearch related properties from your application configuration.
If Spring Data Elasticsearch was configured using a programmatic configuration (see <<elastisearch.clients>>), remove these beans from the Spring application context.
If Spring Data Elasticsearch was configured using a programmatic configuration (see <<elasticsearch.clients>>), remove these beans from the Spring application context.
==== Add dependencies
@@ -74,7 +82,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.1</version>
<version>7.17.3</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -85,7 +93,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>7.17.1</version>
<version>7.17.3</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -0,0 +1,154 @@
[[elasticsearch-migration-guide-4.4-5.0]]
= Upgrading from 4.4.x to 5.0.x
This section describes breaking changes from version 4.4.x to 5.0.x and how removed features can be replaced by new introduced features.
[[elasticsearch-migration-guide-4.4-4.5.deprecations]]
== Deprecations
=== `org.springframework.data.elasticsearch.client.erhlc` package
See <<elasticsearch-migration-guide-4.4-5.0.breaking-changes-packages>>, all classes in this package have been deprecated, as the default client implementations to use are the ones based on the new Java Client from Elasticsearch, see <<elasticsearch-migration-guide-4.4-5.0.new-clients>>
=== Removal of deprecated code
`DateFormat.none` and `DateFormat.custom` had been deprecated since version 4.2 and have been removed.
The properties of `@Document` that were deprecated since 4.2 have been removed.
Use the `@Settings` annotation for these.
`@DynamicMapping` and `@DynamicMappingValue` have been removed.
Use `@Document.dynamic` or `@Field.dynamic` instead.
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes]]
== Breaking Changes
=== Removal of deprecated calls
==== suggest calls in operations interfaces have been removed
Both `SearchOperations` and `ReactiveSearchOperations` had deprecated calls that were using Elasticsearch classes as parameters.
These now have been removed and so the dependency on Elasticsearch classes in these APIs has been cleaned.
[[elasticsearch-migration-guide-4.4-5.0.breaking-changes-packages]]
=== Package changes
All the classes that are using or depend on the deprecated Elasticsearch `RestHighLevelClient` have been moved to the package `org.springframework.data.elasticsearch.client.erhlc`.
By this change we now have a clear separation of code using the old deprecated Elasticsearch libraries, code using the new Elasticsearch client and code that is independent of the client implementation.
Also the reactive implementation that was provided up to now has been moved here, as this implementation contains code that was copied and adapted from Elasticsearch libraries.
If you are using `ElasticsearchRestTemplate` directly and not the `ElasticsearchOperations` interface you'll need to adjust your imports as well.
When working with the `NativeSearchQuery` class, you'll need to switch to the `NativeQuery` class, which can take a
`Query` instance comign from the new Elasticsearch client libraries.
You'll find plenty of examples in the test code.
=== Conversion to Java 17 records
The following classes have been converted to `Record`, you might need to adjust the use of getter methods from
`getProp()` to `prop()`:
* `org.springframework.data.elasticsearch.core.AbstractReactiveElasticsearchTemplate.IndexResponseMetaData`
* `org.springframework.data.elasticsearch.core.ActiveShardCount`
* `org.springframework.data.elasticsearch.support.Version`
* `org.springframework.data.elasticsearch.support.ScoreDoc`
* `org.springframework.data.elasticsearch.core.query.ScriptData`
* `org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm`
=== New HttpHeaders class
Until version 4.4 the client configuration used the `HttpHeaders` class from the `org.springframework:spring-web`
project.
This introduces a dependency on that artifact.
Users that do not use spring-web then face an error as this class cannot be found.
In version 5.0 we introduce our own `HttpHeaders` to configure the clients.
So if you are using headers in the client configuration, you need to replace `org.springframework.http.HttpHeaders`
with `org.springframework.data.elasticsearch.support.HttpHeaders`.
Hint: You can pass a `org.springframework.http
.HttpHeaders` to the `addAll()` method of `org.springframework.data.elasticsearch.support.HttpHeaders`.
[[elasticsearch-migration-guide-4.4-5.0.new-clients]]
== New Elasticsearch client
Spring Data Elasticsearch now uses the new `ElasticsearchClient` and has deprecated the use of the previous `RestHighLevelClient`.
=== Imperative style configuration
To configure Spring Data Elasticsearch to use the new client, it is necessary to create a configuration bean that derives from `org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration`:
====
[source,java]
----
@Configuration
public class NewRestClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
----
====
The configuration is done in the same way as with the old client, but it is not necessary anymore to create more than the configuration bean.
With this configuration, the following beans will be available in the Spring application context:
* a `RestClient` bean, that is the configured low level `RestClient` that is used by the Elasticsearch client
* an `ElasticsearchClient` bean, this is the new client that uses the `RestClient`
* an `ElasticsearchOperations` bean, available with the bean names _elasticsearchOperations_ and _elasticsearchTemplate_, this uses the `ElasticsearchClient`
=== Reactive style configuration
To use the new client in a reactive environment the only difference is the class from which to derive the configuration:
====
[source,java]
----
@Configuration
public class NewRestClientConfig extends ReactiveElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder() //
.connectedTo("localhost:9200") //
.build();
}
}
----
====
With this configuration, the following beans will be available in the Spring application context:
* a `RestClient` bean, that is the configured low level `RestClient` that is used by the Elasticsearch client
* an `ReactiveElasticsearchClient` bean, this is the new reactive client that uses the `RestClient`
* an `ReactiveElasticsearchOperations` bean, available with the bean names _reactiveElasticsearchOperations_ and _reactiveElasticsearchTemplate_, this uses the `ReactiveElasticsearchClient`
[[elasticsearch-migration-guide-4.4-5.0.old-client]]
=== Still want to use the old client?
The old deprecated `RestHighLevelClient` can still be used, but you will need to add the dependency explicitly to your application as Spring Data Elasticsearch does not pull it in automatically anymore:
====
[source,xml]
----
<!-- include the RHLC, specify version explicitly -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.5</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
----
====
Make sure to specify the version 7.17.5 explicitly, otherwise maven will resolve to 8.3.3, and this does not exist.
@@ -87,12 +87,19 @@ private ElasticsearchOperations operations;
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(boolFilter().must(termFilter("id", documentId)))
.build();
Query query = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFilter( q -> q
.bool(b -> b
.must(m -> m
.term(t -> t
.field("id")
.value(documentId))
)))
.build();
Page<SampleEntity> sampleEntities = operations.searchForPage(searchQuery, SampleEntity.class, index);
SearchHits<SampleEntity> sampleEntities = operations.search(query, SampleEntity.class, index);
----
====
@@ -107,13 +114,15 @@ This is internally used by Spring Data Elasticsearch to provide the implementati
----
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
Query searchQuery = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
SearchHitsIterator<SampleEntity> stream = elasticsearchTemplate.searchForStream(searchQuery, SampleEntity.class, index);
SearchHitsIterator<SampleEntity> stream = elasticsearchOperations.searchForStream(searchQuery, SampleEntity.class,
index);
List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {
@@ -124,23 +133,28 @@ stream.close();
----
====
There are no methods in the `SearchOperations` API to access the scroll id, if it should be necessary to access this, the following methods of the `ElasticsearchRestTemplate` can be used:
There are no methods in the `SearchOperations` API to access the scroll id, if it should be necessary to access this,
the following methods of the `AbstractElasticsearchTemplate` can be used (this is the base implementation for the
different `ElasticsearchOperations` implementations:
====
[source,java]
----
@Autowired ElasticsearchRestTemplate template;
@Autowired ElasticsearchOperations operations;
AbstractElasticsearchTemplate template = (AbstractElasticsearchTemplate)operations;
IndexCoordinates index = IndexCoordinates.of("sample-index");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
Query query = NativeQuery.builder()
.withQuery(q -> q
.matchAll(ma -> ma))
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
SearchScrollHits<SampleEntity> scroll = template.searchScrollStart(1000, searchQuery, SampleEntity.class, index);
SearchScrollHits<SampleEntity> scroll = template.searchScrollStart(1000, query, SampleEntity.class, index);
String scrollId = scroll.getScrollId();
List<SampleEntity> sampleEntities = new ArrayList<>();
@@ -261,3 +275,42 @@ SearchHits<SomethingToBuy> searchHits = operations.search(query, SomethingToBuy.
====
This works with every implementation of the `Query` interface.
[[elasticsearch.misc.point-in-time]]
== Point In Time (PIT) API
`ElasticsearchOperations` supports the point in time API of Elasticsearch (see https://www.elastic
.co/guide/en/elasticsearch/reference/8.3/point-in-time-api.html). The following code snippet shows how to use this
feature with a fictional `Person` class:
====
[source,java]
----
ElasticsearchOperations operations; // autowired
Duration tenSeconds = Duration.ofSeconds(10);
String pit = operations.openPointInTime(IndexCoordinates.of("person"), tenSeconds); <.>
// create query for the pit
Query query1 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Smith"))
.withPointInTime(new Query.PointInTime(pit, tenSeconds)) <.>
.build();
SearchHits<Person> searchHits1 = operations.search(query1, Person.class);
// do something with the data
// create 2nd query for the pit, use the id returned in the previous result
Query query2 = new CriteriaQueryBuilder(Criteria.where("lastName").is("Miller"))
.withPointInTime(
new Query.PointInTime(searchHits1.getPointInTimeId(), tenSeconds)) <.>
.build();
SearchHits<Person> searchHits2 = operations.search(query2, Person.class);
// do something with the data
operations.closePointInTime(searchHits2.getPointInTimeId()); <.>
----
<.> create a point in time for an index (can be multiple names) and a keep-alive duration and retrieve its id
<.> pass that id into the query to search together with the next keep-alive value
<.> for the next query, use the id returned from the previous search
<.> when done, close the point in time using the last returned id
====
@@ -1,11 +1,18 @@
[[new-features]]
= What's new
[[new-features.5-0-0]]
== New in Spring Data Elasticsearch 5.0
* Upgrade to Java 17 baseline
* Upgrade to Spring Framework 6
* Use the new Elasticsearch client library
[[new-features.4-4-0]]
== New in Spring Data Elasticsearch 4.4
* Introduction of new imperative and reactive clients using the classes from the new Elasticsearch Java client
* Upgrade to Elasticsearch 7.17.1.
* Upgrade to Elasticsearch 7.17.3.
[[new-features.4-3-0]]
== New in Spring Data Elasticsearch 4.3
@@ -30,36 +30,10 @@ There is support for automatic creation of indices and writing the mappings when
====
[[elasticsearch.operations.resttemplate]]
== ElasticsearchRestTemplate
The `ElasticsearchRestTemplate` is an implementation of the `ElasticsearchOperations` interface using the <<elasticsearch.clients.rest>>.
.ElasticsearchRestTemplate configuration
====
[source,java]
----
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() { <1>
return RestClients.create(ClientConfiguration.localhost()).rest();
}
// no special bean creation needed <2>
}
----
<1> Setting up the <<elasticsearch.clients.rest>>.
<2> The base class `AbstractElasticsearchConfiguration` already provides the `elasticsearchTemplate` bean.
====
[[elasticsearch.operations.usage]]
== Usage examples
As both `ElasticsearchTemplate` and `ElasticsearchRestTemplate` implement the `ElasticsearchOperations` interface, the code to use them is not different.
The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller.
The decision, if this is using the `TransportClient` or the `RestClient` is made by providing the corresponding Bean with one of the configurations shown above.
.ElasticsearchOperations usage
====
@@ -150,7 +124,7 @@ An Iterator returned by the streaming functions of the `SearchOperations` interf
[[elasticsearch.operations.queries]]
== Queries
Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchOperations` interface take a `Query` parameter that defines the query to execute for searching. `Query` is an interface and Spring Data Elasticsearch provides three implementations: `CriteriaQuery`, `StringQuery` and `NativeSearchQuery`.
Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchOperations` interface take a `Query` parameter that defines the query to execute for searching. `Query` is an interface and Spring Data Elasticsearch provides three implementations: `CriteriaQuery`, `StringQuery` and `NativeQuery`.
[[elasticsearch.operations.criteriaquery]]
=== CriteriaQuery
@@ -230,7 +204,7 @@ The following code shows a query that searches for persons having the first name
[source,java]
----
Query query = new SearchQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
Query query = new StringQuery("{ \"match\": { \"firstname\": { \"query\": \"Jack\" } } } ");
SearchHits<Person> searchHits = operations.search(query, Person.class);
----
@@ -238,21 +212,28 @@ SearchHits<Person> searchHits = operations.search(query, Person.class);
Using `StringQuery` may be appropriate if you already have an Elasticsearch query to use.
[[elasticsearch.operations.nativesearchquery]]
=== NativeSearchQuery
[[elasticsearch.operations.nativequery]]
=== NativeQuery
`NativeSearchQuery` is the class to use when you have a complex query, or a query that cannot be expressed by using the `Criteria` API, for example when building queries and using aggregates.
It allows to use all the different `QueryBuilder` implementations from the Elasticsearch library therefore named "native".
`NativeQuery` is the class to use when you have a complex query, or a query that cannot be expressed by using the `Criteria` API, for example when building queries and using aggregates.
It allows to use all the different `co.elastic.clients.elasticsearch._types.query_dsl.Query` implementations from the Elasticsearch library therefore named "native".
The following code shows how to search for persons with a given firstname and for the found documents have a terms aggregation that counts the number of occurences of the lastnames for these persons:
====
[source,java]
----
Query query = new NativeSearchQueryBuilder()
.addAggregation(terms("lastnames").field("lastname").size(10)) //
.withQuery(QueryBuilders.matchQuery("firstname", firstName))
.build();
Query query = NativeQuery.builder()
.withAggregation("lastNames", Aggregation.of(a -> a
.terms(ta -> ta.field("last-name").size(10))))
.withQuery(q -> q
.match(m -> m
.field("firstName")
.query(firstName)
)
)
.withPageable(pageable)
.build();
SearchHits<Person> searchHits = operations.search(query, Person.class);
----
@@ -53,7 +53,7 @@ interface BookRepository extends Repository<Book, String> {
@HighlightField(name = "name"),
@HighlightField(name = "summary")
})
List<SearchHit<Book>> findByNameOrSummary(String text, String summary);
SearchHits<Book> findByNameOrSummary(String text, String summary);
}
----
====
@@ -62,6 +62,30 @@ It is possible to define multiple fields to be highlighted like above, and both
In the search results the highlight data can be retrieved from the `SearchHit` class.
=== @SourceFilters
Sometimes the user does not need to have all the properties of an entity returned from a search but only a subset.
Elasticsearch provides source filtering to reduce the amount of data that is transferred across the network to the
application.
When working with `Query` implementations and the `ElasticsearchOperations` this is easily possible by setting a
source filter on the query.
When using repository methods there is the `@SourceFilters` annotation:
====
[source,java]
----
interface BookRepository extends Repository<Book, String> {
@SourceFilters(includes = "name")
SearchHits<Book> findByName(String text);
}
----
====
In this example, all the properties of the returned `Book` objects would be `null` except the name.
[[elasticsearch.annotation]]
== Annotation based configuration
@@ -12,4 +12,7 @@ include::elasticsearch-migration-guide-4.1-4.2.adoc[]
include::elasticsearch-migration-guide-4.2-4.3.adoc[]
include::elasticsearch-migration-guide-4.3-4.4.adoc[]
include::elasticsearch-migration-guide-4.4-5.0.adoc[]
:leveloffset: -1
@@ -25,6 +25,14 @@ public class NoSuchIndexException extends NonTransientDataAccessResourceExceptio
private final String index;
/**
* @since 4.4
*/
public NoSuchIndexException(String index) {
super(String.format("Index %s not found.", index));
this.index = index;
}
public NoSuchIndexException(String index, Throwable cause) {
super(String.format("Index %s not found.", index), cause);
this.index = index;
@@ -16,9 +16,11 @@
package org.springframework.data.elasticsearch.annotations;
/**
* Values based on reference doc - https://www.elastic.co/guide/reference/mapping/date-format/. The patterns are taken
* from this documentation and slightly adapted so that a Java {@link java.time.format.DateTimeFormatter} produces the
* same values as the Elasticsearch formatter.
* Values based on <a href="https://www.elastic.co/guide/reference/mapping/date-format/">Elasticsearch reference
* documentation</a>. The patterns are taken from this documentation and slightly adapted so that a Java
* {@link java.time.format.DateTimeFormatter} produces the same values as the Elasticsearch formatter. Use
* <code>format = {}</code> to disable built-in date formats in the {@link Field} annotation. If you want to use only a
* custom date format pattern, you must set the <code>format</code> property to empty <code>{}</code>.
*
* @author Jakub Vavrik
* @author Tim te Beek
@@ -26,19 +28,6 @@ package org.springframework.data.elasticsearch.annotations;
* @author Sascha Woo
*/
public enum DateFormat {
/**
* @deprecated since 4.2, will be removed in a future version. Use <code>format = {}</code> to disable built-in date
* formats in the @Field annotation.
*/
@Deprecated
none(""), //
/**
* @deprecated since 4.2, will be removed in a future version.It is no longer required for using a custom date format
* pattern. If you want to use only a custom date format pattern, you must set the <code>format</code>
* property to empty <code>{}</code>.
*/
@Deprecated
custom(""), //
basic_date("uuuuMMdd"), //
basic_date_time("uuuuMMdd'T'HHmmss.SSSXXX"), //
basic_date_time_no_millis("uuuuMMdd'T'HHmmssXXX"), //
@@ -60,7 +49,7 @@ public enum DateFormat {
date_hour_minute_second_millis("uuuu-MM-dd'T'HH:mm:ss.SSS"), //
date_optional_time("uuuu-MM-dd['T'HH:mm:ss.SSSXXX]"), //
date_time("uuuu-MM-dd'T'HH:mm:ss.SSSXXX"), //
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in it's implementation
date_time_no_millis("uuuu-MM-dd'T'HH:mm:ssVV"), // here Elasticsearch uses the zone-id in its implementation
epoch_millis("epoch_millis"), //
epoch_second("epoch_second"), //
hour("HH"), //
@@ -53,49 +53,6 @@ public @interface Document {
*/
String indexName();
/**
* Use server-side settings when creating the index.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
boolean useServerConfiguration() default false;
/**
* Number of shards for the index {@link #indexName()}. Used for index creation. <br/>
* With version 4.0, the default value is changed from 5 to 1 to reflect the change in the default settings of
* Elasticsearch which changed to 1 as well in Elasticsearch 7.0.
* ComposableAnnotationsUnitTest.documentAnnotationShouldBeComposable:60
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
short shards() default 1;
/**
* Number of replicas for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
short replicas() default 1;
/**
* Refresh interval for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
String refreshInterval() default "1s";
/**
* Index storage type for the index {@link #indexName()}. Used for index creation.
*
* @deprecated since 4.2, use the {@link Setting} annotation to configure settings
*/
@Deprecated
String indexStoreType() default "fs";
/**
* Configuration whether to create an index on repository bootstrapping.
*/
@@ -1,40 +0,0 @@
/*
* Copyright 2019-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to set the dynamic mapping mode
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic.html">elasticsearch doc</a>}
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.0
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD })
@Documented
@Deprecated
public @interface DynamicMapping {
DynamicMappingValue value() default DynamicMappingValue.True;
}
@@ -53,9 +53,8 @@ public @interface Field {
String value() default "";
/**
* The <em>name</em> to be used to store the field inside the document.
* <p>
* √5 If not set, the name of the annotated property is used.
* The <em>name</em> to be used to store the field inside the document. If not set, the name of the annotated property
* is used.
*
* @since 3.2
*/
@@ -15,8 +15,14 @@
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.annotation.QueryAnnotation;
import java.lang.annotation.*;
/**
* Query
@@ -34,16 +40,17 @@ import java.lang.annotation.*;
public @interface Query {
/**
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0. Alias for query.
*/
@AliasFor("query")
String value() default "";
/**
* Named Query Named looked up by repository.
*
* @deprecated since 4.2, not implemented and used anywhere
* @return Elasticsearch query to be used when executing query. May contain placeholders eg. ?0. Alias for value
* @since 5.0
*/
String name() default "";
@AliasFor("value")
String query() default "";
/**
* Returns whether the query defined should be executed as count projection.
@@ -0,0 +1,73 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation can be placed on repository methods to define the properties that should be requested from
* Elasticsearch when the method is run.
*
* @author Alexander Torres
* @author Peter-Josef Meisch
* @since 5.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
public @interface SourceFilters {
/**
* Properties to be requested from Elasticsearch to be included in the response. These can be passed in as literals
* like
*
* <pre>
* {@code @SourceFilters(includes = {"property1", "property2"})}
* </pre>
*
* or as a parameterized value
*
* <pre>
* {@code @SourceFilters(includes = "?0")}
* </pre>
*
* when the list of properties is passed as a function parameter.
*/
String[] includes() default "";
/**
* Properties to be requested from Elasticsearch to be excluded in the response. These can be passed in as literals
* like
*
* <pre>
* {@code @SourceFilters(excludes = {"property1", "property2"})}
* </pre>
*
* or as a parameterized value
*
* <pre>
* {@code @SourceFilters(excludes = "?0")}
* </pre>
*
* when the list of properties is passed as a function parameter.
*/
String[] excludes() default "";
}
@@ -26,9 +26,7 @@ import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
@@ -68,8 +66,8 @@ public interface ClientConfiguration {
}
/**
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@code hostAndPort}.
* For example given the endpoint http://localhost:9200
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@code hostAndPort}. For
* example given the endpoint http://localhost:9200
*
* <pre class="code">
* ClientConfiguration configuration = ClientConfiguration.create("localhost:9200");
@@ -82,8 +80,8 @@ public interface ClientConfiguration {
}
/**
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@link InetSocketAddress}.
* For example given the endpoint http://localhost:9200
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@link InetSocketAddress}. For
* example given the endpoint http://localhost:9200
*
* <pre class="code">
* ClientConfiguration configuration = ClientConfiguration
@@ -170,14 +168,6 @@ public interface ClientConfiguration {
*/
Function<WebClient, WebClient> getWebClientConfigurer();
/**
* @return the Rest Client configuration callback.
* @since 4.2
* @deprecated since 4.3 use {@link #getClientConfigurers()}
*/
@Deprecated
HttpClientConfigCallback getHttpClientConfigurer();
/**
* @return the client configuration callbacks
* @since 4.3
@@ -346,29 +336,6 @@ public interface ClientConfiguration {
*/
TerminalClientConfigurationBuilder withProxy(String proxy);
/**
* set customization hook in case of a reactive configuration
*
* @param webClientConfigurer function to configure the WebClient
* @return the {@link TerminalClientConfigurationBuilder}.
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
* {@link ReactiveRestClients.WebClientConfigurationCallback}
*/
@Deprecated
TerminalClientConfigurationBuilder withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
/**
* Register a {HttpClientConfigCallback} to configure the non-reactive REST client.
*
* @param httpClientConfigurer configuration callback, must not be null.
* @return the {@link TerminalClientConfigurationBuilder}.
* @since 4.2
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
* {@link RestClients.RestClientConfigurationCallback}
*/
@Deprecated
TerminalClientConfigurationBuilder withHttpClientConfigurer(HttpClientConfigCallback httpClientConfigurer);
/**
* Register a {@link ClientConfigurationCallback} to configure the client.
*
@@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@@ -31,8 +30,7 @@ import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint;
import org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
@@ -51,7 +49,7 @@ class ClientConfigurationBuilder
implements ClientConfigurationBuilderWithRequiredEndpoint, MaybeSecureClientConfigurationBuilder {
private final List<InetSocketAddress> hosts = new ArrayList<>();
private HttpHeaders headers = HttpHeaders.EMPTY;
private HttpHeaders headers = new HttpHeaders();
private boolean useSsl;
private @Nullable SSLContext sslContext;
private @Nullable HostnameVerifier hostnameVerifier;
@@ -61,10 +59,10 @@ class ClientConfigurationBuilder
private @Nullable String password;
private @Nullable String pathPrefix;
private @Nullable String proxy;
private Function<WebClient, WebClient> webClientConfigurer = Function.identity();
private Supplier<HttpHeaders> headersSupplier = () -> HttpHeaders.EMPTY;
@Deprecated private HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
private List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
private final Function<WebClient, WebClient> webClientConfigurer = Function.identity();
private Supplier<HttpHeaders> headersSupplier = HttpHeaders::new;
@Deprecated private final HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
private final List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
/*
* (non-Javadoc)
@@ -75,7 +73,7 @@ class ClientConfigurationBuilder
Assert.notEmpty(hostAndPorts, "At least one host is required");
this.hosts.addAll(Arrays.stream(hostAndPorts).map(ClientConfigurationBuilder::parse).collect(Collectors.toList()));
this.hosts.addAll(Arrays.stream(hostAndPorts).map(ClientConfigurationBuilder::parse).toList());
return this;
}
@@ -201,28 +199,6 @@ class ClientConfigurationBuilder
return this;
}
@Override
public TerminalClientConfigurationBuilder withWebClientConfigurer(
Function<WebClient, WebClient> webClientConfigurer) {
Assert.notNull(webClientConfigurer, "webClientConfigurer must not be null");
this.webClientConfigurer = webClientConfigurer;
this.clientConfigurers.add(ReactiveRestClients.WebClientConfigurationCallback.from(webClientConfigurer));
return this;
}
@Override
public TerminalClientConfigurationBuilder withHttpClientConfigurer(HttpClientConfigCallback httpClientConfigurer) {
Assert.notNull(httpClientConfigurer, "httpClientConfigurer must not be null");
this.httpClientConfigurer = httpClientConfigurer;
this.clientConfigurers
.add(RestClients.RestClientConfigurationCallback.from(httpClientConfigurer::customizeHttpClient));
return this;
}
@Override
public TerminalClientConfigurationBuilder withClientConfigurer(
ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer) {
@@ -250,9 +226,6 @@ class ClientConfigurationBuilder
public ClientConfiguration build() {
if (username != null && password != null) {
if (HttpHeaders.EMPTY.equals(headers)) {
headers = new HttpHeaders();
}
headers.setBasicAuth(username, password);
}
@@ -19,7 +19,6 @@ import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
@@ -30,6 +29,7 @@ import org.springframework.util.ObjectUtils;
* @author Mark Paluch
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Oliver Drotbohm
* @since 3.2
*/
public abstract class ClientLogger {
@@ -125,10 +125,10 @@ public abstract class ClientLogger {
* @param logId the correlation id, see {@link #newLogId()}.
* @param statusCode the HTTP status code.
*/
public static void logRawResponse(String logId, @Nullable HttpStatus statusCode) {
public static void logRawResponse(String logId, @Nullable Integer statusCode) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received raw response: %s", logId, statusCode));
WIRE_LOGGER.trace(String.format("[%s] Received raw response: %d", logId, statusCode));
}
}
@@ -139,10 +139,10 @@ public abstract class ClientLogger {
* @param statusCode the HTTP status code.
* @param headers a String containing the headers
*/
public static void logRawResponse(String logId, @Nullable HttpStatus statusCode, String headers) {
public static void logRawResponse(String logId, @Nullable Integer statusCode, String headers) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%n%s", logId, statusCode, headers));
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%n%s", logId, statusCode, headers));
}
}
@@ -153,10 +153,10 @@ public abstract class ClientLogger {
* @param statusCode the HTTP status code.
* @param body body content.
*/
public static void logResponse(String logId, HttpStatus statusCode, String body) {
public static void logResponse(String logId, Integer statusCode, String body) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%nResponse body: %s", logId, statusCode, body));
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%nResponse body: %s", logId, statusCode, body));
}
}
@@ -169,10 +169,10 @@ public abstract class ClientLogger {
* @param body body content.
* @since 4.4
*/
public static void logResponse(String logId, @Nullable HttpStatus statusCode, String headers, String body) {
public static void logResponse(String logId, @Nullable Integer statusCode, String headers, String body) {
if (isEnabled()) {
WIRE_LOGGER.trace(String.format("[%s] Received response: %s%nHeaders: %s%nResponse body: %s", logId, statusCode,
WIRE_LOGGER.trace(String.format("[%s] Received response: %d%nHeaders: %s%nResponse body: %s", logId, statusCode,
headers, body));
}
}
@@ -17,8 +17,6 @@ package org.springframework.data.elasticsearch.client;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
@@ -28,7 +26,7 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
@@ -63,8 +61,8 @@ class DefaultClientConfiguration implements ClientConfiguration {
Function<WebClient, WebClient> webClientConfigurer, HttpClientConfigCallback httpClientConfigurer,
List<ClientConfigurationCallback<?>> clientConfigurers, Supplier<HttpHeaders> headersSupplier) {
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
this.headers = new HttpHeaders(headers);
this.hosts = List.copyOf(hosts);
this.headers = headers;
this.useSsl = useSsl;
this.sslContext = sslContext;
this.soTimeout = soTimeout;
@@ -129,13 +127,6 @@ class DefaultClientConfiguration implements ClientConfiguration {
return webClientConfigurer;
}
@Deprecated
@Override
public HttpClientConfigCallback getHttpClientConfigurer() {
return httpClientConfigurer;
}
@SuppressWarnings("unchecked")
@Override
public <T> List<ClientConfigurationCallback<?>> getClientConfigurers() {
return clientConfigurers;
@@ -26,7 +26,7 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @since 3.2
*/
class InetSocketAddressParser {
public class InetSocketAddressParser {
/**
* Parse a host and port string into a {@link InetSocketAddress}.
@@ -36,7 +36,7 @@ class InetSocketAddressParser {
* @return a {@link InetSocketAddress} that is unresolved to avoid DNS lookups.
* @see InetSocketAddress#createUnresolved(String, int)
*/
static InetSocketAddress parse(String hostPortString, int defaultPort) {
public static InetSocketAddress parse(String hostPortString, int defaultPort) {
Assert.notNull(hostPortString, "HostPortString must not be null");
String host;
@@ -0,0 +1,25 @@
/*
* Copyright 2022 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;
/**
* @author Peter-Josef Meisch
*/
public class UnsupportedClientOperationException extends RuntimeException {
public UnsupportedClientOperationException(Class<?> clientClass, String operation) {
super("Client %1$s does not support the operation %2$s".formatted(clientClass, operation));
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2022 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._types.aggregations.Aggregate;
/**
* Class to combine an Elasticsearch {@link co.elastic.clients.elasticsearch._types.aggregations.Aggregate} with its
* name. Necessary as the Elasticsearch Aggregate does not know its name.
*
* @author Peter-Josef Meisch
* @since 4.4
*/
public class Aggregation {
private final String name;
private final Aggregate aggregate;
public Aggregation(String name, Aggregate aggregate) {
this.name = name;
this.aggregate = aggregate;
}
public String getName() {
return name;
}
public Aggregate getAggregate() {
return aggregate;
}
}
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient;
import co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Transport;
import java.io.IOException;
@@ -32,7 +33,7 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 4.4
*/
public abstract class ChildTemplate<CLIENT extends ApiClient> {
public abstract class ChildTemplate<T extends Transport, CLIENT extends ApiClient<T, CLIENT>> {
protected final CLIENT client;
protected final RequestConverter requestConverter;
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.transport.ElasticsearchTransport;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
@@ -29,7 +30,8 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverte
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ClusterTemplate extends ChildTemplate<ElasticsearchClusterClient> implements ClusterOperations {
public class ClusterTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchClusterClient>
implements ClusterOperations {
public ClusterTemplate(ElasticsearchClusterClient client, ElasticsearchConverter elasticsearchConverter) {
super(client, elasticsearchConverter);
@@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.data.elasticsearch.core.convert.GeoConverters;
@@ -67,9 +68,13 @@ class CriteriaFilterProcessor {
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
if (chainedCriteria.isOr()) {
// todo #1973
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
queriesForEntries(chainedCriteria).forEach(boolQueryBuilder::should);
filterQueries.add(new Query(boolQueryBuilder.build()));
} else if (chainedCriteria.isNegating()) {
// todo #1973
Collection<? extends Query> negatingFilters = buildNegatingFilter(criteria.getField().getName(),
criteria.getFilterCriteriaEntries());
filterQueries.addAll(negatingFilters);
} else {
filterQueries.addAll(queriesForEntries(chainedCriteria));
}
@@ -85,11 +90,28 @@ class CriteriaFilterProcessor {
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
filterQueries.forEach(boolQueryBuilder::must);
BoolQuery boolQuery = boolQueryBuilder.build();
return Optional.of(boolQuery._toQuery());
return Optional.of(new Query(boolQuery));
}
}
}
private static Collection<? extends Query> buildNegatingFilter(String fieldName,
Set<Criteria.CriteriaEntry> filterCriteriaEntries) {
List<Query> negationFilters = new ArrayList<>();
filterCriteriaEntries.forEach(criteriaEntry -> {
Optional<Query> query = queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName);
if (query.isPresent()) {
BoolQuery negatingFilter = QueryBuilders.bool().mustNot(query.get()).build();
negationFilters.add(new Query(negatingFilter));
}
});
return negationFilters;
}
private static Collection<? extends Query> queriesForEntries(Criteria criteria) {
Assert.notNull(criteria.getField(), "criteria must have a field");
@@ -108,37 +130,37 @@ class CriteriaFilterProcessor {
ObjectBuilder<? extends QueryVariant> queryBuilder = null;
switch (key) {
case WITHIN:
case WITHIN -> {
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
queryBuilder = withinQuery(fieldName, (Object[]) value);
break;
case BBOX:
}
case BBOX -> {
Assert.isTrue(value instanceof Object[],
"Value of a boundedBy filter should be an array of one or two values.");
queryBuilder = boundingBoxQuery(fieldName, (Object[]) value);
break;
case GEO_INTERSECTS:
}
case GEO_INTERSECTS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_INTERSECTS filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "intersects");
break;
case GEO_IS_DISJOINT:
}
case GEO_IS_DISJOINT -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_IS_DISJOINT filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "disjoint");
break;
case GEO_WITHIN:
}
case GEO_WITHIN -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_WITHIN filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "within");
break;
case GEO_CONTAINS:
}
case GEO_CONTAINS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_CONTAINS filter must be a GeoJson object");
queryBuilder = geoJsonQuery(fieldName, (GeoJson<?>) value, "contains");
break;
}
}
return Optional.ofNullable(queryBuilder != null ? queryBuilder.build()._toQuery() : null);
}
private static ObjectBuilder<GeoDistanceQuery> withinQuery(String fieldName, Object[] values) {
private static ObjectBuilder<GeoDistanceQuery> withinQuery(String fieldName, Object... values) {
Assert.noNullElements(values, "Geo distance filter takes 2 not null elements array as parameter.");
Assert.isTrue(values.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
@@ -154,8 +176,7 @@ class CriteriaFilterProcessor {
.distance(dist) //
.distanceType(GeoDistanceType.Plane) //
.location(location -> {
if (values[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) values[0];
if (values[0]instanceof GeoPoint loc) {
location.latlon(latlon -> latlon.lat(loc.getLat()).lon(loc.getLon()));
} else if (values[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) values[0]);
@@ -173,7 +194,7 @@ class CriteriaFilterProcessor {
});
}
private static ObjectBuilder<GeoBoundingBoxQuery> boundingBoxQuery(String fieldName, Object[] values) {
private static ObjectBuilder<GeoBoundingBoxQuery> boundingBoxQuery(String fieldName, Object... values) {
Assert.noNullElements(values, "Geo boundedBy filter takes a not null element array as parameter.");
@@ -218,13 +239,12 @@ class CriteriaFilterProcessor {
)))));
}
private static void twoParameterBBox(GeoBoundingBoxQuery.Builder queryBuilder, Object[] values) {
private static void twoParameterBBox(GeoBoundingBoxQuery.Builder queryBuilder, Object... values) {
Assert.isTrue(allElementsAreOfType(values, GeoPoint.class) || allElementsAreOfType(values, String.class),
" both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
if (values[0] instanceof GeoPoint) {
GeoPoint topLeft = (GeoPoint) values[0];
if (values[0]instanceof GeoPoint topLeft) {
GeoPoint bottomRight = (GeoPoint) values[1];
queryBuilder.boundingBox(bb -> bb //
.tlbr(tlbr -> tlbr //
@@ -307,12 +327,8 @@ class CriteriaFilterProcessor {
StringBuilder sb = new StringBuilder();
sb.append((int) distance.getValue());
switch ((Metrics) distance.getMetric()) {
case KILOMETERS:
sb.append("km");
break;
case MILES:
sb.append("mi");
break;
case KILOMETERS -> sb.append("km");
case MILES -> sb.append("mi");
}
return sb.toString();
@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.Field;
@@ -184,7 +183,7 @@ class CriteriaQueryProcessor {
Criteria.OperationKey key = entry.getKey();
Object value = key.hasValue() ? entry.getValue() : null;
String searchText = value != null ? QueryParserUtil.escape(value.toString()) : "UNKNOWN_VALUE";
String searchText = value != null ? escape(value.toString()) : "UNKNOWN_VALUE";
Query.Builder queryBuilder = new Query.Builder();
switch (key) {
@@ -289,8 +288,7 @@ class CriteriaQueryProcessor {
break;
case IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
queryBuilder.bool(bb -> bb //
.must(mb -> mb //
@@ -311,8 +309,7 @@ class CriteriaQueryProcessor {
}
break;
case NOT_IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
queryBuilder.bool(bb -> bb //
.mustNot(mnb -> mnb //
@@ -358,11 +355,32 @@ class CriteriaQueryProcessor {
sb.append(' ');
}
sb.append('"');
sb.append(QueryParserUtil.escape(item.toString()));
sb.append(escape(item.toString()));
sb.append('"');
}
}
return sb.toString();
}
/**
* Returns a String where those characters that TextParser expects to be escaped are escaped by a preceding
* <code>\</code>. Copied from Apachae 2 licensed org.apache.lucene.queryparser.flexible.standard.QueryParserUtil
* class
*/
public static String escape(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// These characters are part of the query syntax and must be escaped
if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '['
|| c == ']' || c == '\"' || c == '{' || c == '}' || c == '~' || c == '*' || c == '?' || c == '|' || c == '&'
|| c == '/') {
sb.append('\\');
}
sb.append(c);
}
return sb.toString();
}
}
@@ -19,6 +19,7 @@ import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.elasticsearch.core.MgetResponse;
import co.elastic.clients.elasticsearch.core.explain.ExplanationDetail;
import co.elastic.clients.elasticsearch.core.get.GetResult;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.NestedIdentity;
import co.elastic.clients.json.JsonData;
@@ -28,6 +29,7 @@ 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;
@@ -71,24 +73,17 @@ final class DocumentAdapters {
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
hit.innerHits().forEach((name, innerHitsResult) -> {
// noinspection ReturnOfNull
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null,
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, null,
searchDocument -> null, jsonpMapper));
});
NestedMetaData nestedMetaData = from(hit.nested());
// todo #1973 explanation
Explanation explanation = from(hit.explanation());
// todo #1973 matchedQueries
List<String> matchedQueries = null;
// todo #1973 documentFields
Map<String, List<Object>> documentFields = Collections.emptyMap();
List<String> matchedQueries = hit.matchedQueries();
Document document;
Object source = hit.source();
if (source == null) {
// Elasticsearch provides raw JsonData, so we build the fields into a JSON string
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{");
final boolean[] firstField = { true };
hit.fields().forEach((key, jsonData) -> {
@@ -100,12 +95,29 @@ final class DocumentAdapters {
firstField[0] = false;
});
sb.append('}');
document = Document.parse(sb.toString());
return new EntityAsMap().fromJson(sb.toString());
};
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
Map<String, List<Object>> documentFields = new LinkedHashMap<>();
hitFieldsAsMap.forEach((key, value) -> {
if (value instanceof List) {
// noinspection unchecked
documentFields.put(key, (List<Object>) value);
} else {
documentFields.put(key, Collections.singletonList(value));
}
});
Document document;
Object source = hit.source();
if (source == null) {
document = Document.from(hitFieldsAsMap);
} else {
if (source instanceof EntityAsMap) {
document = Document.from((EntityAsMap) source);
} else if (source instanceof JsonData) {
JsonData jsonData = (JsonData) source;
} else if (source instanceof JsonData jsonData) {
document = Document.from(jsonData.to(EntityAsMap.class));
} else {
@@ -130,6 +142,21 @@ final class DocumentAdapters {
highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
}
public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {
Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create();
document.setIndex(completionSuggestOption.index());
if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id());
}
float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[] {}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
}
@Nullable
private static Explanation from(@Nullable co.elastic.clients.elasticsearch.core.explain.Explanation explanation) {
@@ -0,0 +1,37 @@
/*
* Copyright 2022 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 org.springframework.data.elasticsearch.core.AggregationContainer;
/**
* {@link AggregationContainer} for a {@link Aggregation} that holds Elasticsearch data.
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
private final Aggregation aggregation;
public ElasticsearchAggregation(Aggregation aggregation) {
this.aggregation = aggregation;
}
@Override
public Aggregation aggregation() {
return aggregation;
}
}
@@ -17,26 +17,62 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* AggregationsContainer implementation for the Elasticsearch aggregations.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4
*/
public class ElasticsearchAggregations implements AggregationsContainer<Map<String, Aggregate>> {
public class ElasticsearchAggregations implements AggregationsContainer<List<ElasticsearchAggregation>> {
private final Map<String, Aggregate> aggregations;
private final List<ElasticsearchAggregation> aggregations;
private final Map<String, ElasticsearchAggregation> aggregationsAsMap;
public ElasticsearchAggregations(Map<String, Aggregate> aggregations) {
this.aggregations = aggregations;
Assert.notNull(aggregations, "aggregations must not be null");
aggregationsAsMap = new HashMap<>();
aggregations.forEach((name, aggregate) -> aggregationsAsMap //
.put(name, new ElasticsearchAggregation(new Aggregation(name, aggregate))));
this.aggregations = new ArrayList<>(aggregationsAsMap.values());
}
@Override
public Map<String, Aggregate> aggregations() {
public List<ElasticsearchAggregation> aggregations() {
return aggregations;
}
/**
* @return the {@link ElasticsearchAggregation}s keyed by aggregation name.
*/
public Map<String, ElasticsearchAggregation> aggregationsAsMap() {
return aggregationsAsMap;
}
/**
* Returns the aggregation that is associated with the specified name.
*
* @param name the name of the aggregation
* @return the aggregation or {@literal null} if not found
*/
@Nullable
public ElasticsearchAggregation get(String name) {
Assert.notNull(name, "name must not be null");
return aggregationsAsMap.get(name);
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2022 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 org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
* @author Peter-Josef Meisch
* @since 5.0
*/
public class ElasticsearchClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ElasticsearchClientFactoryBean.class);
setConfigurations(element, builder);
return getSourcedBeanDefinition(builder, element, parserContext);
}
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
builder.addPropertyValue("hosts", element.getAttribute("hosts"));
}
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
ParserContext context) {
AbstractBeanDefinition definition = builder.getBeanDefinition();
definition.setSource(context.extractSource(source));
return definition;
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2022 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* ElasticsearchClientFactoryBean
*
* @author Peter-Josef Meisch
* @since 5.0
*/
public class ElasticsearchClientFactoryBean
implements FactoryBean<ElasticsearchClient>, InitializingBean, DisposableBean {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchClientFactoryBean.class);
private @Nullable AutoCloseableElasticsearchClient client;
private String hosts = "http://localhost:9200";
static final String COMMA = ",";
@Override
public void destroy() {
try {
LOGGER.info("Closing elasticSearch client");
if (client != null) {
client.close();
}
} catch (final Exception e) {
LOGGER.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public void afterPropertiesSet() throws Exception {
buildClient();
}
@Override
public ElasticsearchClient getObject() {
if (client == null) {
throw new FactoryBeanNotInitializedException();
}
return client;
}
@Override
public Class<?> getObjectType() {
return ElasticsearchClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
protected void buildClient() throws Exception {
Assert.hasText(hosts, "[Assertion Failed] At least one host must be set.");
var clientConfiguration = ClientConfiguration.builder().connectedTo(hosts).build();
client = (AutoCloseableElasticsearchClient) ElasticsearchClients.createImperative(clientConfiguration);
}
public void setHosts(String hosts) {
this.hosts = hosts;
}
public String getHosts() {
return this.hosts;
}
}
@@ -19,6 +19,7 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.Version;
import co.elastic.clients.transport.rest_client.RestClientOptions;
import co.elastic.clients.transport.rest_client.RestClientTransport;
@@ -41,19 +42,19 @@ import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.message.BasicHeader;
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.RestClientBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Utility class to create the different Elasticsearch clients
@@ -226,14 +227,20 @@ public final class ElasticsearchClients {
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof ElasticsearchClientConfigurationCallback) {
ElasticsearchClientConfigurationCallback restClientConfigurationCallback = (ElasticsearchClientConfigurationCallback) clientConfigurer;
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;
}
@@ -242,6 +249,18 @@ public final class ElasticsearchClients {
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
: new RestClientOptions(RequestOptions.DEFAULT).toBuilder();
// need to add the compatibility header, this is only done automatically when not passing in custom options.
// code copied from RestClientTransport as it is not available outside the package
ContentType jsonContentType = null;
if (Version.VERSION == null) {
jsonContentType = ContentType.APPLICATION_JSON;
} else {
jsonContentType = ContentType.create("application/vnd.elasticsearch+json",
new BasicNameValuePair("compatible-with", String.valueOf(Version.VERSION.major())));
}
transportOptionsBuilder.addHeader("Accept", jsonContentType.toString());
TransportOptions transportOptionsWithHeader = transportOptionsBuilder
.addHeader(X_SPRING_DATA_ELASTICSEARCH_CLIENT, clientType).build();
@@ -285,9 +304,9 @@ public final class ElasticsearchClients {
+ ((header.getName().equals("Authorization")) ? ": *****" : ": " + header.getValue()))
.collect(Collectors.joining(", ", "[", "]"));
if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) {
if (request instanceof HttpEntityEnclosingRequest entityRequest
&& ((HttpEntityEnclosingRequest) request).getEntity() != null) {
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
entity.writeTo(buffer);
@@ -316,7 +335,7 @@ public final class ElasticsearchClients {
// no way of logging the body, in this callback, it is not read yset, later there is no callback possibility in
// RestClient or RestClientTransport
ClientLogger.logRawResponse(logId, HttpStatus.resolve(response.getStatusLine().getStatusCode()), headers);
ClientLogger.logRawResponse(logId, response.getStatusLine().getStatusCode(), headers);
}
}
@@ -337,7 +356,7 @@ public final class ElasticsearchClients {
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && httpHeaders != HttpHeaders.EMPTY) {
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
@@ -345,37 +364,37 @@ public final class ElasticsearchClients {
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the RestClient with a {@link HttpAsyncClientBuilder}
* the Elasticsearch RestClient's Http client with a {@link HttpAsyncClientBuilder}
*
* @since 4.4
*/
public interface ElasticsearchClientConfigurationCallback
public interface ElasticsearchHttpClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
static ElasticsearchClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> clientBuilderCallback) {
static ElasticsearchHttpClientConfigurationCallback from(
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> httpClientBuilderCallback) {
Assert.notNull(clientBuilderCallback, "clientBuilderCallback must not be null");
Assert.notNull(httpClientBuilderCallback, "httpClientBuilderCallback must not be null");
// noinspection NullableProblems
return clientBuilderCallback::apply;
return httpClientBuilderCallback::apply;
}
}
/**
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
* the ReactiveElasticsearchClient with a {@link WebClient}
* the RestClient client with a {@link RestClientBuilder}
*
* @since 4.4
* @since 5.0
*/
public interface WebClientConfigurationCallback extends ClientConfiguration.ClientConfigurationCallback<WebClient> {
public interface ElasticsearchRestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<RestClientBuilder> {
static WebClientConfigurationCallback from(Function<WebClient, WebClient> webClientCallback) {
static ElasticsearchRestClientConfigurationCallback from(
Function<RestClientBuilder, RestClientBuilder> restClientBuilderCallback) {
Assert.notNull(webClientCallback, "webClientCallback must not be null");
Assert.notNull(restClientBuilderCallback, "restClientBuilderCallback must not be null");
// noinspection NullableProblems
return webClientCallback::apply;
return restClientBuilderCallback::apply;
}
}
}
@@ -42,7 +42,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
*
* @return configuration, must not be {@literal null}
*/
@Bean
@Bean(name="elasticsearchClientConfiguration")
public abstract ClientConfiguration clientConfiguration();
/**
@@ -52,7 +52,7 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
* @return RestClient
*/
@Bean
public RestClient restClient(ClientConfiguration clientConfiguration) {
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
@@ -20,12 +20,16 @@ import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpMapper;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.elasticsearch.client.ResponseException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
/**
@@ -67,14 +71,27 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
}
// todo #1973 index unavailable?
if (ex instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) ex;
if (ex instanceof ElasticsearchException elasticsearchException) {
ErrorResponse response = elasticsearchException.response();
if (response.status() == 404 && "index_not_found_exception".equals(response.error().type())) {
// noinspection RegExpRedundantEscape
Pattern pattern = Pattern.compile(".*no such index \\[(.*)\\]");
String index = "";
Matcher matcher = pattern.matcher(response.error().reason());
if (matcher.matches()) {
index = matcher.group(1);
}
return new NoSuchIndexException(index);
}
String body = JsonUtils.toJson(response, jsonpMapper);
if (response.error().type().contains("validation_exception")) {
return new DataIntegrityViolationException(response.error().reason());
}
return new UncategorizedElasticsearchException(ex.getMessage(), response.status(), body, ex);
}
@@ -86,20 +103,19 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return null;
}
private boolean isSeqNoConflict(Exception exception) {
// todo #1973 check if this works
private boolean isSeqNoConflict(Throwable exception) {
Integer status = null;
String message = null;
if (exception instanceof RestStatusException) {
RestStatusException statusException = (RestStatusException) exception;
status = statusException.getStatus();
message = statusException.getMessage();
if (exception instanceof ResponseException responseException) {
status = responseException.getResponse().getStatusLine().getStatusCode();
message = responseException.getMessage();
} else if (exception.getCause() != null) {
return isSeqNoConflict(exception.getCause());
}
if (status != null && message != null) {
return status == 409 && message.contains("type=version_conflict_engine_exception")
return status == 409 && message.contains("type\":\"version_conflict_engine_exception")
&& message.contains("version conflict, required seqNo");
}
@@ -15,14 +15,19 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -30,6 +35,8 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
@@ -40,6 +47,7 @@ import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
@@ -63,6 +71,8 @@ import org.springframework.util.Assert;
*/
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
private final ElasticsearchClient client;
private final RequestConverter requestConverter;
private final ResponseConverter responseConverter;
@@ -70,6 +80,17 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private final ElasticsearchExceptionTranslator exceptionTranslator;
// region _initialization
public ElasticsearchTemplate(ElasticsearchClient client) {
Assert.notNull(client, "client must not be null");
this.client = client;
this.jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper);
exceptionTranslator = new ElasticsearchExceptionTranslator(jsonpMapper);
}
public ElasticsearchTemplate(ElasticsearchClient client, ElasticsearchConverter elasticsearchConverter) {
super(elasticsearchConverter);
@@ -137,7 +158,12 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(queries, "queries must not be null");
Assert.notNull(bulkOptions, "bulkOptions must not be null");
Assert.notNull(index, "index must not be null");
doBulkOperation(queries, bulkOptions, index);
}
@Override
@@ -155,12 +181,25 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@Override
public UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
co.elastic.clients.elasticsearch.core.UpdateResponse<Document> response = execute(
client -> client.update(request, Document.class));
return UpdateResponse.of(result(response.result()));
}
@Override
public ByQueryResponse updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(updateQuery, "updateQuery must not be null");
Assert.notNull(index, "index must not be null");
UpdateByQueryRequest request = requestConverter.documentUpdateByQueryRequest(updateQuery, index,
getRefreshPolicy());
UpdateByQueryResponse byQueryResponse = execute(client -> client.updateByQuery(request));
return responseConverter.byQueryResponse(byQueryResponse);
}
@Override
@@ -226,7 +265,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
client -> client.reindex(reindexRequestES));
if (reindexResponse.task() == null) {
// todo #1973 check behaviour and create issue in ES if necessary
throw new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request");
}
@@ -247,18 +285,17 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// endregion
@Override
protected String getClusterVersion() {
public String getClusterVersion() {
return execute(client -> client.info().version().number());
}
@Override
protected String getVendor() {
public String getVendor() {
return "Elasticsearch";
}
@Override
protected String getRuntimeLibraryVersion() {
public String getRuntimeLibraryVersion() {
return Version.VERSION.toString();
}
@@ -306,7 +343,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected <T> SearchScrollHits<T> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
public <T> SearchScrollHits<T> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
@@ -319,7 +356,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected <T> SearchScrollHits<T> searchScrollContinue(String scrollId, long scrollTimeInMillis, Class<T> clazz,
public <T> SearchScrollHits<T> searchScrollContinue(String scrollId, long scrollTimeInMillis, Class<T> clazz,
IndexCoordinates index) {
Assert.notNull(scrollId, "scrollId must not be null");
@@ -331,8 +368,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return getSearchScrollHits(clazz, index, response);
}
private <T, R extends SearchResponse<EntityAsMap>> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz,
IndexCoordinates index, R response) {
private <T> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz, IndexCoordinates index,
ResponseBody<EntityAsMap> response) {
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
@@ -342,7 +379,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected void searchScrollClear(List<String> scrollIds) {
public void searchScrollClear(List<String> scrollIds) {
Assert.notNull(scrollIds, "scrollIds must not be null");
@@ -404,24 +441,76 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return doMultiSearch(multiSearchQueryParameters);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<SearchHits<?>> doMultiSearch(List<MultiSearchQueryParameter> multiSearchQueryParameters) {
throw new UnsupportedOperationException("not implemented");
MsearchRequest request = requestConverter.searchMsearchRequest(multiSearchQueryParameters);
MsearchResponse<EntityAsMap> msearchResponse = execute(client -> client.msearch(request, EntityAsMap.class));
List<MultiSearchResponseItem<EntityAsMap>> responseItems = msearchResponse.responses();
Assert.isTrue(multiSearchQueryParameters.size() == responseItems.size(),
"number of response items does not match number of requests");
List<SearchHits<?>> searchHitsList = new ArrayList<>(multiSearchQueryParameters.size());
Iterator<MultiSearchQueryParameter> queryIterator = multiSearchQueryParameters.iterator();
Iterator<MultiSearchResponseItem<EntityAsMap>> responseIterator = responseItems.iterator();
while (queryIterator.hasNext()) {
MultiSearchQueryParameter queryParameter = queryIterator.next();
MultiSearchResponseItem<EntityAsMap> responseItem = responseIterator.next();
if (responseItem.isResult()) {
Class clazz = queryParameter.clazz;
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz,
queryParameter.index);
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(clazz,
queryParameter.index);
SearchHits<?> searchHits = callback.doWith(
SearchDocumentResponseBuilder.from(responseItem.result(), getEntityCreator(documentCallback), jsonpMapper));
searchHitsList.add(searchHits);
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER
.warn(String.format("multisearch responsecontains failure: {}", responseItem.failure().error().reason()));
}
}
}
return searchHitsList;
}
/**
* value class combining the information needed for a single query in a multisearch request.
*/
private static class MultiSearchQueryParameter {
final Query query;
final Class<?> clazz;
final IndexCoordinates index;
public MultiSearchQueryParameter(Query query, Class<?> clazz, IndexCoordinates index) {
this.query = query;
this.clazz = clazz;
this.index = index;
}
record MultiSearchQueryParameter(Query query, Class<?> clazz, IndexCoordinates index) {
}
@Override
public String openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable) {
Assert.notNull(index, "index must not be null");
Assert.notNull(keepAlive, "keepAlive must not be null");
Assert.notNull(ignoreUnavailable, "ignoreUnavailable must not be null");
var request = requestConverter.searchOpenPointInTimeRequest(index, keepAlive, ignoreUnavailable);
return execute(client -> client.openPointInTime(request)).id();
}
@Override
public Boolean closePointInTime(String pit) {
Assert.notNull(pit, "pit must not be null");
ClosePointInTimeRequest request = requestConverter.searchClosePointInTime(pit);
var response = execute(client -> client.closePointInTime(request));
return response.succeeded();
}
// endregion
// region client callback
@@ -87,10 +87,6 @@ class HighlightQueryBuilder {
builder.boundaryScannerLocale(parameters.getBoundaryScannerLocale());
}
if (parameters.getForceSource()) { // default is false
// todo #1973 parameter missing in new client
}
if (StringUtils.hasLength(parameters.getFragmenter())) {
builder.fragmenter(highlighterFragmenter(parameters.getFragmenter()));
}
@@ -111,10 +107,6 @@ class HighlightQueryBuilder {
builder.order(highlighterOrder(parameters.getOrder()));
}
if (parameters.getPhraseLimit() > -1) {
// todo #1973 parameter missing in new client
}
if (parameters.getPreTags().length > 0) {
builder.preTags(Arrays.asList(parameters.getPreTags()));
}
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.util.StringUtils.*;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import java.util.List;
@@ -56,7 +57,8 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 4.4
*/
public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> implements IndexOperations {
public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, ElasticsearchIndicesClient>
implements IndexOperations {
private static final Logger LOGGER = LoggerFactory.getLogger(IndicesTemplate.class);
@@ -187,7 +189,6 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> i
Assert.notNull(clazz, "clazz must not be null");
// load mapping specified in Mapping annotation if present
// noinspection DuplicatedCode
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
if (mappingAnnotation != null) {
@@ -277,12 +278,22 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchIndicesClient> i
@Override
public Map<String, Set<AliasData>> getAliases(String... aliasNames) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(aliasNames, "aliasNames must not be null");
GetAliasRequest getAliasRequest = requestConverter.indicesGetAliasRequest(aliasNames, null);
var getAliasResponse = execute(client -> client.getAlias(getAliasRequest));
return responseConverter.indicesGetAliasData(getAliasResponse);
}
@Override
public Map<String, Set<AliasData>> getAliasesForIndex(String... indexNames) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(indexNames, "indexNames must not be null");
GetAliasRequest getAliasRequest = requestConverter.indicesGetAliasRequest(null, indexNames);
var getAliasResponse = execute(client -> client.getAlias(getAliasRequest));
return responseConverter.indicesGetAliasData(getAliasResponse);
}
@Override
@@ -20,9 +20,11 @@ import jakarta.json.stream.JsonGenerator;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
/**
* @author Peter-Josef Meisch
@@ -41,13 +43,28 @@ final class JsonUtils {
JsonGenerator generator = mapper.jsonProvider().createGenerator(baos);
mapper.serialize(object, generator);
generator.close();
String jsonMapping = "{}";
String json = "{}";
try {
jsonMapping = baos.toString("UTF-8");
json = baos.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
LOGGER.warn("could not read json", e);
}
return jsonMapping;
return json;
}
@Nullable
public static String queryToJson(@Nullable co.elastic.clients.elasticsearch._types.query_dsl.Query query, JsonpMapper mapper) {
if (query == null) {
return null;
}
var baos = new ByteArrayOutputStream();
var generator = mapper.jsonProvider().createGenerator(baos);
query.serialize(generator, mapper);
generator.close();
return baos.toString(StandardCharsets.UTF_8);
}
}
@@ -15,34 +15,49 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A {@link org.springframework.data.elasticsearch.core.query.Query} implementation using query builders from the new
* Elasticsearch Client library.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4
*/
public class NativeQuery extends BaseQuery {
@Nullable private final Query query;
@Nullable private Query filter;
// note: the new client does not have pipeline aggs, these are just set up as normal aggs
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse;
private List<ScriptedField> scriptedFields = Collections.emptyList();
private List<SortOptions> sortOptions = Collections.emptyList();
public NativeQuery(NativeQueryBuilder builder) {
super(builder);
this.query = builder.getQuery();
this.filter = builder.getFilter();
this.aggregations.putAll(builder.getAggregations());
this.suggester = builder.getSuggester();
this.fieldCollapse = builder.getFieldCollapse();
this.scriptedFields = builder.getScriptedFields();
this.sortOptions = builder.getSortOptions();
}
public NativeQuery(@Nullable Query query) {
@@ -58,20 +73,9 @@ public class NativeQuery extends BaseQuery {
return query;
}
public void addAggregation(String name, Aggregation aggregation) {
Assert.notNull(name, "name must not be null");
Assert.notNull(aggregation, "aggregation must not be null");
aggregations.put(name, aggregation);
}
public void setAggregations(Map<String, Aggregation> aggregations) {
Assert.notNull(aggregations, "aggregations must not be null");
this.aggregations.clear();
this.aggregations.putAll(aggregations);
@Nullable
public Query getFilter() {
return filter;
}
public Map<String, Aggregation> getAggregations() {
@@ -83,8 +87,16 @@ public class NativeQuery extends BaseQuery {
return suggester;
}
public void setSuggester(@Nullable Suggester suggester) {
this.suggester = suggester;
@Nullable
public FieldCollapse getFieldCollapse() {
return fieldCollapse;
}
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
public List<SortOptions> getSortOptions() {
return sortOptions;
}
}
@@ -15,28 +15,40 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.util.ObjectBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4
*/
public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> {
@Nullable private Query query;
@Nullable private Query filter;
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse;
private final List<ScriptedField> scriptedFields = new ArrayList<>();
private List<SortOptions> sortOptions = new ArrayList<>();
public NativeQueryBuilder() {
}
@@ -46,6 +58,33 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return query;
}
@Nullable
public Query getFilter() {
return this.filter;
}
public Map<String, Aggregation> getAggregations() {
return aggregations;
}
@Nullable
public Suggester getSuggester() {
return suggester;
}
@Nullable
public FieldCollapse getFieldCollapse() {
return fieldCollapse;
}
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
public List<SortOptions> getSortOptions() {
return sortOptions;
}
public NativeQueryBuilder withQuery(Query query) {
Assert.notNull(query, "query must not be null");
@@ -61,6 +100,18 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return withQuery(fn.apply(new Query.Builder()).build());
}
public NativeQueryBuilder withFilter(@Nullable Query filter) {
this.filter = filter;
return this;
}
public NativeQueryBuilder withFilter(Function<Query.Builder, ObjectBuilder<Query>> fn) {
Assert.notNull(fn, "fn must not be null");
return withFilter(fn.apply(new Query.Builder()).build());
}
public NativeQueryBuilder withAggregation(String name, Aggregation aggregation) {
Assert.notNull(name, "name must not be null");
@@ -75,11 +126,48 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return this;
}
public NativeQuery build() {
NativeQuery nativeQuery = new NativeQuery(this);
nativeQuery.setAggregations(aggregations);
nativeQuery.setSuggester(suggester);
public NativeQueryBuilder withFieldCollapse(@Nullable FieldCollapse fieldCollapse) {
this.fieldCollapse = fieldCollapse;
return this;
}
return nativeQuery;
public NativeQueryBuilder withScriptedField(ScriptedField scriptedField) {
Assert.notNull(scriptedField, "scriptedField must not be null");
this.scriptedFields.add(scriptedField);
return this;
}
public NativeQueryBuilder withSort(List<SortOptions> values) {
Assert.notEmpty(values, "values must not be empty");
sortOptions.clear();
sortOptions.addAll(values);
return this;
}
public NativeQueryBuilder withSort(SortOptions value, SortOptions... values) {
Assert.notNull(value, "value must not be null");
sortOptions.add(value);
if (values.length > 0) {
sortOptions.addAll(Arrays.asList(values));
}
return this;
}
public NativeQueryBuilder withSort(Function<SortOptions.Builder, ObjectBuilder<SortOptions>> fn) {
Assert.notNull(fn, "fn must not be null");
withSort(fn.apply(new SortOptions.Builder()).build());
return this;
}
public NativeQuery build() {
return new NativeQuery(this);
}
}
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.LatLonGeoLocation;
import co.elastic.clients.elasticsearch._types.query_dsl.IdsQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
@@ -29,6 +30,7 @@ import co.elastic.clients.util.ObjectBuilder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.function.Function;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
@@ -45,6 +47,21 @@ public final class QueryBuilders {
private QueryBuilders() {}
public static IdsQuery idsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
return IdsQuery.of(i -> i.values(ids));
}
public static Query idsQueryAsQuery(List<String> ids) {
Assert.notNull(ids, "ids must not be null");
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.ids(idsQuery(ids));
return builder.apply(new Query.Builder()).build();
}
public static MatchQuery matchQuery(String fieldName, String query, @Nullable Operator operator,
@Nullable Float boost) {
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Transport;
import reactor.core.publisher.Flux;
import org.reactivestreams.Publisher;
@@ -29,18 +30,17 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ReactiveChildTemplate<CLIENT extends ApiClient> {
public class ReactiveChildTemplate<T extends Transport, CLIENT extends ApiClient<T, CLIENT>> {
protected final CLIENT client;
protected final ElasticsearchConverter elasticsearchConverter;
protected final RequestConverter requestConverter;
protected final ResponseConverter responseConverter;
private final JsonpMapper jsonpMapper;
protected final ElasticsearchExceptionTranslator exceptionTranslator;
public ReactiveChildTemplate(CLIENT client, ElasticsearchConverter elasticsearchConverter) {
this.client = client;
this.elasticsearchConverter = elasticsearchConverter;
jsonpMapper = client._transport().jsonpMapper();
JsonpMapper jsonpMapper = client._transport().jsonpMapper();
requestConverter = new RequestConverter(elasticsearchConverter, jsonpMapper);
responseConverter = new ResponseConverter(jsonpMapper);
exceptionTranslator = new ElasticsearchExceptionTranslator(jsonpMapper);
@@ -17,17 +17,19 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.transport.ElasticsearchTransport;
import reactor.core.publisher.Mono;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
/**
* @author Peter-Josef Meisch
* @since 4.4
*/
public class ReactiveClusterTemplate extends ReactiveChildTemplate<ReactiveElasticsearchClusterClient>
public class ReactiveClusterTemplate
extends ReactiveChildTemplate<ElasticsearchTransport, ReactiveElasticsearchClusterClient>
implements ReactiveClusterOperations {
public ReactiveClusterTemplate(ReactiveElasticsearchClusterClient client,
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.ApiClient;
import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.JsonEndpoint;
import co.elastic.clients.transport.TransportOptions;
@@ -127,12 +128,51 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
}
public <T, P> Mono<UpdateResponse<T>> update(UpdateRequest<T, P> request, Class<T> clazz) {
Assert.notNull(request, "request must not be null");
// noinspection unchecked
JsonEndpoint<UpdateRequest<?, ?>, UpdateResponse<T>, ErrorResponse> endpoint = new EndpointWithResponseMapperAttr(
UpdateRequest._ENDPOINT, "co.elastic.clients:Deserializer:_global.update.TDocument",
this.getDeserializer(clazz));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, this.transportOptions));
}
public <T, P> Mono<UpdateResponse<T>> update(
Function<UpdateRequest.Builder<T, P>, ObjectBuilder<UpdateRequest<T, P>>> fn, Class<T> clazz) {
Assert.notNull(fn, "fn must not be null");
return update(fn.apply(new UpdateRequest.Builder<>()).build(), clazz);
}
public <T> Mono<GetResponse<T>> get(Function<GetRequest.Builder, ObjectBuilder<GetRequest>> fn, Class<T> tClass) {
Assert.notNull(fn, "fn must not be null");
return get(fn.apply(new GetRequest.Builder()).build(), tClass);
}
public <T> Mono<MgetResponse<T>> mget(MgetRequest request, Class<T> clazz) {
Assert.notNull(request, "request must not be null");
Assert.notNull(clazz, "clazz must not be null");
// noinspection unchecked
JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse>) MgetRequest._ENDPOINT;
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.mget.TDocument",
this.getDeserializer(clazz));
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
}
public <T> Mono<MgetResponse<T>> mget(Function<MgetRequest.Builder, ObjectBuilder<MgetRequest>> fn, Class<T> clazz) {
Assert.notNull(fn, "fn must not be null");
return mget(fn.apply(new MgetRequest.Builder()).build(), clazz);
}
public Mono<ReindexResponse> reindex(ReindexRequest request) {
Assert.notNull(request, "request must not be null");
@@ -161,10 +201,25 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
return delete(fn.apply(new DeleteRequest.Builder()).build());
}
public Mono<DeleteByQueryResponse> deleteByQuery(DeleteByQueryRequest request) {
Assert.notNull(request, "request must not be null");
return Mono.fromFuture(transport.performRequestAsync(request, DeleteByQueryRequest._ENDPOINT, transportOptions));
}
public Mono<DeleteByQueryResponse> deleteByQuery(
Function<DeleteByQueryRequest.Builder, ObjectBuilder<DeleteByQueryRequest>> fn) {
Assert.notNull(fn, "fn must not be null");
return deleteByQuery(fn.apply(new DeleteByQueryRequest.Builder()).build());
}
// endregion
// region search
public <T> Mono<SearchResponse<T>> search(SearchRequest request, Class<T> tDocumentClass) {
public <T> Mono<ResponseBody<T>> search(SearchRequest request, Class<T> tDocumentClass) {
Assert.notNull(request, "request must not be null");
Assert.notNull(tDocumentClass, "tDocumentClass must not be null");
@@ -173,7 +228,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
SearchRequest.createSearchEndpoint(this.getDeserializer(tDocumentClass)), transportOptions));
}
public <T> Mono<SearchResponse<T>> search(Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> fn,
public <T> Mono<ResponseBody<T>> search(Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> fn,
Class<T> tDocumentClass) {
Assert.notNull(fn, "fn must not be null");
@@ -221,6 +276,48 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
return clearScroll(fn.apply(new ClearScrollRequest.Builder()).build());
}
/**
* @since 5.0
*/
public Mono<OpenPointInTimeResponse> openPointInTime(OpenPointInTimeRequest request) {
Assert.notNull(request, "request must not be null");
return Mono.fromFuture(transport.performRequestAsync(request, OpenPointInTimeRequest._ENDPOINT, transportOptions));
}
/**
* @since 5.0
*/
public Mono<OpenPointInTimeResponse> openPointInTime(
Function<OpenPointInTimeRequest.Builder, ObjectBuilder<OpenPointInTimeRequest>> fn) {
Assert.notNull(fn, "fn must not be null");
return openPointInTime(fn.apply(new OpenPointInTimeRequest.Builder()).build());
}
/**
* @since 5.0
*/
public Mono<ClosePointInTimeResponse> closePointInTime(ClosePointInTimeRequest request) {
Assert.notNull(request, "request must not be null");
return Mono.fromFuture(transport.performRequestAsync(request, ClosePointInTimeRequest._ENDPOINT, transportOptions));
}
/**
* @since 5.0
*/
public Mono<ClosePointInTimeResponse> closePointInTime(
Function<ClosePointInTimeRequest.Builder, ObjectBuilder<ClosePointInTimeRequest>> fn) {
Assert.notNull(fn, "fn must not be null");
return closePointInTime(fn.apply(new ClosePointInTimeRequest.Builder()).build());
}
// endregion
}
@@ -41,7 +41,7 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
*
* @return configuration, must not be {@literal null}
*/
@Bean
@Bean(name="elasticsearchClientConfiguration")
public abstract ClientConfiguration clientConfiguration();
/**
@@ -51,7 +51,7 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
* @return RestClient
*/
@Bean
public RestClient restClient(ClientConfiguration clientConfiguration) {
public RestClient elasticsearchRestClient(ClientConfiguration clientConfiguration) {
Assert.notNull(clientConfiguration, "clientConfiguration must not be null");
@@ -212,14 +212,6 @@ public class ReactiveElasticsearchIndicesClient
return existsTemplate(fn.apply(new ExistsTemplateRequest.Builder()).build());
}
public Mono<BooleanResponse> existsType(ExistsTypeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, ExistsTypeRequest._ENDPOINT, transportOptions));
}
public Mono<BooleanResponse> existsType(Function<ExistsTypeRequest.Builder, ObjectBuilder<ExistsTypeRequest>> fn) {
return existsType(fn.apply(new ExistsTypeRequest.Builder()).build());
}
public Mono<FlushResponse> flush(FlushRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, FlushRequest._ENDPOINT, transportOptions));
}
@@ -232,19 +224,6 @@ public class ReactiveElasticsearchIndicesClient
return flush(builder -> builder);
}
public Mono<FlushSyncedResponse> flushSynced(FlushSyncedRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, FlushSyncedRequest._ENDPOINT, transportOptions));
}
public Mono<FlushSyncedResponse> flushSynced(
Function<FlushSyncedRequest.Builder, ObjectBuilder<FlushSyncedRequest>> fn) {
return flushSynced(fn.apply(new FlushSyncedRequest.Builder()).build());
}
public Mono<FlushSyncedResponse> flushSynced() {
return flushSynced(builder -> builder);
}
@SuppressWarnings("SpellCheckingInspection")
public Mono<ForcemergeResponse> forcemerge(ForcemergeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, ForcemergeRequest._ENDPOINT, transportOptions));
@@ -260,14 +239,6 @@ public class ReactiveElasticsearchIndicesClient
return forcemerge(builder -> builder);
}
public Mono<FreezeResponse> freeze(FreezeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, FreezeRequest._ENDPOINT, transportOptions));
}
public Mono<FreezeResponse> freeze(Function<FreezeRequest.Builder, ObjectBuilder<FreezeRequest>> fn) {
return freeze(fn.apply(new FreezeRequest.Builder()).build());
}
public Mono<GetIndexResponse> get(GetIndexRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, GetIndexRequest._ENDPOINT, transportOptions));
}
@@ -363,18 +334,6 @@ public class ReactiveElasticsearchIndicesClient
return getTemplate(builder -> builder);
}
public Mono<GetUpgradeResponse> getUpgrade(GetUpgradeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, GetUpgradeRequest._ENDPOINT, transportOptions));
}
public Mono<GetUpgradeResponse> getUpgrade(Function<GetUpgradeRequest.Builder, ObjectBuilder<GetUpgradeRequest>> fn) {
return getUpgrade(fn.apply(new GetUpgradeRequest.Builder()).build());
}
public Mono<GetUpgradeResponse> getUpgrade() {
return getUpgrade(builder -> builder);
}
public Mono<MigrateToDataStreamResponse> migrateToDataStream(MigrateToDataStreamRequest request) {
return Mono
.fromFuture(transport.performRequestAsync(request, MigrateToDataStreamRequest._ENDPOINT, transportOptions));
@@ -601,18 +560,6 @@ public class ReactiveElasticsearchIndicesClient
return updateAliases(builder -> builder);
}
public Mono<UpgradeResponse> upgrade(UpgradeRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, UpgradeRequest._ENDPOINT, transportOptions));
}
public Mono<UpgradeResponse> upgrade(Function<UpgradeRequest.Builder, ObjectBuilder<UpgradeRequest>> fn) {
return upgrade(fn.apply(new UpgradeRequest.Builder()).build());
}
public Mono<UpgradeResponse> upgrade() {
return upgrade(builder -> builder);
}
public Mono<ValidateQueryResponse> validateQuery(ValidateQueryRequest request) {
return Mono.fromFuture(transport.performRequestAsync(request, ValidateQueryRequest._ENDPOINT, transportOptions));
}
@@ -15,28 +15,33 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import static co.elastic.clients.util.ApiTypeHelper.*;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import co.elastic.clients.elasticsearch._types.Result;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.get.GetResult;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import java.time.Duration;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.client.util.ScrollState;
import org.springframework.data.elasticsearch.core.AbstractReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.AggregationContainer;
@@ -44,8 +49,8 @@ import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@@ -75,7 +80,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private final JsonpMapper jsonpMapper;
private final ElasticsearchExceptionTranslator exceptionTranslator;
protected ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client, ElasticsearchConverter converter) {
public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client, ElasticsearchConverter converter) {
super(converter);
Assert.notNull(client, "client must not be null");
@@ -103,6 +108,34 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
)));
}
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
return entitiesPublisher //
.flatMapMany(entities -> Flux.fromIterable(entities) //
.concatMap(entity -> maybeCallBeforeConvert(entity, index)) //
).collectList() //
.map(Entities::new) //
.flatMapMany(entities -> {
if (entities.isEmpty()) {
return Flux.empty();
}
return doBulkOperation(entities.indexQueries(), BulkOptions.defaultOptions(), index)//
.index() //
.flatMap(indexAndResponse -> {
T savedEntity = entities.entityAt(indexAndResponse.getT1());
BulkResponseItem response = indexAndResponse.getT2();
updateIndexedObject(savedEntity, IndexedObjectInformation.of(response.id(), response.seqNo(),
response.primaryTerm(), response.version()));
return maybeCallAfterSave(savedEntity, index);
});
});
}
@Override
public <T> Mono<T> get(String id, Class<T> entityType, IndexCoordinates index) {
@@ -145,7 +178,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.ReindexResponse>>) client -> client
.reindex(reindexRequestES)))
.flatMap(response -> (response.task() == null)
? Mono.error( // todo #1973 check behaviour and create issue in ES if necessary
? Mono.error(
new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request"))
: Mono.just(response.task()));
}
@@ -170,7 +203,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
}
private <R> Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
private Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
if (bulkResponse.errors()) {
Map<String, String> failedDocuments = new HashMap<>();
@@ -214,6 +247,31 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
}).onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
}
@Override
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
Assert.notNull(clazz, "clazz must not be null");
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
Publisher<MgetResponse<EntityAsMap>> response = execute(
(ClientCallback<Publisher<MgetResponse<EntityAsMap>>>) client -> client.mget(request, EntityAsMap.class));
return Mono.from(response)//
.flatMapMany(it -> Flux.fromIterable(DocumentAdapters.from(it))) //
.flatMap(multiGetItem -> {
if (multiGetItem.isFailed()) {
return Mono.just(MultiGetItem.of(null, multiGetItem.getFailure()));
} else {
return callback.toEntity(multiGetItem.getItem()) //
.map(t -> MultiGetItem.of(t, multiGetItem.getFailure()));
}
});
}
// endregion
@Override
@@ -223,8 +281,30 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
@Override
protected Mono<Boolean> doExists(String id, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(id, "id must not be null");
Assert.notNull(index, "index must not be null");
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, true);
return Mono.from(execute(
((ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class))))
.map(GetResult::found) //
.onErrorReturn(NoSuchIndexException.class, false);
}
@Override
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, entityType, index,
getRefreshPolicy());
return Mono
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
.map(responseConverter::byQueryResponse);
}
// region search operations
@Override
@@ -247,9 +327,9 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Time scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll() : Time.of(t -> t.time("1m"));
Flux<SearchResponse<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
Flux<ResponseBody<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
state -> Mono
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client1 -> client1
.search(searchRequest, EntityAsMap.class))) //
.expand(entityAsMapSearchResponse -> {
@@ -260,8 +340,11 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return Mono.empty();
}
return Mono.from(execute((ClientCallback<Publisher<ScrollResponse<EntityAsMap>>>) client -> client.scroll(
ScrollRequest.of(sr -> sr.scrollId(state.getScrollId()).scroll(scrollTimeout)), EntityAsMap.class)));
return Mono.from(execute((ClientCallback<Publisher<ScrollResponse<EntityAsMap>>>) client1 -> {
ScrollRequest scrollRequest = ScrollRequest
.of(sr -> sr.scrollId(state.getScrollId()).scroll(scrollTimeout));
return client1.scroll(scrollRequest, EntityAsMap.class);
}));
}),
this::cleanupScroll, (state, ex) -> cleanupScroll(state), this::cleanupScroll);
@@ -284,7 +367,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest searchRequest = requestConverter.searchRequest(query, entityType, index, true, false);
return Mono
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class)))
.map(searchResponse -> searchResponse.hits().total() != null ? searchResponse.hits().total().value() : 0L);
}
@@ -292,7 +375,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private Flux<SearchDocument> doFind(SearchRequest searchRequest) {
return Mono
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class))) //
.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits()) //
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
@@ -307,74 +390,86 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
// noinspection unchecked
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<T>((Class<T>) clazz, index);
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>((Class<T>) clazz, index);
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> callback.toEntity(searchDocument)
.toFuture();
return Mono
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
EntityAsMap.class)))
.map(searchResponse -> SearchDocumentResponseBuilder.from(searchResponse, entityCreator, jsonpMapper));
}
@Override
public Flux<? extends AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
return doFindForResponse(query, entityType, index).flatMapMany(searchDocumentResponse -> {
ElasticsearchAggregations aggregations = (ElasticsearchAggregations) searchDocumentResponse.getAggregations();
return aggregations == null ? Flux.empty() : Flux.fromIterable(aggregations.aggregations());
});
}
@Override
public Mono<String> openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable) {
Assert.notNull(index, "index must not be null");
Assert.notNull(keepAlive, "keepAlive must not be null");
Assert.notNull(ignoreUnavailable, "ignoreUnavailable must not be null");
var request = requestConverter.searchOpenPointInTimeRequest(index, keepAlive, ignoreUnavailable);
return Mono
.from(execute((ClientCallback<Publisher<OpenPointInTimeResponse>>) client -> client.openPointInTime(request)))
.map(OpenPointInTimeResponse::id);
}
@Override
public Mono<Boolean> closePointInTime(String pit) {
Assert.notNull(pit, "pit must not be null");
ClosePointInTimeRequest request = requestConverter.searchClosePointInTime(pit);
return Mono
.from(execute((ClientCallback<Publisher<ClosePointInTimeResponse>>) client -> client.closePointInTime(request)))
.map(ClosePointInTimeResponse::succeeded);
}
// endregion
@Override
protected Mono<String> getVendor() {
public Mono<String> getVendor() {
return Mono.just("Elasticsearch");
}
@Override
protected Mono<String> getRuntimeLibraryVersion() {
return Mono.just(Version.VERSION.toString());
public Mono<String> getRuntimeLibraryVersion() {
return Mono.just(Version.VERSION != null ? Version.VERSION.toString() : "null");
}
@Override
protected Mono<String> getClusterVersion() {
return Mono.from(execute(ReactiveElasticsearchClient::info)).map(infoResponse -> infoResponse.version().number());
}
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
return entitiesPublisher //
.flatMapMany(entities -> Flux.fromIterable(entities) //
.concatMap(entity -> maybeCallBeforeConvert(entity, index)) //
).collectList() //
.map(Entities::new) //
.flatMapMany(entities -> {
if (entities.isEmpty()) {
return Flux.empty();
}
return doBulkOperation(entities.indexQueries(), BulkOptions.defaultOptions(), index)//
.index() //
.flatMap(indexAndResponse -> {
T savedEntity = entities.entityAt(indexAndResponse.getT1());
BulkResponseItem response = indexAndResponse.getT2();
updateIndexedObject(savedEntity, IndexedObjectInformation.of(response.id(), response.seqNo(),
response.primaryTerm(), response.version()));
return maybeCallAfterSave(savedEntity, index);
});
});
}
@Override
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
public Mono<String> getClusterVersion() {
return Mono.from(execute((ReactiveElasticsearchClient reactiveElasticsearchClient) -> {
try (var ignored = DANGEROUS_disableRequiredPropertiesCheck(true)) {
return reactiveElasticsearchClient.info();
}
})).map(infoResponse -> infoResponse.version().number());
}
@Override
public Mono<UpdateResponse> update(UpdateQuery updateQuery, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
Assert.notNull(updateQuery, "UpdateQuery must not be null");
Assert.notNull(index, "Index must not be null");
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
return Mono.from(execute(
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.UpdateResponse<Document>>>) client -> client
.update(request, Document.class)))
.flatMap(response -> {
UpdateResponse.Result result = result(response.result());
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
});
}
@Override
@@ -413,21 +508,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return new ReactiveClusterTemplate(client.cluster(), converter);
}
@Override
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Flux<Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Flux<Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public Query matchAllQuery() {
return NativeQuery.builder().withQuery(QueryBuilders.matchAllQueryAsQuery()).build();
@@ -435,7 +515,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
@Override
public Query idsQuery(List<String> ids) {
throw new UnsupportedOperationException("not implemented");
return NativeQuery.builder().withQuery(QueryBuilders.idsQueryAsQuery(ids)).build();
}
/**
@@ -19,6 +19,7 @@ import static org.springframework.util.StringUtils.*;
import co.elastic.clients.elasticsearch._types.AcknowledgedResponseBase;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -53,7 +54,8 @@ import org.springframework.util.Assert;
/**
* @author Peter-Josef Meisch
*/
public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElasticsearchIndicesClient>
public class ReactiveIndicesTemplate
extends ReactiveChildTemplate<ElasticsearchTransport, ReactiveElasticsearchIndicesClient>
implements ReactiveIndexOperations {
@Nullable private final Class<?> boundClass;
@@ -152,7 +154,7 @@ public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElast
@Override
public Mono<Void> refresh() {
return Mono.from(execute(client1 -> client1.refresh())).then();
return Mono.from(execute(ReactiveElasticsearchIndicesClient::refresh)).then();
}
@Override
@@ -297,7 +299,7 @@ public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElast
co.elastic.clients.elasticsearch.indices.ExistsTemplateRequest existsTemplateRequestES = requestConverter
.indicesExistsTemplateRequest(existsTemplateRequest);
return Mono.from(execute(client1 -> client1.existsTemplate(existsTemplateRequestES))).map(BooleanResponse::value);
return Mono.from(execute(client -> client.existsTemplate(existsTemplateRequestES))).map(BooleanResponse::value);
}
@Override
@@ -307,13 +309,18 @@ public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElast
co.elastic.clients.elasticsearch.indices.DeleteTemplateRequest deleteTemplateRequestES = requestConverter
.indicesDeleteTemplateRequest(deleteTemplateRequest);
return Mono.from(execute(client1 -> client1.deleteTemplate(deleteTemplateRequestES)))
return Mono.from(execute(client -> client.deleteTemplate(deleteTemplateRequestES)))
.map(DeleteTemplateResponse::acknowledged);
}
@Override
public Flux<IndexInformation> getInformation(IndexCoordinates index) {
throw new UnsupportedOperationException("not implemented");
GetIndexRequest request = requestConverter.indicesGetIndexRequest(index);
return Mono.from(execute(client -> client.get(request))) //
.map(responseConverter::indicesGetIndexInformations) //
.flatMapMany(Flux::fromIterable);
}
@Override
@@ -16,14 +16,15 @@
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import static org.springframework.util.ObjectUtils.*;
import static org.springframework.util.CollectionUtils.*;
import co.elastic.clients.elasticsearch._types.Conflicts;
import co.elastic.clients.elasticsearch._types.InlineScript;
import co.elastic.clients.elasticsearch._types.OpType;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.VersionType;
import co.elastic.clients.elasticsearch._types.WaitForActiveShardOptions;
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
@@ -31,20 +32,18 @@ import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch._types.query_dsl.Like;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
import co.elastic.clients.elasticsearch.core.DeleteRequest;
import co.elastic.clients.elasticsearch.core.GetRequest;
import co.elastic.clients.elasticsearch.core.IndexRequest;
import co.elastic.clients.elasticsearch.core.MgetRequest;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation;
import co.elastic.clients.elasticsearch.core.mget.MultiGetOperation;
import co.elastic.clients.elasticsearch.core.msearch.MultisearchBody;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Rescore;
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpDeserializer;
@@ -58,7 +57,9 @@ import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -67,6 +68,7 @@ import java.util.stream.Collectors;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.ScriptType;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasAction;
@@ -91,6 +93,8 @@ import org.springframework.util.StringUtils;
* Class to create Elasticsearch request and request builders.
*
* @author Peter-Josef Meisch
* @author Sascha Woo
* @author cdalxndr
* @since 4.4
*/
class RequestConverter {
@@ -172,8 +176,7 @@ class RequestConverter {
Action.Builder actionBuilder = new Action.Builder();
if (aliasAction instanceof AliasAction.Add) {
AliasAction.Add add = (AliasAction.Add) aliasAction;
if (aliasAction instanceof AliasAction.Add add) {
AliasActionParameters parameters = add.getParameters();
actionBuilder.add(addActionBuilder -> {
addActionBuilder //
@@ -192,7 +195,7 @@ class RequestConverter {
if (filterQuery != null) {
elasticsearchConverter.updateQuery(filterQuery, parameters.getFilterQueryClass());
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
if (esQuery != null) {
addActionBuilder.filter(esQuery);
@@ -202,8 +205,7 @@ class RequestConverter {
});
}
if (aliasAction instanceof AliasAction.Remove) {
AliasAction.Remove remove = (AliasAction.Remove) aliasAction;
if (aliasAction instanceof AliasAction.Remove remove) {
AliasActionParameters parameters = remove.getParameters();
actionBuilder.remove(removeActionBuilder -> {
removeActionBuilder.indices(Arrays.asList(parameters.getIndices()));
@@ -216,8 +218,7 @@ class RequestConverter {
});
}
if (aliasAction instanceof AliasAction.RemoveIndex) {
AliasAction.RemoveIndex removeIndex = (AliasAction.RemoveIndex) aliasAction;
if (aliasAction instanceof AliasAction.RemoveIndex removeIndex) {
AliasActionParameters parameters = removeIndex.getParameters();
actionBuilder.removeIndex(
removeIndexActionBuilder -> removeIndexActionBuilder.indices(Arrays.asList(parameters.getIndices())));
@@ -239,7 +240,6 @@ class RequestConverter {
PutMappingRequest.Builder builder = new PutMappingRequest.Builder();
builder.index(Arrays.asList(indexCoordinates.getIndexNames()));
addPropertiesToMapping(builder, mapping);
// TODO #1973 what else to add
return builder.build();
}
@@ -374,7 +374,7 @@ class RequestConverter {
Query filterQuery = parameters.getFilterQuery();
if (filterQuery != null) {
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
if (esQuery != null) {
aliasBuilder.filter(esQuery);
@@ -426,7 +426,6 @@ class RequestConverter {
* so the code needs to be duplicated.
*/
@SuppressWarnings("DuplicatedCode")
public IndexRequest<?> documentIndexRequest(IndexQuery query, IndexCoordinates indexCoordinates,
@Nullable RefreshPolicy refreshPolicy) {
@@ -466,16 +465,12 @@ class RequestConverter {
if (query.getOpType() != null) {
switch (query.getOpType()) {
case INDEX:
builder.opType(OpType.Index);
break;
case CREATE:
builder.opType(OpType.Create);
break;
case INDEX -> builder.opType(OpType.Index);
case CREATE -> builder.opType(OpType.Create);
}
}
builder.refresh(refresh(refreshPolicy));
builder.refresh(TypeUtils.refresh(refreshPolicy));
return builder.build();
}
@@ -562,6 +557,77 @@ class RequestConverter {
return builder.build();
}
private UpdateOperation<?, ?> bulkUpdateOperation(UpdateQuery query, IndexCoordinates index,
@Nullable RefreshPolicy refreshPolicy) {
UpdateOperation.Builder<Object, Object> uob = new UpdateOperation.Builder<>();
String indexName = query.getIndexName() != null ? query.getIndexName() : index.getIndexName();
uob.index(indexName).id(query.getId());
uob.action(a -> {
a //
.script(getScript(query.getScriptData())) //
.doc(query.getDocument()) //
.upsert(query.getUpsert()) //
.scriptedUpsert(query.getScriptedUpsert()) //
.docAsUpsert(query.getDocAsUpsert()) //
;
if (query.getFetchSource() != null) {
a.source(sc -> sc.fetch(query.getFetchSource()));
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
a.source(sc -> sc.filter(sf -> sf.includes(includes).excludes(excludes)));
}
return a;
});
uob //
.routing(query.getRouting()) //
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
.retryOnConflict(query.getRetryOnConflict()) //
;
// no refresh, timeout, waitForActiveShards on UpdateOperation or UpdateAction
return uob.build();
}
@Nullable
private co.elastic.clients.elasticsearch._types.Script getScript(@Nullable ScriptData scriptData) {
if (scriptData == null) {
return null;
}
Map<String, JsonData> params = new HashMap<>();
if (scriptData.params() != null) {
scriptData.params().forEach((key, value) -> params.put(key, JsonData.of(value, jsonpMapper)));
}
return co.elastic.clients.elasticsearch._types.Script.of(sb -> {
if (scriptData.type() == ScriptType.INLINE) {
sb.inline(is -> is //
.lang(scriptData.language()) //
.source(scriptData.script()) //
.params(params)); //
} else if (scriptData.type() == ScriptType.STORED) {
sb.stored(ss -> ss //
.id(scriptData.script()) //
.params(params) //
);
}
return sb;
});
}
public BulkRequest documentBulkRequest(List<?> queries, BulkOptions bulkOptions, IndexCoordinates indexCoordinates,
@Nullable RefreshPolicy refreshPolicy) {
@@ -571,13 +637,13 @@ class RequestConverter {
builder.timeout(tb -> tb.time(Long.valueOf(bulkOptions.getTimeout().toMillis()).toString() + "ms"));
}
builder.refresh(refresh(refreshPolicy));
builder.refresh(TypeUtils.refresh(refreshPolicy));
if (bulkOptions.getRefreshPolicy() != null) {
builder.refresh(refresh(bulkOptions.getRefreshPolicy()));
builder.refresh(TypeUtils.refresh(bulkOptions.getRefreshPolicy()));
}
if (bulkOptions.getWaitForActiveShards() != null) {
builder.waitForActiveShards(wasb -> wasb.count(bulkOptions.getWaitForActiveShards().getValue()));
builder.waitForActiveShards(wasb -> wasb.count(bulkOptions.getWaitForActiveShards().value()));
}
if (bulkOptions.getPipeline() != null) {
@@ -590,16 +656,15 @@ class RequestConverter {
List<BulkOperation> operations = queries.stream().map(query -> {
BulkOperation.Builder ob = new BulkOperation.Builder();
if (query instanceof IndexQuery) {
IndexQuery indexQuery = (IndexQuery) query;
if (query instanceof IndexQuery indexQuery) {
if (indexQuery.getOpType() == IndexQuery.OpType.CREATE) {
ob.create(bulkCreateOperation(indexQuery, indexCoordinates, refreshPolicy));
} else {
ob.index(bulkIndexOperation(indexQuery, indexCoordinates, refreshPolicy));
}
} else if (query instanceof UpdateQuery) {
// todo #1973
} else if (query instanceof UpdateQuery updateQuery) {
ob.update(bulkUpdateOperation(updateQuery, indexCoordinates, refreshPolicy));
}
return ob.build();
}).collect(Collectors.toList());
@@ -647,8 +712,8 @@ class RequestConverter {
List<MultiGetOperation> multiGetOperations = query.getIdsWithRouting().stream()
.map(idWithRouting -> MultiGetOperation.of(mgo -> mgo //
.index(index.getIndexName()) //
.id(idWithRouting.getId()) //
.routing(idWithRouting.getRouting()) //
.id(idWithRouting.id()) //
.routing(idWithRouting.routing()) //
.source(sourceConfig)))
.collect(Collectors.toList());
@@ -674,7 +739,7 @@ class RequestConverter {
}
if (source.getQuery() != null) {
s.query(getQuery(source.getQuery()));
s.query(getQuery(source.getQuery(), null));
}
if (source.getRemote() != null) {
@@ -717,13 +782,13 @@ class RequestConverter {
ReindexRequest.Dest dest = reindexRequest.getDest();
return d //
.index(dest.getIndex().getIndexName()) //
.versionType(versionType(dest.getVersionType())) //
.opType(opType(dest.getOpType()));
.versionType(TypeUtils.versionType(dest.getVersionType())) //
.opType(TypeUtils.opType(dest.getOpType()));
} //
);
if (reindexRequest.getConflicts() != null) {
builder.conflicts(conflicts(reindexRequest.getConflicts()));
builder.conflicts(TypeUtils.conflicts(reindexRequest.getConflicts()));
}
ReindexRequest.Script script = reindexRequest.getScript();
@@ -731,17 +796,12 @@ class RequestConverter {
builder.script(s -> s.inline(InlineScript.of(i -> i.lang(script.getLang()).source(script.getSource()))));
}
if (reindexRequest.getTimeout() != null) {
builder.timeout(tv -> tv.time(reindexRequest.getTimeout().toMillis() + "ms"));
}
if (reindexRequest.getScroll() != null) {
builder.scroll(tv -> tv.time(reindexRequest.getScroll().toMillis() + "ms"));
}
builder.timeout(time(reindexRequest.getTimeout())) //
.scroll(time(reindexRequest.getScroll()));
if (reindexRequest.getWaitForActiveShards() != null) {
builder.waitForActiveShards(wfas -> wfas //
.count(waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
.count(TypeUtils.waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
}
builder //
@@ -749,7 +809,7 @@ class RequestConverter {
.refresh(reindexRequest.getRefresh()) //
.requireAlias(reindexRequest.getRequireAlias()) //
.requestsPerSecond(reindexRequest.getRequestsPerSecond()) //
.slices(reindexRequest.getSlices());
.slices(slices(reindexRequest.getSlices()));
return builder.build();
}
@@ -766,7 +826,7 @@ class RequestConverter {
if (routing != null) {
r.routing(routing);
}
r.refresh(refresh(refreshPolicy));
r.refresh(TypeUtils.refresh(refreshPolicy));
return r;
});
}
@@ -779,7 +839,7 @@ class RequestConverter {
return DeleteByQueryRequest.of(b -> {
b.index(Arrays.asList(index.getIndexNames())) //
.query(getQuery(query))//
.query(getQuery(query, clazz))//
.refresh(deleteByQueryRefresh(refreshPolicy));
if (query.isLimiting()) {
@@ -787,10 +847,7 @@ class RequestConverter {
b.maxDocs(Long.valueOf(query.getMaxResults()));
}
if (query.hasScrollTime()) {
// noinspection ConstantConditions
b.scroll(Time.of(t -> t.time(query.getScrollTime().toMillis() + "ms")));
}
b.scroll(time(query.getScrollTime()));
if (query.getRoute() != null) {
b.routing(query.getRoute());
@@ -800,6 +857,138 @@ class RequestConverter {
});
}
public UpdateRequest<Document, ?> documentUpdateRequest(UpdateQuery query, IndexCoordinates index,
@Nullable RefreshPolicy refreshPolicy, @Nullable String routing) {
String indexName = query.getIndexName() != null ? query.getIndexName() : index.getIndexName();
return UpdateRequest.of(uqb -> {
uqb.index(indexName).id(query.getId());
if (query.getScript() != null) {
Map<String, JsonData> params = new HashMap<>();
if (query.getParams() != null) {
query.getParams().forEach((key, value) -> params.put(key, JsonData.of(value, jsonpMapper)));
}
uqb.script(sb -> {
if (query.getScriptType() == ScriptType.INLINE) {
sb.inline(is -> is //
.lang(query.getLang()) //
.source(query.getScript()) //
.params(params)); //
} else if (query.getScriptType() == ScriptType.STORED) {
sb.stored(ss -> ss //
.id(query.getScript()) //
.params(params) //
);
}
return sb;
}
);
}
uqb //
.doc(query.getDocument()) //
.upsert(query.getUpsert()) //
.routing(query.getRouting() != null ? query.getRouting() : routing) //
.scriptedUpsert(query.getScriptedUpsert()) //
.docAsUpsert(query.getDocAsUpsert()) //
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
.refresh(TypeUtils.refresh(refreshPolicy)) //
.retryOnConflict(query.getRetryOnConflict()) //
;
if (query.getFetchSource() != null) {
uqb.source(sc -> sc.fetch(query.getFetchSource()));
}
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
: Collections.emptyList();
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
: Collections.emptyList();
uqb.source(sc -> sc.filter(sf -> sf.includes(includes).excludes(excludes)));
}
if (query.getTimeout() != null) {
uqb.timeout(tv -> tv.time(query.getTimeout()));
}
String waitForActiveShards = query.getWaitForActiveShards();
if (waitForActiveShards != null) {
if ("all".equalsIgnoreCase(waitForActiveShards)) {
uqb.waitForActiveShards(wfa -> wfa.option(WaitForActiveShardOptions.All));
} else {
int val;
try {
val = Integer.parseInt(waitForActiveShards);
} catch (NumberFormatException var3) {
throw new IllegalArgumentException("cannot parse ActiveShardCount[" + waitForActiveShards + "]", var3);
}
uqb.waitForActiveShards(wfa -> wfa.count(val));
}
}
return uqb;
} //
);
}
public UpdateByQueryRequest documentUpdateByQueryRequest(UpdateQuery updateQuery, IndexCoordinates index,
@Nullable RefreshPolicy refreshPolicy) {
return UpdateByQueryRequest.of(ub -> {
ub //
.index(Arrays.asList(index.getIndexNames())) //
.refresh(refreshPolicy == RefreshPolicy.IMMEDIATE) //
.routing(updateQuery.getRouting()) //
.script(getScript(updateQuery.getScriptData())) //
.maxDocs(updateQuery.getMaxDocs() != null ? Long.valueOf(updateQuery.getMaxDocs()) : null) //
.pipeline(updateQuery.getPipeline()) //
.requestsPerSecond(
updateQuery.getRequestsPerSecond() != null ? updateQuery.getRequestsPerSecond().longValue() : null) //
.slices(slices(updateQuery.getSlices() != null ? Long.valueOf(updateQuery.getSlices()) : null));
if (updateQuery.getAbortOnVersionConflict() != null) {
ub.conflicts(updateQuery.getAbortOnVersionConflict() ? Conflicts.Abort : Conflicts.Proceed);
}
if (updateQuery.getQuery() != null) {
Query queryQuery = updateQuery.getQuery();
if (updateQuery.getBatchSize() != null) {
((BaseQuery) queryQuery).setMaxResults(updateQuery.getBatchSize());
}
ub.query(getQuery(queryQuery, null));
// no indicesOptions available like in old client
ub.scroll(time(queryQuery.getScrollTime()));
}
// no maxRetries available like in old client
// no shouldStoreResult
if (updateQuery.getRefreshPolicy() != null) {
ub.refresh(updateQuery.getRefreshPolicy() == RefreshPolicy.IMMEDIATE);
}
if (updateQuery.getTimeout() != null) {
ub.timeout(tb -> tb.time(updateQuery.getTimeout()));
}
if (updateQuery.getWaitForActiveShards() != null) {
ub.waitForActiveShards(w -> w.count(TypeUtils.waitForActiveShardsCount(updateQuery.getWaitForActiveShards())));
}
return ub;
});
}
// endregion
// region search
@@ -823,7 +1012,6 @@ class RequestConverter {
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
elasticsearchConverter.updateQuery(query, clazz);
SearchRequest.Builder builder = new SearchRequest.Builder();
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, useScroll);
@@ -831,13 +1019,133 @@ class RequestConverter {
builder.scroll(t -> t.time(scrollTimeInMillis + "ms"));
}
builder.query(getQuery(query));
builder.query(getQuery(query, clazz));
addFilter(query, builder);
return builder.build();
}
public MsearchRequest searchMsearchRequest(
List<ElasticsearchTemplate.MultiSearchQueryParameter> multiSearchQueryParameters) {
// basically the same stuff as in prepareSearchRequest, but the new Elasticsearch has different builders for a
// normal search and msearch
return MsearchRequest.of(mrb -> {
multiSearchQueryParameters.forEach(param -> {
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(param.clazz());
var query = param.query();
mrb.searches(sb -> sb //
.header(h -> {
h //
.index(Arrays.asList(param.index().getIndexNames())) //
.routing(query.getRoute()) //
.searchType(searchType(query.getSearchType())) //
.requestCache(query.getRequestCache()) //
;
if (query.getPreference() != null) {
h.preference(query.getPreference());
}
return h;
}) //
.body(bb -> {
bb //
.query(getQuery(query, param.clazz()))//
.seqNoPrimaryTerm(persistentEntity != null ? persistentEntity.hasSeqNoPrimaryTermProperty() : null) //
.version(true) //
.trackScores(query.getTrackScores()) //
.source(getSourceConfig(query)) //
.timeout(timeStringMs(query.getTimeout())) //
;
if (query.getPageable().isPaged()) {
bb //
.from((int) query.getPageable().getOffset()) //
.size(query.getPageable().getPageSize());
}
if (!isEmpty(query.getFields())) {
bb.fields(fb -> {
query.getFields().forEach(fb::field);
return fb;
});
}
if (!isEmpty(query.getStoredFields())) {
bb.storedFields(query.getStoredFields());
}
if (query.isLimiting()) {
bb.size(query.getMaxResults());
}
if (query.getMinScore() > 0) {
bb.minScore((double) query.getMinScore());
}
if (query.getSort() != null) {
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
if (!sortOptions.isEmpty()) {
bb.sort(sortOptions);
}
}
addHighlight(query, bb);
if (query.getExplain()) {
bb.explain(true);
}
if (!isEmpty(query.getSearchAfter())) {
bb.searchAfter(query.getSearchAfter().stream().map(Object::toString).collect(Collectors.toList()));
}
query.getRescorerQueries().forEach(rescorerQuery -> bb.rescore(getRescore(rescorerQuery)));
if (!query.getRuntimeFields().isEmpty()) {
Map<String, List<RuntimeField>> runtimeMappings = new HashMap<>();
query.getRuntimeFields().forEach(runtimeField -> {
RuntimeField esRuntimeField = RuntimeField.of(rt -> {
RuntimeField.Builder builder = rt
.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType()));
String script = runtimeField.getScript();
if (script != null) {
builder = builder.script(s -> s.inline(is -> is.source(script)));
}
return builder;
});
runtimeMappings.put(runtimeField.getName(), Collections.singletonList(esRuntimeField));
});
bb.runtimeMappings(runtimeMappings);
}
if (!isEmpty(query.getIndicesBoost())) {
Map<String, Double> boosts = new LinkedHashMap<>();
query.getIndicesBoost()
.forEach(indexBoost -> boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost()));
// noinspection unchecked
bb.indicesBoost(boosts);
}
if (query instanceof NativeQuery) {
prepareNativeSearch((NativeQuery) query, bb);
}
return bb;
} //
) //
);
});
return mrb;
});
}
private <T> void prepareSearchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
SearchRequest.Builder builder, boolean forCount, boolean useScroll) {
@@ -848,10 +1156,24 @@ class RequestConverter {
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(clazz);
builder //
.index(Arrays.asList(indexNames)) //
.version(true) //
.trackScores(query.getTrackScores());
var pointInTime = query.getPointInTime();
if (pointInTime != null) {
builder.pit(pb -> pb.id(pointInTime.id()).keepAlive(time(pointInTime.keepAlive())));
} else {
builder.index(Arrays.asList(indexNames));
if (query.getRoute() != null) {
builder.routing(query.getRoute());
}
if (query.getPreference() != null) {
builder.preference(query.getPreference());
}
}
if (persistentEntity != null && persistentEntity.hasSeqNoPrimaryTermProperty()) {
builder.seqNoPrimaryTerm(true);
}
@@ -878,7 +1200,7 @@ class RequestConverter {
}
if (query.getIndicesOptions() != null) {
// todo #1973 indices options
// new Elasticsearch client does not support the old Indices options, need to be adapted
}
if (query.isLimiting()) {
@@ -889,11 +1211,7 @@ class RequestConverter {
builder.minScore((double) query.getMinScore());
}
if (query.getPreference() != null) {
builder.preference(query.getPreference());
}
// todo #1973 searchType
builder.searchType(searchType(query.getSearchType()));
if (query.getSort() != null) {
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
@@ -917,11 +1235,7 @@ class RequestConverter {
builder.trackTotalHits(th -> th.count(query.getTrackTotalHitsUpTo()));
}
if (query.getRoute() != null) {
builder.routing(query.getRoute());
}
// todo #1973 timeout
builder.timeout(timeStringMs(query.getTimeout()));
if (query.getExplain()) {
builder.explain(true);
@@ -930,17 +1244,19 @@ class RequestConverter {
if (!isEmpty(query.getSearchAfter())) {
builder.searchAfter(query.getSearchAfter().stream().map(Object::toString).collect(Collectors.toList()));
}
// todo #1973 rescorer queries
// todo #1973 request cache
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
builder.requestCache(query.getRequestCache());
if (!query.getRuntimeFields().isEmpty()) {
Map<String, RuntimeField> runtimeMappings = new HashMap<>();
query.getRuntimeFields().forEach(runtimeField -> {
runtimeMappings.put(runtimeField.getName(), RuntimeField.of(rt -> rt //
.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType())) //
.script(s -> s.inline(is -> is.source(runtimeField.getScript())))));
});
Map<String, List<RuntimeField>> runtimeMappings = new HashMap<>();
query.getRuntimeFields()
.forEach(runtimeField -> runtimeMappings.put(runtimeField.getName(),
Collections.singletonList(RuntimeField.of(rt -> rt //
.type(RuntimeFieldType._DESERIALIZER.parse(runtimeField.getType())) //
.script(s -> s.inline(is -> is.source(runtimeField.getScript())))))));
builder.runtimeMappings(runtimeMappings);
}
@@ -952,10 +1268,33 @@ class RequestConverter {
// request_cache is not allowed on scroll requests.
builder.requestCache(null);
Duration scrollTimeout = query.getScrollTime() != null ? query.getScrollTime() : Duration.ofMinutes(1);
builder.scroll(tv -> tv.time(scrollTimeout.toMillis() + "ms"));
builder.scroll(time(scrollTimeout));
// limit the number of documents in a batch
builder.size(500);
}
if (!isEmpty(query.getIndicesBoost())) {
Map<String, Double> boosts = new LinkedHashMap<>();
query.getIndicesBoost()
.forEach(indexBoost -> boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost()));
// noinspection unchecked
builder.indicesBoost(boosts);
}
}
private Rescore getRescore(RescorerQuery rescorerQuery) {
return Rescore.of(r -> r //
.query(rq -> rq //
.query(getQuery(rescorerQuery.getQuery(), null)) //
.scoreMode(TypeUtils.scoreMode(rescorerQuery.getScoreMode())) //
.queryWeight(rescorerQuery.getQueryWeight() != null ? Double.valueOf(rescorerQuery.getQueryWeight()) : 1.0) //
.rescoreQueryWeight(
rescorerQuery.getRescoreQueryWeight() != null ? Double.valueOf(rescorerQuery.getRescoreQueryWeight())
: 1.0) //
) //
.windowSize(rescorerQuery.getWindowSize()));
}
private void addHighlight(Query query, SearchRequest.Builder builder) {
@@ -968,6 +1307,16 @@ class RequestConverter {
builder.highlight(highlight);
}
private void addHighlight(Query query, MultisearchBody.Builder builder) {
Highlight highlight = query.getHighlightQuery()
.map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext())
.getHighlight(highlightQuery.getHighlight(), highlightQuery.getType()))
.orElse(null);
builder.highlight(highlight);
}
private List<SortOptions> getSortOptions(Sort sort, @Nullable ElasticsearchPersistentEntity<?> persistentEntity) {
return sort.stream().map(order -> getSortOptions(order, persistentEntity)).collect(Collectors.toList());
}
@@ -978,8 +1327,7 @@ class RequestConverter {
Order.Mode mode = Order.DEFAULT_MODE;
String unmappedType = null;
if (order instanceof Order) {
Order o = (Order) order;
if (order instanceof Order o) {
mode = o.getMode();
unmappedType = o.getUnmappedType();
}
@@ -993,15 +1341,15 @@ class RequestConverter {
String fieldName = property != null ? property.getFieldName() : order.getProperty();
Order.Mode finalMode = mode;
if (order instanceof GeoDistanceOrder) {
GeoDistanceOrder geoDistanceOrder = (GeoDistanceOrder) order;
if (order instanceof GeoDistanceOrder geoDistanceOrder) {
return SortOptions.of(so -> so //
.geoDistance(gd -> gd //
.field(fieldName) //
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint())))//
.distanceType(geoDistanceType(geoDistanceOrder.getDistanceType())).mode(sortMode(finalMode)) //
.unit(distanceUnit(geoDistanceOrder.getUnit())) //
.distanceType(TypeUtils.geoDistanceType(geoDistanceOrder.getDistanceType()))
.mode(TypeUtils.sortMode(finalMode)) //
.unit(TypeUtils.distanceUnit(geoDistanceOrder.getUnit())) //
.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped())));
} else {
String missing = (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) ? "_first"
@@ -1011,10 +1359,10 @@ class RequestConverter {
.field(f -> {
f.field(fieldName) //
.order(sortOrder) //
.mode(sortMode(finalMode));
.mode(TypeUtils.sortMode(finalMode));
if (finalUnmappedType != null) {
FieldType fieldType = fieldType(finalUnmappedType);
FieldType fieldType = TypeUtils.fieldType(finalUnmappedType);
if (fieldType != null) {
f.unmappedType(fieldType);
@@ -1031,32 +1379,59 @@ class RequestConverter {
}
}
@SuppressWarnings("DuplicatedCode")
private void prepareNativeSearch(NativeQuery query, SearchRequest.Builder builder) {
// todo #1973 script fields
// todo #1973 collapse builder
// todo #1973 indices boost
query.getScriptedFields().forEach(scriptedField -> builder.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
builder //
.suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) //
.sort(query.getSortOptions());
if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations());
}
builder.suggest(query.getSuggester());
// todo #2150 searchExt, currently not supported by the new client
}
// todo #1973 searchExt
@SuppressWarnings("DuplicatedCode")
private void prepareNativeSearch(NativeQuery query, MultisearchBody.Builder builder) {
query.getScriptedFields().forEach(scriptedField -> builder.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
builder //
.suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) //
.sort(query.getSortOptions());
if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations());
}
// todo #2150 searchExt, currently not supported by the new client
}
@Nullable
private co.elastic.clients.elasticsearch._types.query_dsl.Query getQuery(Query query) {
private co.elastic.clients.elasticsearch._types.query_dsl.Query getQuery(@Nullable Query query,
@Nullable Class<?> clazz) {
if (query == null) {
return null;
}
elasticsearchConverter.updateQuery(query, clazz);
// todo #1973 some native stuff
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = null;
if (query instanceof CriteriaQuery) {
esQuery = CriteriaQueryProcessor.createQuery(((CriteriaQuery) query).getCriteria());
} else if (query instanceof StringQuery) {
esQuery = QueryBuilders.wrapperQueryAsQuery(((StringQuery) query).getSource());
} else if (query instanceof NativeQuery) {
NativeQuery nativeQuery = (NativeQuery) query;
} else if (query instanceof NativeQuery nativeQuery) {
if (nativeQuery.getQuery() != null) {
esQuery = nativeQuery.getQuery();
@@ -1064,6 +1439,7 @@ class RequestConverter {
} else {
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
}
return esQuery;
}
@@ -1074,7 +1450,7 @@ class RequestConverter {
} else if (query instanceof StringQuery) {
// no filter for StringQuery
} else if (query instanceof NativeQuery) {
// todo #1973 NativeQuery filter
builder.postFilter(((NativeQuery) query).getFilter());
} else {
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
}
@@ -1128,6 +1504,28 @@ class RequestConverter {
return moreLikeThisQuery;
}
public OpenPointInTimeRequest searchOpenPointInTimeRequest(IndexCoordinates index, Duration keepAlive,
Boolean ignoreUnavailable) {
Assert.notNull(index, "index must not be null");
Assert.notNull(keepAlive, "keepAlive must not be null");
Assert.notNull(ignoreUnavailable, "ignoreUnavailable must not be null");
return OpenPointInTimeRequest.of(opit -> opit //
.index(Arrays.asList(index.getIndexNames())) //
.ignoreUnavailable(ignoreUnavailable) //
.keepAlive(time(keepAlive)) //
);
}
public ClosePointInTimeRequest searchClosePointInTime(String pit) {
Assert.notNull(pit, "pit must not be null");
return ClosePointInTimeRequest.of(cpit -> cpit.id(pit));
}
// endregion
// region helper functions
@@ -1188,32 +1586,18 @@ class RequestConverter {
.getVersionType();
if (entityVersionType != null) {
switch (entityVersionType) {
case INTERNAL:
versionType = VersionType.Internal;
break;
case EXTERNAL:
versionType = VersionType.External;
break;
case EXTERNAL_GTE:
versionType = VersionType.ExternalGte;
break;
case FORCE:
versionType = VersionType.Force;
break;
}
versionType = switch (entityVersionType) {
case INTERNAL -> VersionType.Internal;
case EXTERNAL -> VersionType.External;
case EXTERNAL_GTE -> VersionType.ExternalGte;
case FORCE -> VersionType.Force;
};
}
}
return versionType != null ? versionType : VersionType.External;
}
private co.elastic.clients.elasticsearch._types.query_dsl.Query getFilter(Query filterQuery) {
// TODO #1973 add filter query
throw new UnsupportedOperationException("not implemented");
}
@Nullable
private SourceConfig getSourceConfig(Query query) {
@@ -1246,15 +1630,11 @@ class RequestConverter {
return null;
}
switch (refreshPolicy) {
case IMMEDIATE:
return true;
case WAIT_UNTIL:
return null;
case NONE:
default:
return false;
}
return switch (refreshPolicy) {
case IMMEDIATE -> true;
case WAIT_UNTIL -> null;
case NONE -> false;
};
}
// endregion
@@ -23,6 +23,7 @@ import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
import co.elastic.clients.elasticsearch.core.UpdateByQueryResponse;
import co.elastic.clients.elasticsearch.core.mget.MultiGetError;
import co.elastic.clients.elasticsearch.core.mget.MultiGetResponseItem;
import co.elastic.clients.elasticsearch.indices.*;
@@ -89,7 +90,7 @@ class ResponseConverter {
.withNumberOfPendingTasks(healthResponse.numberOfPendingTasks()) //
.withRelocatingShards(healthResponse.relocatingShards()) //
.withStatus(healthResponse.status().toString()) //
.withTaskMaxWaitingTimeMillis(Long.parseLong(healthResponse.taskMaxWaitingInQueueMillis())) //
.withTaskMaxWaitingTimeMillis(healthResponse.taskMaxWaitingInQueueMillis().toEpochMilli()) //
.withTimedOut(healthResponse.timedOut()) //
.withUnassignedShards(healthResponse.unassignedShards()) //
.build(); //
@@ -143,11 +144,11 @@ class ResponseConverter {
if (indexMappingRecord == null) {
if (mappings.size() != 1) {
LOGGER.warn("no mapping returned for index {}", indexCoordinates.getIndexName());
return Document.create();
}
String index = mappings.keySet().iterator().next();
indexMappingRecord = mappings.get(index);
LOGGER.warn("no mapping returned for index {}", indexCoordinates.getIndexName());
return Document.create();
}
String index = mappings.keySet().iterator().next();
indexMappingRecord = mappings.get(index);
}
return Document.parse(toJson(indexMappingRecord.mappings(), jsonpMapper));
@@ -276,9 +277,9 @@ class ResponseConverter {
.withNoops(reindexResponse.noops()) //
.withBulkRetries(reindexResponse.retries().bulk()) //
.withSearchRetries(reindexResponse.retries().search()) //
.withThrottledMillis(Long.parseLong(reindexResponse.throttledMillis())) //
.withThrottledMillis(reindexResponse.throttledMillis().toEpochMilli()) //
.withRequestsPerSecond(reindexResponse.requestsPerSecond()) //
.withThrottledUntilMillis(Long.parseLong(reindexResponse.throttledUntilMillis())).withFailures(failures) //
.withThrottledUntilMillis(reindexResponse.throttledUntilMillis().toEpochMilli()).withFailures(failures) //
.build();
}
@@ -314,6 +315,56 @@ class ResponseConverter {
}
public ByQueryResponse byQueryResponse(DeleteByQueryResponse response) {
// the code for the methods taking a DeleteByQueryResponse or a UpdateByQueryResponse is duplicated because the
// Elasticsearch responses do not share a common class
// noinspection DuplicatedCode
List<ByQueryResponse.Failure> failures = response.failures().stream().map(this::byQueryResponseFailureOf)
.collect(Collectors.toList());
ByQueryResponse.ByQueryResponseBuilder builder = ByQueryResponse.builder();
if (response.took() != null) {
builder.withTook(response.took());
}
if (response.timedOut() != null) {
builder.withTimedOut(response.timedOut());
}
if (response.total() != null) {
builder.withTotal(response.total());
}
if (response.deleted() != null) {
builder.withDeleted(response.deleted());
}
if (response.batches() != null) {
builder.withBatches(Math.toIntExact(response.batches()));
}
if (response.versionConflicts() != null) {
builder.withVersionConflicts(response.versionConflicts());
}
if (response.noops() != null) {
builder.withNoops(response.noops());
}
if (response.retries() != null) {
builder.withBulkRetries(response.retries().bulk());
builder.withSearchRetries(response.retries().search());
}
builder.withFailures(failures);
return builder.build();
}
public ByQueryResponse byQueryResponse(UpdateByQueryResponse response) {
// the code for the methods taking a DeleteByQueryResponse or a UpdateByQueryResponse is duplicated because the
// Elasticsearch responses do not share a common class
// noinspection DuplicatedCode
List<ByQueryResponse.Failure> failures = response.failures().stream().map(this::byQueryResponseFailureOf)
.collect(Collectors.toList());
@@ -358,8 +409,8 @@ class ResponseConverter {
}
// endregion
// region helper functions
private long timeToLong(Time time) {
if (time.isTime()) {
@@ -17,23 +17,35 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggest;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonpMapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.elasticsearch.search.SearchHits;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.PhraseSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion;
import org.springframework.data.elasticsearch.support.ScoreDoc;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Factory class to create {@link SearchDocumentResponse} instances.
@@ -42,34 +54,38 @@ import org.springframework.util.Assert;
* @since 4.4
*/
class SearchDocumentResponseBuilder {
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponseBuilder.class);
/**
* creates a SearchDocumentResponse from the {@link SearchResponse}
*
* @param searchResponse the Elasticsearch search response
* @param responseBody the Elasticsearch response body
* @param entityCreator function to create an entity from a {@link SearchDocument}
* @param jsonpMapper to map JsonData objects
* @return the SearchDocumentResponse
*/
public static <T> SearchDocumentResponse from(SearchResponse<EntityAsMap> searchResponse,
public static <T> SearchDocumentResponse from(ResponseBody<EntityAsMap> responseBody,
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {
Assert.notNull(searchResponse, "searchResponse must not be null");
Assert.notNull(responseBody, "responseBody must not be null");
Assert.notNull(entityCreator, "entityCreator must not be null");
HitsMetadata<EntityAsMap> hitsMetadata = searchResponse.hits();
String scrollId = searchResponse.scrollId();
Map<String, Aggregate> aggregations = searchResponse.aggregations();
Map<String, List<Suggestion<EntityAsMap>>> suggest = searchResponse.suggest();
HitsMetadata<EntityAsMap> hitsMetadata = responseBody.hits();
String scrollId = responseBody.scrollId();
Map<String, Aggregate> aggregations = responseBody.aggregations();
Map<String, List<Suggestion<EntityAsMap>>> suggest = responseBody.suggest();
var pointInTimeId = responseBody.pitId();
return from(hitsMetadata, scrollId, aggregations, suggest, entityCreator, jsonpMapper);
return from(hitsMetadata, scrollId, pointInTimeId, aggregations, suggest, entityCreator, jsonpMapper);
}
/**
* creates a {@link SearchDocumentResponseBuilder} from {@link SearchHits} with the given scrollId aggregations and
* creates a {@link SearchDocumentResponseBuilder} from {@link HitsMetadata} with the given scrollId aggregations and
* suggestES
*
* @param <T> entity type
* @param hitsMetadata the {@link SearchHits} to process
* @param hitsMetadata the {@link HitsMetadata} to process
* @param scrollId scrollId
* @param aggregations aggregations
* @param suggestES the suggestion response from Elasticsearch
@@ -78,8 +94,9 @@ class SearchDocumentResponseBuilder {
* @return the {@link SearchDocumentResponse}
*/
public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nullable String scrollId,
Map<String, Aggregate> aggregations, Map<String, List<Suggestion<EntityAsMap>>> suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {
@Nullable String pointInTimeId, @Nullable Map<String, Aggregate> aggregations,
Map<String, List<Suggestion<EntityAsMap>>> suggestES, SearchDocumentResponse.EntityCreator<T> entityCreator,
JsonpMapper jsonpMapper) {
Assert.notNull(hitsMetadata, "hitsMetadata must not be null");
@@ -89,16 +106,11 @@ class SearchDocumentResponseBuilder {
TotalHits responseTotalHits = hitsMetadata.total();
if (responseTotalHits != null) {
totalHits = responseTotalHits.value();
switch (responseTotalHits.relation().jsonValue()) {
case "eq":
totalHitsRelation = TotalHitsRelation.EQUAL_TO.name();
break;
case "gte":
totalHitsRelation = TotalHitsRelation.GREATER_THAN_OR_EQUAL_TO.name();
break;
default:
totalHitsRelation = TotalHitsRelation.OFF.name();
}
totalHitsRelation = switch (responseTotalHits.relation().jsonValue()) {
case "eq" -> TotalHitsRelation.EQUAL_TO.name();
case "gte" -> TotalHitsRelation.GREATER_THAN_OR_EQUAL_TO.name();
default -> TotalHitsRelation.OFF.name();
};
} else {
totalHits = hitsMetadata.hits().size();
totalHitsRelation = "OFF";
@@ -114,10 +126,115 @@ class SearchDocumentResponseBuilder {
ElasticsearchAggregations aggregationsContainer = aggregations != null ? new ElasticsearchAggregations(aggregations)
: null;
// todo #1973
Suggest suggest = null;
Suggest suggest = suggestFrom(suggestES, entityCreator);
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, pointInTimeId, searchDocuments,
aggregationsContainer, suggest);
}
@Nullable
private static <T> Suggest suggestFrom(Map<String, List<Suggestion<EntityAsMap>>> suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
if (CollectionUtils.isEmpty(suggestES)) {
return null;
}
List<Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>> suggestions = new ArrayList<>();
suggestES.forEach((name, suggestionsES) -> {
if (!suggestionsES.isEmpty()) {
// take the type from the first entry
switch (suggestionsES.get(0)._kind()) {
case Term -> {
suggestions.add(getTermSuggestion(name, suggestionsES));
break;
}
case Phrase -> {
suggestions.add(getPhraseSuggestion(name, suggestionsES));
break;
}
case Completion -> {
suggestions.add(getCompletionSuggestion(name, suggestionsES, entityCreator));
break;
}
default -> {}
}
}
});
// todo: hasScoreDocs checks if any one
boolean hasScoreDocs = false;
return new Suggest(suggestions, hasScoreDocs);
}
private static TermSuggestion getTermSuggestion(String name, List<Suggestion<EntityAsMap>> suggestionsES) {
List<TermSuggestion.Entry> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
var termSuggest = suggestionES.term();
var termSuggestOptions = termSuggest.options();
List<TermSuggestion.Entry.Option> options = new ArrayList<>();
termSuggestOptions.forEach(optionES -> options.add(new TermSuggestion.Entry.Option(optionES.text(), null,
optionES.score(), null, Math.toIntExact(optionES.freq()))));
entries.add(new TermSuggestion.Entry(termSuggest.text(), termSuggest.offset(), termSuggest.length(), options));
});
return new TermSuggestion(name, suggestionsES.size(), entries, null);
}
private static PhraseSuggestion getPhraseSuggestion(String name, List<Suggestion<EntityAsMap>> suggestionsES) {
List<PhraseSuggestion.Entry> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
var phraseSuggest = suggestionES.phrase();
var phraseSuggestOptions = phraseSuggest.options();
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
phraseSuggestOptions.forEach(optionES -> options
.add(new PhraseSuggestion.Entry.Option(optionES.text(), optionES.highlighted(), null, null)));
entries.add(new PhraseSuggestion.Entry(phraseSuggest.text(), phraseSuggest.offset(), phraseSuggest.length(),
options, null));
});
return new PhraseSuggestion(name, suggestionsES.size(), entries);
}
private static <T> CompletionSuggestion<T> getCompletionSuggestion(String name,
List<Suggestion<EntityAsMap>> suggestionsES, SearchDocumentResponse.EntityCreator<T> entityCreator) {
List<CompletionSuggestion.Entry<T>> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
CompletionSuggest<EntityAsMap> completionSuggest = suggestionES.completion();
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
List<CompletionSuggestOption<EntityAsMap>> optionsES = completionSuggest.options();
optionsES.forEach(optionES -> {
SearchDocument searchDocument = (optionES.source() != null) ? DocumentAdapters.from(optionES) : null;
T hitEntity = null;
if (searchDocument != null) {
try {
hitEntity = entityCreator.apply(searchDocument).get();
} catch (Exception e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Error creating entity from SearchDocument: " + e.getMessage());
}
}
}
Map<String, Set<String>> contexts = new HashMap<>();
optionES.contexts().forEach((key, contextList) -> contexts.put(key,
contextList.stream().map(context -> context._get().toString()).collect(Collectors.toSet())));
// response from the new client does not have a doc and shardindex as the ScoreDoc from the old client responses
options.add(new CompletionSuggestion.Entry.Option<>(optionES.text(), null, optionES.score(),
optionES.collateMatch() != null ? optionES.collateMatch() : false, contexts,
new ScoreDoc(optionES.score() != null ? optionES.score() : Double.NaN, null, null), searchDocument,
hitEntity));
});
entries.add(new CompletionSuggestion.Entry<>(completionSuggest.text(), completionSuggest.offset(),
completionSuggest.length(), options));
});
return new CompletionSuggestion<>(name, suggestionsES.size(), entries);
}
}
@@ -15,26 +15,25 @@
*/
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.Conflicts;
import co.elastic.clients.elasticsearch._types.DistanceUnit;
import co.elastic.clients.elasticsearch._types.GeoDistanceType;
import co.elastic.clients.elasticsearch._types.OpType;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.SortMode;
import co.elastic.clients.elasticsearch._types.VersionType;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
import co.elastic.clients.elasticsearch.core.search.BuiltinHighlighterType;
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
import co.elastic.clients.elasticsearch.core.search.HighlighterOrder;
import co.elastic.clients.elasticsearch.core.search.HighlighterTagsSchema;
import co.elastic.clients.elasticsearch.core.search.HighlighterType;
import co.elastic.clients.elasticsearch.core.search.ScoreMode;
import java.time.Duration;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.Order;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
import org.springframework.lang.Nullable;
@@ -50,64 +49,38 @@ final class TypeUtils {
static BoundaryScanner boundaryScanner(@Nullable String value) {
if (value != null) {
switch (value.toLowerCase()) {
case "chars":
return BoundaryScanner.Chars;
case "sentence":
return BoundaryScanner.Sentence;
case "word":
return BoundaryScanner.Word;
default:
return null;
}
return switch (value.toLowerCase()) {
case "chars" -> BoundaryScanner.Chars;
case "sentence" -> BoundaryScanner.Sentence;
case "word" -> BoundaryScanner.Word;
default -> null;
};
}
return null;
}
static Conflicts conflicts(ReindexRequest.Conflicts conflicts) {
switch (conflicts) {
case ABORT:
return Conflicts.Abort;
case PROCEED:
return Conflicts.Proceed;
}
throw new IllegalArgumentException("Cannot map conflicts value " + conflicts.name());
return switch (conflicts) {
case ABORT -> Conflicts.Abort;
case PROCEED -> Conflicts.Proceed;
};
}
@Nullable
static DistanceUnit distanceUnit(String unit) {
switch (unit.toLowerCase()) {
case "in":
case "inch":
return DistanceUnit.Inches;
case "yd":
case "yards":
return DistanceUnit.Yards;
case "ft":
case "feet":
return DistanceUnit.Feet;
case "km":
case "kilometers":
return DistanceUnit.Kilometers;
case "nm":
case "nmi":
return DistanceUnit.NauticMiles;
case "mm":
case "millimeters":
return DistanceUnit.Millimeters;
case "cm":
case "centimeters":
return DistanceUnit.Centimeters;
case "mi":
case "miles":
return DistanceUnit.Miles;
case "m":
case "meters":
return DistanceUnit.Meters;
}
return null;
return switch (unit.toLowerCase()) {
case "in", "inch" -> DistanceUnit.Inches;
case "yd", "yards" -> DistanceUnit.Yards;
case "ft", "feet" -> DistanceUnit.Feet;
case "km", "kilometers" -> DistanceUnit.Kilometers;
case "nm", "nmi" -> DistanceUnit.NauticMiles;
case "mm", "millimeters" -> DistanceUnit.Millimeters;
case "cm", "centimeters" -> DistanceUnit.Centimeters;
case "mi", "miles" -> DistanceUnit.Miles;
case "m", "meters" -> DistanceUnit.Meters;
default -> null;
};
}
@Nullable
@@ -125,28 +98,22 @@ final class TypeUtils {
@Nullable
static GeoDistanceType geoDistanceType(GeoDistanceOrder.DistanceType distanceType) {
switch (distanceType) {
case arc:
return GeoDistanceType.Arc;
case plane:
return GeoDistanceType.Plane;
}
return switch (distanceType) {
case arc -> GeoDistanceType.Arc;
case plane -> GeoDistanceType.Plane;
};
return null;
}
@Nullable
static HighlighterFragmenter highlighterFragmenter(@Nullable String value) {
if (value != null) {
switch (value.toLowerCase()) {
case "simple":
return HighlighterFragmenter.Simple;
case "span":
return HighlighterFragmenter.Span;
default:
return null;
}
return switch (value.toLowerCase()) {
case "simple" -> HighlighterFragmenter.Simple;
case "span" -> HighlighterFragmenter.Span;
default -> null;
};
}
return null;
@@ -168,16 +135,12 @@ final class TypeUtils {
static HighlighterType highlighterType(@Nullable String value) {
if (value != null) {
switch (value.toLowerCase()) {
case "unified":
return HighlighterType.of(b -> b.builtin(BuiltinHighlighterType.Unified));
case "plain":
return HighlighterType.of(b -> b.builtin(BuiltinHighlighterType.Plain));
case "fvh":
return HighlighterType.of(b -> b.builtin(BuiltinHighlighterType.FastVector));
default:
return null;
}
return switch (value.toLowerCase()) {
case "unified" -> HighlighterType.Unified;
case "plain" -> HighlighterType.Plain;
case "fvh" -> HighlighterType.FastVector;
default -> null;
};
}
return null;
@@ -187,14 +150,11 @@ final class TypeUtils {
static HighlighterEncoder highlighterEncoder(@Nullable String value) {
if (value != null) {
switch (value.toLowerCase()) {
case "default":
return HighlighterEncoder.Default;
case "html":
return HighlighterEncoder.Html;
default:
return null;
}
return switch (value.toLowerCase()) {
case "default" -> HighlighterEncoder.Default;
case "html" -> HighlighterEncoder.Html;
default -> null;
};
}
return null;
@@ -216,12 +176,10 @@ final class TypeUtils {
static OpType opType(@Nullable IndexQuery.OpType opType) {
if (opType != null) {
switch (opType) {
case INDEX:
return OpType.Index;
case CREATE:
return OpType.Create;
}
return switch (opType) {
case INDEX -> OpType.Index;
case CREATE -> OpType.Create;
};
}
return null;
}
@@ -232,32 +190,102 @@ final class TypeUtils {
return Refresh.False;
}
switch (refreshPolicy) {
case IMMEDIATE:
return Refresh.True;
case WAIT_UNTIL:
return Refresh.WaitFor;
case NONE:
default:
return Refresh.False;
return switch (refreshPolicy) {
case IMMEDIATE -> Refresh.True;
case WAIT_UNTIL -> Refresh.WaitFor;
case NONE -> Refresh.False;
};
}
@Nullable
static UpdateResponse.Result result(@Nullable Result result) {
if (result == null) {
return null;
}
return switch (result) {
case Created -> UpdateResponse.Result.CREATED;
case Updated -> UpdateResponse.Result.UPDATED;
case Deleted -> UpdateResponse.Result.DELETED;
case NotFound -> UpdateResponse.Result.NOT_FOUND;
case NoOp -> UpdateResponse.Result.NOOP;
};
}
@Nullable
static ScoreMode scoreMode(@Nullable RescorerQuery.ScoreMode scoreMode) {
if (scoreMode == null) {
return null;
}
return switch (scoreMode) {
case Default -> null;
case Avg -> ScoreMode.Avg;
case Max -> ScoreMode.Max;
case Min -> ScoreMode.Min;
case Total -> ScoreMode.Total;
case Multiply -> ScoreMode.Multiply;
};
}
@Nullable
static SearchType searchType(@Nullable Query.SearchType searchType) {
if (searchType == null) {
return null;
}
return switch (searchType) {
case QUERY_THEN_FETCH -> SearchType.QueryThenFetch;
case DFS_QUERY_THEN_FETCH -> SearchType.DfsQueryThenFetch;
};
}
@Nullable
static Slices slices(@Nullable Long count) {
if (count == null) {
return null;
}
return Slices.of(s -> s.value(Math.toIntExact(count)));
}
@Nullable
static SortMode sortMode(Order.Mode mode) {
switch (mode) {
case min:
return SortMode.Min;
case max:
return SortMode.Max;
case median:
return SortMode.Median;
case avg:
return SortMode.Avg;
return switch (mode) {
case min -> SortMode.Min;
case max -> SortMode.Max;
case median -> SortMode.Median;
case avg -> SortMode.Avg;
};
}
@Nullable
static Time time(@Nullable Duration duration) {
if (duration == null) {
return null;
}
return null;
return Time.of(t -> t.time(duration.toMillis() + "ms"));
}
@Nullable
static String timeStringMs(@Nullable Duration duration) {
if (duration == null) {
return null;
}
return duration.toMillis() + "ms";
}
@Nullable
@@ -265,16 +293,12 @@ final class TypeUtils {
@Nullable org.springframework.data.elasticsearch.annotations.Document.VersionType versionType) {
if (versionType != null) {
switch (versionType) {
case INTERNAL:
return VersionType.Internal;
case EXTERNAL:
return VersionType.External;
case EXTERNAL_GTE:
return VersionType.ExternalGte;
case FORCE:
return VersionType.Force;
}
return switch (versionType) {
case INTERNAL -> VersionType.Internal;
case EXTERNAL -> VersionType.External;
case EXTERNAL_GTE -> VersionType.ExternalGte;
case FORCE -> VersionType.Force;
};
}
return null;
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.config;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
/**
@@ -26,7 +26,9 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverte
* @author Peter-Josef Meisch
* @since 3.2
* @see ElasticsearchConfigurationSupport
* @deprecated since 5.0
*/
@Deprecated
public abstract class AbstractElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
/**
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.config;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.action.support.IndicesOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.lang.Nullable;
@@ -29,7 +28,9 @@ import org.springframework.lang.Nullable;
* @author Peter-Josef Meisch
* @since 3.2
* @see ElasticsearchConfigurationSupport
* @deprecated since 5.0
*/
@Deprecated
public abstract class AbstractReactiveElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
@@ -47,7 +47,9 @@ import org.springframework.util.Assert;
* @author Mohsin Husen
* @author Artur Konczak
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
class CriteriaFilterProcessor {
@Nullable
@@ -103,37 +105,37 @@ class CriteriaFilterProcessor {
QueryBuilder filter = null;
switch (key) {
case WITHIN:
case WITHIN -> {
Assert.isTrue(value instanceof Object[], "Value of a geo distance filter should be an array of two values.");
filter = withinQuery(fieldName, (Object[]) value);
break;
case BBOX:
}
case BBOX -> {
Assert.isTrue(value instanceof Object[],
"Value of a boundedBy filter should be an array of one or two values.");
filter = boundingBoxQuery(fieldName, (Object[]) value);
break;
case GEO_INTERSECTS:
}
case GEO_INTERSECTS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_INTERSECTS filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "intersects");
break;
case GEO_IS_DISJOINT:
}
case GEO_IS_DISJOINT -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_IS_DISJOINT filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "disjoint");
break;
case GEO_WITHIN:
}
case GEO_WITHIN -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_WITHIN filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "within");
break;
case GEO_CONTAINS:
}
case GEO_CONTAINS -> {
Assert.isTrue(value instanceof GeoJson<?>, "value of a GEO_CONTAINS filter must be a GeoJson object");
filter = geoJsonQuery(fieldName, (GeoJson<?>) value, "contains");
break;
}
}
return filter;
}
private QueryBuilder withinQuery(String fieldName, Object[] valArray) {
private QueryBuilder withinQuery(String fieldName, Object... valArray) {
GeoDistanceQueryBuilder filter = QueryBuilders.geoDistanceQuery(fieldName);
@@ -152,8 +154,7 @@ class CriteriaFilterProcessor {
dist.append((String) valArray[1]);
}
if (valArray[0] instanceof GeoPoint) {
GeoPoint loc = (GeoPoint) valArray[0];
if (valArray[0]instanceof GeoPoint loc) {
filter.point(loc.getLat(), loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
} else if (valArray[0] instanceof Point) {
GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]);
@@ -172,7 +173,7 @@ class CriteriaFilterProcessor {
return filter;
}
private QueryBuilder boundingBoxQuery(String fieldName, Object[] valArray) {
private QueryBuilder boundingBoxQuery(String fieldName, Object... valArray) {
Assert.noNullElements(valArray, "Geo boundedBy filter takes a not null element array as parameter.");
@@ -215,12 +216,8 @@ class CriteriaFilterProcessor {
Metrics metric = (Metrics) distance.getMetric();
switch (metric) {
case KILOMETERS:
sb.append("km");
break;
case MILES:
sb.append("mi");
break;
case KILOMETERS -> sb.append("km");
case MILES -> sb.append("mi");
}
}
@@ -248,11 +245,10 @@ class CriteriaFilterProcessor {
return true;
}
private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object[] values) {
private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object... values) {
Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class),
" both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
if (values[0] instanceof GeoPoint) {
GeoPoint topLeft = (GeoPoint) values[0];
if (values[0]instanceof GeoPoint topLeft) {
GeoPoint bottomRight = (GeoPoint) values[1];
filter.setCorners(topLeft.getLat(), topLeft.getLon(), bottomRight.getLat(), bottomRight.getLon());
} else {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import static org.elasticsearch.index.query.Operator.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
@@ -44,7 +44,9 @@ import org.springframework.util.Assert;
* @author Rasmus Faber-Espensen
* @author James Bodkin
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
class CriteriaQueryProcessor {
@Nullable
@@ -173,17 +175,10 @@ class CriteriaQueryProcessor {
// operations without a value
switch (key) {
case EXISTS:
query = existsQuery(fieldName);
break;
case EMPTY:
query = boolQuery().must(existsQuery(fieldName)).mustNot(wildcardQuery(fieldName, "*"));
break;
case NOT_EMPTY:
query = wildcardQuery(fieldName, "*");
break;
default:
break;
case EXISTS -> query = existsQuery(fieldName);
case EMPTY -> query = boolQuery().must(existsQuery(fieldName)).mustNot(wildcardQuery(fieldName, "*"));
case NOT_EMPTY -> query = wildcardQuery(fieldName, "*");
default -> {}
}
if (query != null) {
@@ -236,8 +231,7 @@ class CriteriaQueryProcessor {
query = matchQuery(fieldName, value).operator(org.elasticsearch.index.query.Operator.AND);
break;
case IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
query = boolQuery().must(termsQuery(fieldName, toStringList(iterable)));
} else {
@@ -246,8 +240,7 @@ class CriteriaQueryProcessor {
}
break;
case NOT_IN:
if (value instanceof Iterable) {
Iterable<?> iterable = (Iterable<?>) value;
if (value instanceof Iterable<?> iterable) {
if (isKeywordField) {
query = boolQuery().mustNot(termsQuery(fieldName, toStringList(iterable)));
} else {
@@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.cluster;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.RequestOptions;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ResponseConverter;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
/**
* Default implementation of {@link ClusterOperations} using the {@link ElasticsearchRestTemplate}.
*
* @author Peter-Josef Meisch
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
class DefaultClusterOperations implements ClusterOperations {
private final ElasticsearchRestTemplate template;
@@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.cluster;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Mono;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ResponseConverter;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
/**
* Default implementation of {@link ReactiveClusterOperations} using the {@link ReactiveElasticsearchOperations}.
*
* @author Peter-Josef Meisch
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
public class DefaultReactiveClusterOperations implements ReactiveClusterOperations {
private final ReactiveElasticsearchOperations operations;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -95,12 +95,10 @@ import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Cluster;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices;
import org.springframework.data.elasticsearch.client.util.NamedXContents;
import org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Cluster;
import org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Indices;
import org.springframework.data.elasticsearch.client.util.ScrollState;
import org.springframework.data.elasticsearch.core.ResponseConverter;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.util.Lazy;
import org.springframework.http.HttpHeaders;
@@ -134,12 +132,14 @@ import org.springframework.web.reactive.function.client.WebClient.RequestBodySpe
* @since 3.2
* @see ClientConfiguration
* @see ReactiveRestClients
* @deprecated since 5.0
*/
@Deprecated
public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearchClient, Indices, Cluster {
private final HostProvider<?> hostProvider;
private final RequestCreator requestCreator;
private Supplier<HttpHeaders> headersSupplier = () -> HttpHeaders.EMPTY;
private Supplier<org.springframework.data.elasticsearch.support.HttpHeaders> headersSupplier = org.springframework.data.elasticsearch.support.HttpHeaders::new;
/**
* Create a new {@link DefaultReactiveElasticsearchClient} using the given {@link HostProvider} to obtain server
@@ -181,8 +181,10 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
Assert.notNull(headers, "HttpHeaders must not be null");
Assert.notEmpty(hosts, "Elasticsearch Cluster needs to consist of at least one host");
var httpHeaders = new org.springframework.data.elasticsearch.support.HttpHeaders();
httpHeaders.addAll(headers);
ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(hosts)
.withDefaultHeaders(headers).build();
.withDefaultHeaders(httpHeaders).build();
return create(clientConfiguration);
}
@@ -226,7 +228,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
return client;
}
public void setHeadersSupplier(Supplier<HttpHeaders> headersSupplier) {
public void setHeadersSupplier(Supplier<org.springframework.data.elasticsearch.support.HttpHeaders> headersSupplier) {
Assert.notNull(headersSupplier, "headersSupplier must not be null");
@@ -716,25 +718,25 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
if (RawActionResponse.class.equals(responseType)) {
ClientLogger.logRawResponse(logId, response.statusCode());
ClientLogger.logRawResponse(logId, response.statusCode().value());
return Mono.just(responseType.cast(RawActionResponse.create(response)));
}
if (response.statusCode().is5xxServerError()) {
ClientLogger.logRawResponse(logId, response.statusCode());
ClientLogger.logRawResponse(logId, response.statusCode().value());
return handleServerError(request, response);
}
if (response.statusCode().is4xxClientError()) {
ClientLogger.logRawResponse(logId, response.statusCode());
ClientLogger.logRawResponse(logId, response.statusCode().value());
return handleClientError(logId, response, responseType);
}
return response.body(BodyExtractors.toMono(byte[].class)) //
.map(it -> new String(it, StandardCharsets.UTF_8)) //
.doOnNext(it -> ClientLogger.logResponse(logId, response.statusCode(), it)) //
.doOnNext(it -> ClientLogger.logResponse(logId, response.statusCode().value(), it)) //
.flatMap(content -> doDecode(response, responseType, content));
}
@@ -811,7 +813,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
return response.body(BodyExtractors.toMono(byte[].class)) //
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
.flatMap(content -> contentOrError(content, mediaType, status)) //
.doOnNext(content -> ClientLogger.logResponse(logId, response.statusCode(), content)) //
.doOnNext(content -> ClientLogger.logResponse(logId, response.statusCode().value(), content)) //
.flatMap(content -> doDecode(response, responseType, content));
}
@@ -893,7 +895,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Status#hosts()
* @see org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchClient.Status#hosts()
*/
@Override
public Collection<ElasticsearchHost> hosts() {
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
/**
* @author Roman Puchkovskiy
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
class DefaultRequestCreator implements RequestCreator {}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import java.net.InetSocketAddress;
import java.util.Map;
@@ -37,7 +37,9 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
* @author Huw Ayling-Miller
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class DefaultWebClientProvider implements WebClientProvider {
private final Map<InetSocketAddress, WebClient> cachedClients;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.document;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -41,7 +41,11 @@ import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.ResponseConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.document.Explanation;
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.mapping.MappingException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -62,7 +66,9 @@ import com.fasterxml.jackson.core.JsonGenerator;
* @author Roman Puchkovskiy
* @author Matt Gilene
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public final class DocumentAdapters {
private DocumentAdapters() {}
@@ -711,10 +717,9 @@ public final class DocumentAdapters {
if (this == o) {
return true;
}
if (!(o instanceof SearchDocumentAdapter)) {
if (!(o instanceof SearchDocumentAdapter that)) {
return false;
}
SearchDocumentAdapter that = (SearchDocumentAdapter) o;
return Float.compare(that.score, score) == 0 && delegate.equals(that.delegate);
}
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.search.aggregations.Aggregation;
import org.springframework.data.elasticsearch.core.AggregationContainer;
import org.springframework.lang.NonNull;
/**
@@ -23,7 +24,9 @@ import org.springframework.lang.NonNull;
*
* @author Peter-Josef Meisch
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
private final Aggregation aggregation;
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.search.aggregations.Aggregations;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.lang.NonNull;
/**
@@ -23,7 +24,9 @@ import org.springframework.lang.NonNull;
*
* @author Peter-Josef Meisch
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchAggregations implements AggregationsContainer<Aggregations> {
private final Aggregations aggregations;
@@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.cluster;
package org.springframework.data.elasticsearch.client.erhlc;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.util.Assert;
/**
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchClusterOperations {
/**
* Creates a ClusterOperations for a {@link ElasticsearchRestTemplate}.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.IOException;
import java.util.List;
@@ -44,7 +44,9 @@ import org.springframework.util.StringUtils;
* @author Roman Puchkovskiy
* @author Mark Paluch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchExceptionTranslator implements PersistenceExceptionTranslator {
@Override
@@ -54,17 +56,14 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
}
if (ex instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) ex;
if (ex instanceof ElasticsearchException elasticsearchException) {
if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
ex);
}
if (elasticsearchException instanceof ElasticsearchStatusException) {
ElasticsearchStatusException elasticsearchStatusException = (ElasticsearchStatusException) elasticsearchException;
if (elasticsearchException instanceof ElasticsearchStatusException elasticsearchStatusException) {
return new RestStatusException(elasticsearchStatusException.status().getStatus(),
elasticsearchStatusException.getMessage(), elasticsearchStatusException);
}
@@ -72,11 +71,9 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
return new UncategorizedElasticsearchException(ex.getMessage(), ex);
}
if (ex instanceof RestStatusException) {
RestStatusException restStatusException = (RestStatusException) ex;
if (ex instanceof RestStatusException restStatusException) {
Throwable cause = restStatusException.getCause();
if (cause instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) cause;
if (cause instanceof ElasticsearchException elasticsearchException) {
if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
@@ -102,16 +99,14 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
Integer status = null;
String message = null;
if (exception instanceof ElasticsearchStatusException) {
if (exception instanceof ElasticsearchStatusException statusException) {
ElasticsearchStatusException statusException = (ElasticsearchStatusException) exception;
status = statusException.status().getStatus();
message = statusException.getMessage();
}
if (exception instanceof RestStatusException) {
if (exception instanceof RestStatusException statusException) {
RestStatusException statusException = (RestStatusException) exception;
status = statusException.getStatus();
message = statusException.getMessage();
}
@@ -121,9 +116,7 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
&& message.contains("version conflict, required seqNo");
}
if (exception instanceof VersionConflictEngineException) {
VersionConflictEngineException versionConflictEngineException = (VersionConflictEngineException) exception;
if (exception instanceof VersionConflictEngineException versionConflictEngineException) {
return versionConflictEngineException.getMessage() != null
&& versionConflictEngineException.getMessage().contains("version conflict, required seqNo");
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.IOException;
import java.util.ArrayList;
@@ -57,17 +57,20 @@ import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchScrollHits;
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
import org.springframework.data.elasticsearch.core.cluster.ElasticsearchClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponseBuilder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
@@ -110,7 +113,9 @@ import org.springframework.util.Assert;
* @author Farid Faoudi
* @author Sijia Liu
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchRestTemplate.class);
@@ -454,7 +459,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
}
@Override
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(suggestion, index);
return execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
@@ -607,7 +611,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
// region helper methods
@Override
protected String getClusterVersion() {
public String getClusterVersion() {
try {
return execute(client -> client.info(RequestOptions.DEFAULT)).getVersion().getNumber();
} catch (Exception ignored) {}
@@ -629,16 +633,15 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
}
@Override
protected String getVendor() {
public String getVendor() {
return "Elasticsearch";
}
@Override
protected String getRuntimeLibraryVersion() {
public String getRuntimeLibraryVersion() {
return Version.CURRENT.toString();
}
@Override
@Deprecated
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
return suggest(suggestion, getIndexCoordinatesFor(clazz));
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.query;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.Arrays;
import java.util.stream.Collectors;
@@ -35,7 +35,9 @@ import org.springframework.util.StringUtils;
* Converts the {@link Highlight} annotation from a method to an Elasticsearch 7 {@link HighlightBuilder}.
*
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
@Deprecated
public class HighlightQueryBuilder {
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
@@ -131,9 +133,8 @@ public class HighlightQueryBuilder {
builder.highlighterType(parameters.getType());
}
if (builder instanceof HighlightBuilder && parameters instanceof HighlightParameters) {
HighlightBuilder highlightBuilder = (HighlightBuilder) builder;
HighlightParameters highlightParameters = (HighlightParameters) parameters;
if (builder instanceof HighlightBuilder highlightBuilder
&& parameters instanceof HighlightParameters highlightParameters) {
if (StringUtils.hasLength(highlightParameters.getEncoder())) {
highlightBuilder.encoder(highlightParameters.getEncoder());
@@ -144,9 +145,8 @@ public class HighlightQueryBuilder {
}
}
if (builder instanceof HighlightBuilder.Field && parameters instanceof HighlightFieldParameters) {
HighlightBuilder.Field field = (HighlightBuilder.Field) builder;
HighlightFieldParameters fieldParameters = (HighlightFieldParameters) parameters;
if (builder instanceof HighlightBuilder.Field field
&& parameters instanceof HighlightFieldParameters fieldParameters) {
if ((fieldParameters).getFragmentOffset() > -1) {
field.fragmentOffset(fieldParameters.getFragmentOffset());
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Mono;
@@ -24,7 +24,7 @@ import java.util.function.Supplier;
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
import org.springframework.data.elasticsearch.client.NoReachableHostException;
import org.springframework.http.HttpHeaders;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
@@ -36,7 +36,9 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public interface HostProvider<T extends HostProvider<T>> {
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -45,7 +45,9 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class MultiNodeHostProvider implements HostProvider<MultiNodeHostProvider> {
private final static Log LOGGER = LogFactory.getLog(MultiNodeHostProvider.class);
@@ -68,7 +70,7 @@ class MultiNodeHostProvider implements HostProvider<MultiNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#clusterInfo()
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#clusterInfo()
*/
@Override
public Mono<ClusterInformation> clusterInfo() {
@@ -78,7 +80,7 @@ class MultiNodeHostProvider implements HostProvider<MultiNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#createWebClient(java.net.InetSocketAddress)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#createWebClient(java.net.InetSocketAddress)
*/
@Override
public WebClient createWebClient(InetSocketAddress endpoint) {
@@ -87,7 +89,7 @@ class MultiNodeHostProvider implements HostProvider<MultiNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification)
*/
@Override
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.util;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.HashMap;
import java.util.List;
@@ -78,7 +78,6 @@ import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
import org.elasticsearch.xcontent.ContextParser;
import org.elasticsearch.xcontent.NamedXContentRegistry;
import org.elasticsearch.xcontent.ParseField;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
/**
* <p>
@@ -92,7 +91,9 @@ import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsea
*
* @author Russell Parry
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public class NamedXContents {
private NamedXContents() {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.query;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,6 +28,8 @@ import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.IndexBoost;
import org.springframework.lang.Nullable;
/**
@@ -43,7 +45,9 @@ import org.springframework.lang.Nullable;
* @author Martin Choraine
* @author Peter-Josef Meisch
* @author Sijia Liu
* @deprecated since 5.0
*/
@Deprecated
public class NativeSearchQuery extends BaseQuery {
@Nullable private final QueryBuilder query;
@@ -55,7 +59,6 @@ public class NativeSearchQuery extends BaseQuery {
@Nullable private List<PipelineAggregationBuilder> pipelineAggregations;
@Nullable private HighlightBuilder highlightBuilder;
@Nullable private HighlightBuilder.Field[] highlightFields;
@Nullable private List<IndexBoost> indicesBoost;
@Nullable private SearchTemplateRequestBuilder searchTemplate;
@Nullable private SuggestBuilder suggestBuilder;
@Nullable private List<SearchExtBuilder> searchExtBuilders;
@@ -182,11 +185,6 @@ public class NativeSearchQuery extends BaseQuery {
this.pipelineAggregations = pipelineAggregationBuilders;
}
@Nullable
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
public void setIndicesBoost(List<IndexBoost> indicesBoost) {
this.indicesBoost = indicesBoost;
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.query;
package org.springframework.data.elasticsearch.client.erhlc;
import static org.springframework.util.CollectionUtils.*;
@@ -32,6 +32,10 @@ import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.lang.Nullable;
/**
@@ -50,7 +54,9 @@ import org.springframework.lang.Nullable;
* @author Peer Mueller
* @author vdisk
* @author owen.qq
* @deprecated since 5.0
*/
@Deprecated
public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery, NativeSearchQueryBuilder> {
@Nullable private QueryBuilder queryBuilder;
@@ -63,9 +69,7 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
@Nullable private List<HighlightBuilder.Field> highlightFields = new ArrayList<>();
@Nullable protected List<String> storedFields;
@Nullable private CollapseBuilder collapseBuilder;
@Nullable private List<IndexBoost> indicesBoost = new ArrayList<>();
@Nullable private SearchTemplateRequestBuilder searchTemplateBuilder;
@Nullable private String route;
@Nullable private SearchType searchType;
@Nullable private Boolean trackTotalHits;
@Nullable private Duration timeout;
@@ -195,19 +199,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
return this;
}
public NativeSearchQueryBuilder withIndicesBoost(Collection<IndexBoost> indicesBoost) {
this.indicesBoost.addAll(indicesBoost);
return this;
}
/**
* @since 4.3
*/
public NativeSearchQueryBuilder withIndicesBoost(IndexBoost... indicesBoost) {
Collections.addAll(this.indicesBoost, indicesBoost);
return this;
}
public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder) {
this.searchTemplateBuilder = searchTemplateBuilder;
return this;
@@ -232,11 +223,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
return this;
}
public NativeSearchQueryBuilder withRoute(String route) {
this.route = route;
return this;
}
public NativeSearchQueryBuilder withSearchType(SearchType searchType) {
this.searchType = searchType;
return this;
@@ -296,10 +282,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
nativeSearchQuery.setStoredFields(storedFields);
}
if (indicesBoost != null) {
nativeSearchQuery.setIndicesBoost(indicesBoost);
}
if (searchTemplateBuilder != null) {
nativeSearchQuery.setSearchTemplate(searchTemplateBuilder);
}
@@ -320,10 +302,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
nativeSearchQuery.setPipelineAggregations(pipelineAggregationBuilders);
}
if (route != null) {
nativeSearchQuery.setRoute(route);
}
if (searchType != null) {
nativeSearchQuery.setSearchType(Query.SearchType.valueOf(searchType.name()));
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Mono;
@@ -21,7 +21,7 @@ import java.io.IOException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.client.ClientResponse;
@@ -32,8 +32,11 @@ import org.springframework.web.reactive.function.client.ClientResponse;
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Mark Paluch
* @author Oliver Drotbohm
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class RawActionResponse extends ActionResponse {
private final ClientResponse delegate;
@@ -46,7 +49,7 @@ class RawActionResponse extends ActionResponse {
return new RawActionResponse(response);
}
public HttpStatus statusCode() {
public HttpStatusCode statusCode() {
return delegate.statusCode();
}
@@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.cluster;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Mono;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
/**
* Reactive Elasticsearch operations on cluster level.
*
* @author Peter-Josef Meisch
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
public interface ReactiveClusterOperations {
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -81,7 +81,9 @@ import org.springframework.web.reactive.function.client.WebClient;
* @since 3.2
* @see ClientConfiguration
* @see ReactiveRestClients
* @deprecated since 5.0
*/
@Deprecated
public interface ReactiveElasticsearchClient {
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -43,23 +43,25 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.cluster.DefaultReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.AbstractReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.AggregationContainer;
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.SearchHitMapping;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponseBuilder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
@@ -83,7 +85,9 @@ import org.springframework.util.Assert;
* @author Farid Faoudi
* @author Sijia Liu
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearchTemplate {
private final ReactiveElasticsearchClient client;
@@ -277,8 +281,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
*/
protected Mono<GetResult> doGet(GetRequest request) {
return Mono.from(execute(client -> client.get(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
return Mono.from(execute(client -> client.get(request)));
}
protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index) {
@@ -558,26 +561,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
.onErrorResume(NoSuchIndexException.class, it -> Flux.empty()).map(ElasticsearchAggregation::new);
}
@Override
@Deprecated
public Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType) {
return doSuggest(suggestion, getIndexCoordinatesFor(entityType));
}
@Override
@Deprecated
public Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index) {
return doSuggest(suggestion, index);
}
@Deprecated
private Flux<org.elasticsearch.search.suggest.Suggest> doSuggest(SuggestBuilder suggestion, IndexCoordinates index) {
return Flux.defer(() -> {
SearchRequest request = requestFactory.searchRequest(suggestion, index);
return Flux.from(execute(client -> client.suggest(request)));
});
}
protected Mono<Long> doCount(Query query, Class<?> entityType, IndexCoordinates index) {
return Mono.defer(() -> {
@@ -633,8 +616,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
QUERY_LOGGER.debug(String.format("Executing doCount: %s", request));
}
return Mono.from(execute(client -> client.count(request))) //
.onErrorResume(NoSuchIndexException.class, it -> Mono.just(0L));
return Mono.from(execute(client -> client.count(request)));
}
/**
@@ -678,7 +660,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
// region Helper methods
@Override
protected Mono<String> getClusterVersion() {
public Mono<String> getClusterVersion() {
try {
return Mono.from(execute(ReactiveElasticsearchClient::info))
.map(mainResponse -> mainResponse.getVersion().toString());
@@ -691,7 +673,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
* @since 4.3
*/
@Override
protected Mono<String> getVendor() {
public Mono<String> getVendor() {
return Mono.just("Elasticsearch");
}
@@ -700,7 +682,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
* @since 4.3
*/
@Override
protected Mono<String> getRuntimeLibraryVersion() {
public Mono<String> getRuntimeLibraryVersion() {
return Mono.just(Version.CURRENT.toString());
}
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import static org.elasticsearch.client.Requests.*;
import static org.springframework.util.StringUtils.*;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.ReactiveResourceUtil;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -62,7 +66,9 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @author George Popides
* @since 4.1
* @deprecated since 5.0
*/
@Deprecated
class ReactiveIndexTemplate implements ReactiveIndexOperations {
private static final Log LOGGER = LogFactory.getLog(ReactiveIndexTemplate.class);
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.function.Function;
@@ -29,7 +29,9 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Mark Paluch
* @author Roman Puchkovskiy
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public final class ReactiveRestClients {
private ReactiveRestClients() {}
@@ -70,7 +72,9 @@ public final class ReactiveRestClients {
* the ReactiveElasticsearchClient with a {@link WebClient}
*
* @since 4.3
* @deprecated
*/
@Deprecated
public interface WebClientConfigurationCallback extends ClientConfiguration.ClientConfigurationCallback<WebClient> {
static WebClientConfigurationCallback from(Function<WebClient, WebClient> webClientCallback) {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import org.springframework.web.reactive.function.client.WebClientException;
@@ -23,8 +23,10 @@ import org.springframework.web.reactive.function.client.WebClientException;
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.2
* @deprecated since 5.0
*/
public class RequestBodyEncodingException extends WebClientException {
@Deprecated public class RequestBodyEncodingException extends WebClientException {
private static final long serialVersionUID = 472776714118912855L;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.util;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -101,7 +101,6 @@ import org.elasticsearch.xcontent.XContent;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
@@ -120,7 +119,9 @@ import org.springframework.lang.Nullable;
* @author Peter-Josef Meisch
* @author Farid Faoudi
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
@SuppressWarnings("JavadocReference")
public class RequestConverters {
@@ -336,8 +337,13 @@ public class RequestConverters {
public static Request index(IndexRequest indexRequest) {
String method = Strings.hasLength(indexRequest.id()) ? HttpMethod.PUT.name() : HttpMethod.POST.name();
boolean isCreate = (indexRequest.opType() == DocWriteRequest.OpType.CREATE);
String endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(),
isCreate ? "_create" : null);
String endpoint;
if (indexRequest.opType() == DocWriteRequest.OpType.CREATE) {
endpoint = indexRequest.type().equals("_doc") ? endpoint(indexRequest.index(), "_create", indexRequest.id())
: endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(), "_create");
} else {
endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id());
}
Request request = new Request(method, endpoint);
Params parameters = new Params(request);
@@ -362,7 +368,9 @@ public class RequestConverters {
}
public static Request update(UpdateRequest updateRequest) {
String endpoint = endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
String endpoint = updateRequest.type().equals("_doc")
? endpoint(updateRequest.index(), "_update", updateRequest.id())
: endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
Request request = new Request(HttpMethod.POST.name(), endpoint);
Params parameters = new Params(request);
@@ -500,8 +508,11 @@ public class RequestConverters {
}
public static Request explain(ExplainRequest explainRequest) {
String endpoint = explainRequest.type().equals("_doc")
? endpoint(explainRequest.index(), "_explain", explainRequest.id())
: endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain");
Request request = new Request(HttpMethod.GET.name(),
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain"));
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), endpoint));
Params params = new Params(request);
params.withStoredFields(explainRequest.storedFields());
@@ -815,7 +826,7 @@ public class RequestConverters {
}
public static Request indexCreate(org.elasticsearch.client.indices.CreateIndexRequest createIndexRequest) {
String endpoint = RequestConverters.endpoint(new String[] { createIndexRequest.index() });
String endpoint = RequestConverters.endpoint(createIndexRequest.index());
Request request = new Request(HttpMethod.PUT.name(), endpoint);
Params parameters = new Params(request);
@@ -1047,7 +1058,7 @@ public class RequestConverters {
return new EndpointBuilder().addPathPart(index, type, id).addPathPartAsIs(endpoint).build();
}
static String endpoint(String[] indices) {
static String endpoint(String... indices) {
return new EndpointBuilder().addCommaSeparatedPathParts(indices).build();
}
@@ -1060,7 +1071,7 @@ public class RequestConverters {
.addPathPartAsIs(endpoint).build();
}
static String endpoint(String[] indices, String endpoint, String[] suffixes) {
static String endpoint(String[] indices, String endpoint, String... suffixes) {
return new EndpointBuilder().addCommaSeparatedPathParts(indices).addPathPartAsIs(endpoint)
.addCommaSeparatedPathParts(suffixes).build();
}
@@ -1128,7 +1139,7 @@ public class RequestConverters {
return this;
}
Params withFields(String[] fields) {
Params withFields(String... fields) {
if (fields != null && fields.length > 0) {
return putParam("fields", String.join(",", fields));
}
@@ -1189,7 +1200,7 @@ public class RequestConverters {
return putParam("routing", routing);
}
Params withStoredFields(String[] storedFields) {
Params withStoredFields(String... storedFields) {
if (storedFields != null && storedFields.length > 0) {
return putParam("stored_fields", String.join(",", storedFields));
}
@@ -1312,14 +1323,14 @@ public class RequestConverters {
return putParam("wait_for_completion", waitForCompletion.toString());
}
Params withNodes(String[] nodes) {
Params withNodes(String... nodes) {
if (nodes != null && nodes.length > 0) {
return putParam("nodes", String.join(",", nodes));
}
return this;
}
Params withActions(String[] actions) {
Params withActions(String... actions) {
if (actions != null && actions.length > 0) {
return putParam("actions", String.join(",", actions));
}
@@ -1422,7 +1433,7 @@ public class RequestConverters {
return this;
}
EndpointBuilder addCommaSeparatedPathParts(String[] parts) {
EndpointBuilder addCommaSeparatedPathParts(String... parts) {
addPathPart(String.join(",", parts));
return this;
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.IOException;
import java.util.function.Function;
@@ -53,14 +53,15 @@ import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.mustache.SearchTemplateRequest;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.util.RequestConverters;
/**
* @author Roman Puchkovskiy
* @author Farid Faoudi
* @author George Popides
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public interface RequestCreator {
default Function<SearchRequest, Request> search() {
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import static org.elasticsearch.core.TimeValue.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
@@ -91,6 +91,8 @@ import org.elasticsearch.xcontent.ToXContent;
import org.elasticsearch.xcontent.XContentBuilder;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.ScriptType;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasAction;
@@ -127,7 +129,9 @@ import org.springframework.util.StringUtils;
* @author Sijia Liu
* @author Peter Nowak
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
class RequestFactory {
// the default max result window size of Elasticsearch
@@ -162,8 +166,7 @@ class RequestFactory {
IndicesAliasesRequest.AliasActions aliasActionsES = null;
if (aliasAction instanceof AliasAction.Add) {
AliasAction.Add add = (AliasAction.Add) aliasAction;
if (aliasAction instanceof AliasAction.Add add) {
IndicesAliasesRequest.AliasActions addES = IndicesAliasesRequest.AliasActions.add();
AliasActionParameters parameters = add.getParameters();
@@ -189,8 +192,7 @@ class RequestFactory {
}
aliasActionsES = addES;
} else if (aliasAction instanceof AliasAction.Remove) {
AliasAction.Remove remove = (AliasAction.Remove) aliasAction;
} else if (aliasAction instanceof AliasAction.Remove remove) {
IndicesAliasesRequest.AliasActions removeES = IndicesAliasesRequest.AliasActions.remove();
AliasActionParameters parameters = remove.getParameters();
@@ -198,8 +200,7 @@ class RequestFactory {
removeES.aliases(parameters.getAliases());
aliasActionsES = removeES;
} else if (aliasAction instanceof AliasAction.RemoveIndex) {
AliasAction.RemoveIndex removeIndex = (AliasAction.RemoveIndex) aliasAction;
} else if (aliasAction instanceof AliasAction.RemoveIndex removeIndex) {
IndicesAliasesRequest.AliasActions removeIndexES = IndicesAliasesRequest.AliasActions.removeIndex();
AliasActionParameters parameters = removeIndex.getParameters();
@@ -231,7 +232,7 @@ class RequestFactory {
}
if (bulkOptions.getWaitForActiveShards() != null) {
bulkRequest.waitForActiveShards(ActiveShardCount.from(bulkOptions.getWaitForActiveShards().getValue()));
bulkRequest.waitForActiveShards(ActiveShardCount.from(bulkOptions.getWaitForActiveShards().value()));
}
if (bulkOptions.getPipeline() != null) {
@@ -586,9 +587,9 @@ class RequestFactory {
String indexName = index.getIndexName();
for (Query.IdWithRouting idWithRouting : searchQuery.getIdsWithRouting()) {
MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, idWithRouting.getId());
if (idWithRouting.getRouting() != null) {
item = item.routing(idWithRouting.getRouting());
MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, idWithRouting.id());
if (idWithRouting.routing() != null) {
item = item.routing(idWithRouting.routing());
}
// note: multiGet does not have fields, need to set sourceContext to filter
@@ -613,7 +614,7 @@ class RequestFactory {
Object queryObject = query.getObject();
if (queryObject != null) {
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
// If we have a query id and a document id, do not ask ES to generate one.
if (id != null) {
indexRequest = new IndexRequest(indexName).id(id);
@@ -649,6 +650,7 @@ class RequestFactory {
}
if (query.getOpType() != null) {
switch (query.getOpType()) {
case INDEX:
indexRequest.opType(DocWriteRequest.OpType.INDEX);
@@ -673,8 +675,7 @@ class RequestFactory {
if (highlightBuilder == null) {
if (query instanceof NativeSearchQuery) {
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
if (query instanceof NativeSearchQuery searchQuery) {
if ((searchQuery.getHighlightFields() != null && searchQuery.getHighlightFields().length > 0)
|| searchQuery.getHighlightBuilder() != null) {
@@ -864,9 +865,8 @@ class RequestFactory {
if (!query.getRuntimeFields().isEmpty()) {
Map<String, Object> runtimeMappings = new HashMap<>();
query.getRuntimeFields().forEach(runtimeField -> {
runtimeMappings.put(runtimeField.getName(), runtimeField.getMapping());
});
query.getRuntimeFields()
.forEach(runtimeField -> runtimeMappings.put(runtimeField.getName(), runtimeField.getMapping()));
sourceBuilder.runtimeMappings(runtimeMappings);
}
@@ -921,8 +921,7 @@ class RequestFactory {
query.getSort().forEach(order -> sourceBuilder.sort(getSortBuilder(order, entity)));
}
if (query instanceof NativeSearchQuery) {
NativeSearchQuery nativeSearchQuery = (NativeSearchQuery) query;
if (query instanceof NativeSearchQuery nativeSearchQuery) {
List<SortBuilder<?>> sorts = nativeSearchQuery.getElasticsearchSorts();
if (sorts != null) {
sorts.forEach(sourceBuilder::sort);
@@ -936,8 +935,7 @@ class RequestFactory {
Order.Mode mode = Order.DEFAULT_MODE;
String unmappedType = null;
if (order instanceof Order) {
Order o = (Order) order;
if (order instanceof Order o) {
mode = o.getMode();
unmappedType = o.getUnmappedType();
}
@@ -952,8 +950,7 @@ class RequestFactory {
: null;
String fieldName = property != null ? property.getFieldName() : order.getProperty();
if (order instanceof GeoDistanceOrder) {
GeoDistanceOrder geoDistanceOrder = (GeoDistanceOrder) order;
if (order instanceof GeoDistanceOrder geoDistanceOrder) {
GeoDistanceSortBuilder sort = SortBuilders.geoDistanceSort(fieldName, geoDistanceOrder.getGeoPoint().getLat(),
geoDistanceOrder.getGeoPoint().getLon());
@@ -1027,7 +1024,14 @@ class RequestFactory {
if (params == null) {
params = new HashMap<>();
}
Script script = new Script(getScriptType(query.getScriptType()), query.getLang(), query.getScript(), params);
org.elasticsearch.script.ScriptType scriptType = getScriptType(query.getScriptType());
String lang = query.getLang();
if (scriptType == org.elasticsearch.script.ScriptType.INLINE && lang == null) {
lang = "painless";
}
Script script = new Script(scriptType, lang, query.getScript(), params);
updateRequest.script(script);
}
@@ -1092,8 +1096,7 @@ class RequestFactory {
public UpdateByQueryRequest updateByQueryRequest(UpdateQuery query, IndexCoordinates index) {
String indexName = index.getIndexName();
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(indexName);
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(index.getIndexNames());
updateByQueryRequest.setScript(getScript(query));
if (query.getAbortOnVersionConflict() != null) {
@@ -1168,14 +1171,11 @@ class RequestFactory {
private QueryBuilder getQuery(Query query) {
QueryBuilder elasticsearchQuery;
if (query instanceof NativeSearchQuery) {
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
if (query instanceof NativeSearchQuery searchQuery) {
elasticsearchQuery = searchQuery.getQuery();
} else if (query instanceof CriteriaQuery) {
CriteriaQuery criteriaQuery = (CriteriaQuery) query;
} else if (query instanceof CriteriaQuery criteriaQuery) {
elasticsearchQuery = new CriteriaQueryProcessor().createQuery(criteriaQuery.getCriteria());
} else if (query instanceof StringQuery) {
StringQuery stringQuery = (StringQuery) query;
} else if (query instanceof StringQuery stringQuery) {
elasticsearchQuery = wrapperQuery(stringQuery.getSource());
} else {
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
@@ -1188,11 +1188,9 @@ class RequestFactory {
private QueryBuilder getFilter(Query query) {
QueryBuilder elasticsearchFilter;
if (query instanceof NativeSearchQuery) {
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
if (query instanceof NativeSearchQuery searchQuery) {
elasticsearchFilter = searchQuery.getFilter();
} else if (query instanceof CriteriaQuery) {
CriteriaQuery criteriaQuery = (CriteriaQuery) query;
} else if (query instanceof CriteriaQuery criteriaQuery) {
elasticsearchFilter = new CriteriaFilterProcessor().createFilter(criteriaQuery.getCriteria());
} else if (query instanceof StringQuery) {
elasticsearchFilter = null;
@@ -1204,15 +1202,11 @@ class RequestFactory {
}
public static WriteRequest.RefreshPolicy toElasticsearchRefreshPolicy(RefreshPolicy refreshPolicy) {
switch (refreshPolicy) {
case IMMEDIATE:
return WriteRequest.RefreshPolicy.IMMEDIATE;
case WAIT_UNTIL:
return WriteRequest.RefreshPolicy.WAIT_UNTIL;
case NONE:
default:
return WriteRequest.RefreshPolicy.NONE;
}
return switch (refreshPolicy) {
case IMMEDIATE -> WriteRequest.RefreshPolicy.IMMEDIATE;
case WAIT_UNTIL -> WriteRequest.RefreshPolicy.WAIT_UNTIL;
case NONE -> WriteRequest.RefreshPolicy.NONE;
};
}
@Nullable
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.ArrayList;
import java.util.Collections;
@@ -40,6 +40,8 @@ import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.ScrollableHitSource;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasData;
@@ -57,7 +59,9 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @author Sijia Liu
* @since 4.2
* @deprecated since 5.0
*/
@Deprecated
public class ResponseConverter {
private ResponseConverter() {}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client;
package org.springframework.data.elasticsearch.client.erhlc;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@@ -43,8 +43,10 @@ import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.elasticsearch.client.RestHighLevelClientBuilder;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
/**
@@ -58,7 +60,9 @@ import org.springframework.util.Assert;
* @author Peter-Josef Meisch
* @author Nic Hines
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
public final class RestClients {
/**
@@ -123,8 +127,7 @@ public final class RestClients {
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
.getClientConfigurers()) {
if (clientConfigurer instanceof RestClientConfigurationCallback) {
RestClientConfigurationCallback restClientConfigurationCallback = (RestClientConfigurationCallback) clientConfigurer;
if (clientConfigurer instanceof RestClientConfigurationCallback restClientConfigurationCallback) {
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
}
}
@@ -132,7 +135,7 @@ public final class RestClients {
return clientBuilder;
});
RestHighLevelClient client = new RestHighLevelClient(builder);
RestHighLevelClient client = new RestHighLevelClientBuilder(builder.build()).setApiCompatibilityMode(true).build();
return () -> client;
}
@@ -194,9 +197,9 @@ public final class RestClients {
context.setAttribute(RestClients.LOG_ID_ATTRIBUTE, logId);
}
if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) {
if (request instanceof HttpEntityEnclosingRequest entityRequest
&& ((HttpEntityEnclosingRequest) request).getEntity() != null) {
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
entity.writeTo(buffer);
@@ -215,7 +218,7 @@ public final class RestClients {
@Override
public void process(HttpResponse response, HttpContext context) {
String logId = (String) context.getAttribute(RestClients.LOG_ID_ATTRIBUTE);
ClientLogger.logRawResponse(logId, HttpStatus.resolve(response.getStatusLine().getStatusCode()));
ClientLogger.logRawResponse(logId, response.getStatusLine().getStatusCode());
}
}
@@ -236,7 +239,7 @@ public final class RestClients {
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (httpHeaders != null && httpHeaders != HttpHeaders.EMPTY) {
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
@@ -247,7 +250,9 @@ public final class RestClients {
* the RestClient with a {@link HttpAsyncClientBuilder}
*
* @since 4.3
* @deprecated since 5.0
*/
@Deprecated
public interface RestClientConfigurationCallback
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
@@ -13,23 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.config;
package org.springframework.data.elasticsearch.client.erhlc;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.elasticsearch.client.RestClientFactoryBean;
import org.w3c.dom.Element;
/**
* @author Don Wellington
* @deprecated since 5.0
*/
public class RestClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Deprecated
public class RestHighLevelClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RestClientFactoryBean.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RestHighLevelClientFactoryBean.class);
setConfigurations(element, builder);
return getSourcedBeanDefinition(builder, element, parserContext);
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client;
package org.springframework.data.elasticsearch.client.erhlc;
import java.net.URL;
import java.util.ArrayList;
@@ -31,14 +31,17 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* RestClientFactoryBean
* RestHighLevelClientFactoryBean
*
* @author Don Wellington
* @author Peter-Josef Meisch
* @deprecated since 5.0
*/
public class RestClientFactoryBean implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
@Deprecated
public class RestHighLevelClientFactoryBean
implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
private static final Log LOGGER = LogFactory.getLog(RestClientFactoryBean.class);
private static final Log LOGGER = LogFactory.getLog(RestHighLevelClientFactoryBean.class);
private @Nullable RestHighLevelClient client;
private String hosts = "http://localhost:9200";
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.Collections;
import java.util.List;
@@ -40,6 +40,9 @@ import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.springframework.data.elasticsearch.core.AbstractIndexTemplate;
import org.springframework.data.elasticsearch.core.IndexInformation;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
@@ -60,7 +63,9 @@ import org.springframework.util.Assert;
* @author Sascha Woo
* @author George Popides
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
class RestIndexTemplate extends AbstractIndexTemplate implements IndexOperations {
private static final Log LOGGER = LogFactory.getLog(RestIndexTemplate.class);
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.query;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.script.Script;
/**
* @author Ryan Murfitt
* @author Artur Konczak
* @deprecated since 5.0
*/
@Deprecated
public class ScriptField {
private final String fieldName;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.document;
package org.springframework.data.elasticsearch.client.erhlc;
import java.util.ArrayList;
import java.util.List;
@@ -26,7 +26,8 @@ import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregations;
import org.springframework.data.elasticsearch.core.ElasticsearchAggregations;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.PhraseSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.SortBy;
@@ -41,7 +42,9 @@ import org.springframework.util.Assert;
*
* @author Peter-Josef Meisch
* @since 4.0
* @deprecated since 5.0
*/
@Deprecated
public class SearchDocumentResponseBuilder {
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponse.class);
@@ -110,7 +113,8 @@ public class SearchDocumentResponseBuilder {
: null;
Suggest suggest = suggestFrom(suggestES, entityCreator);
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,
// no pointInTimeId for the deprecated implementation
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, null, searchDocuments,
aggregationsContainer, suggest);
}
@@ -126,8 +130,7 @@ public class SearchDocumentResponseBuilder {
for (org.elasticsearch.search.suggest.Suggest.Suggestion<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option>> suggestionES : suggestES) {
if (suggestionES instanceof org.elasticsearch.search.suggest.term.TermSuggestion) {
org.elasticsearch.search.suggest.term.TermSuggestion termSuggestionES = (org.elasticsearch.search.suggest.term.TermSuggestion) suggestionES;
if (suggestionES instanceof org.elasticsearch.search.suggest.term.TermSuggestion termSuggestionES) {
List<TermSuggestion.Entry> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.term.TermSuggestion.Entry entryES : termSuggestionES) {
@@ -147,8 +150,7 @@ public class SearchDocumentResponseBuilder {
suggestFrom(termSuggestionES.getSort())));
}
if (suggestionES instanceof org.elasticsearch.search.suggest.phrase.PhraseSuggestion) {
org.elasticsearch.search.suggest.phrase.PhraseSuggestion phraseSuggestionES = (org.elasticsearch.search.suggest.phrase.PhraseSuggestion) suggestionES;
if (suggestionES instanceof org.elasticsearch.search.suggest.phrase.PhraseSuggestion phraseSuggestionES) {
List<PhraseSuggestion.Entry> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry entryES : phraseSuggestionES) {
@@ -156,7 +158,7 @@ public class SearchDocumentResponseBuilder {
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry.Option optionES : entryES) {
options.add(new PhraseSuggestion.Entry.Option(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch()));
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch()));
}
entries.add(new PhraseSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
@@ -166,8 +168,7 @@ public class SearchDocumentResponseBuilder {
suggestions.add(new PhraseSuggestion(phraseSuggestionES.getName(), phraseSuggestionES.getSize(), entries));
}
if (suggestionES instanceof org.elasticsearch.search.suggest.completion.CompletionSuggestion) {
org.elasticsearch.search.suggest.completion.CompletionSuggestion completionSuggestionES = (org.elasticsearch.search.suggest.completion.CompletionSuggestion) suggestionES;
if (suggestionES instanceof org.elasticsearch.search.suggest.completion.CompletionSuggestion completionSuggestionES) {
List<CompletionSuggestion.Entry<T>> entries = new ArrayList<>();
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry entryES : completionSuggestionES) {
@@ -188,7 +189,7 @@ public class SearchDocumentResponseBuilder {
}
options.add(new CompletionSuggestion.Entry.Option<>(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch(),
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core;
package org.springframework.data.elasticsearch.client.erhlc;
import org.elasticsearch.search.SearchHits;
@@ -22,7 +22,9 @@ import org.elasticsearch.search.SearchHits;
*
* @author Peter-Josef Meisch
* @since 4.4
* @deprecated since 5.0
*/
@Deprecated
public final class SearchHitsUtil {
private SearchHitsUtil() {}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.client.reactive;
package org.springframework.data.elasticsearch.client.erhlc;
import reactor.core.publisher.Mono;
@@ -32,7 +32,9 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Mark Paluch
* @author Peter-Josef Meisch
* @since 3.2
* @deprecated since 5.0
*/
@Deprecated
class SingleNodeHostProvider implements HostProvider<SingleNodeHostProvider> {
private final WebClientProvider clientProvider;
@@ -48,7 +50,7 @@ class SingleNodeHostProvider implements HostProvider<SingleNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#clusterInfo()
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#clusterInfo()
*/
@Override
public Mono<ClusterInformation> clusterInfo() {
@@ -71,7 +73,7 @@ class SingleNodeHostProvider implements HostProvider<SingleNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#createWebClient(java.net.InetSocketAddress)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#createWebClient(java.net.InetSocketAddress)
*/
@Override
public WebClient createWebClient(InetSocketAddress endpoint) {
@@ -80,7 +82,7 @@ class SingleNodeHostProvider implements HostProvider<SingleNodeHostProvider> {
/*
* (non-Javadoc)
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification)
* @see org.springframework.data.elasticsearch.client.erhlc.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.erhlc.HostProvider.Verification)
*/
@Override
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {

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