Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bad6afdecc | |||
| a6a5e3f7f5 | |||
| b3a7a44db1 | |||
| ccf136c8e2 | |||
| 77da22598a | |||
| 86634ceb38 | |||
| 8377f64a8a | |||
| b549601d34 | |||
| 3298ba21ce | |||
| 18be558740 | |||
| 46cd4cd59e | |||
| a4ed7300d1 | |||
| b511756b2b | |||
| 68ba4cd39b | |||
| 6b39b39d35 | |||
| 4f4c99ec1f | |||
| 6cfb8eeea6 | |||
| c4910bf399 | |||
| c93039410a | |||
| 33c9180ccd | |||
| df5fd0b97c | |||
| 44a79093ce | |||
| cf135f4cdd | |||
| 954d8b0f97 | |||
| e71c66949a | |||
| acf02a1dc9 | |||
| 6ad449c9f4 | |||
| 94ebc40582 | |||
| ca39c1d24c | |||
| 2049726bcb | |||
| 7620222f89 | |||
| 1f1076aa8b | |||
| 3e950b8053 | |||
| 1c31a31e11 | |||
| 373be49f97 | |||
| 12b332223f | |||
| 47c0e186ec | |||
| 8b69a8a030 | |||
| 8bb0d8cb41 | |||
| 26a2f54c0f | |||
| 1754f59e70 | |||
| 8a166a1a40 | |||
| 5c029602b5 | |||
| 82a545912e |
Vendored
+4
-4
@@ -32,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'
|
||||
@@ -63,7 +63,7 @@ pipeline {
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
|
||||
+4
-4
@@ -1,4 +1,3 @@
|
||||
|
||||
image:https://spring.io/badges/spring-data-elasticsearch/ga.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start] image:https://spring.io/badges/spring-data-elasticsearch/snapshot.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start]
|
||||
|
||||
= Spring Data for Elasticsearch image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]]
|
||||
@@ -40,7 +39,7 @@ This means that a switch to this client version can only be done with the next m
|
||||
|
||||
=== Elasticsearch 8 cluster
|
||||
|
||||
It should be possible to use the Elasticsearch 7 client to access a cluster running version 8 by setting the appropriate aompatibility 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.
|
||||
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
|
||||
|
||||
@@ -191,7 +190,9 @@ Attach a link to your code or a compressed file containing your code.
|
||||
== Building from Source
|
||||
|
||||
You don’t 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]
|
||||
----
|
||||
@@ -205,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.
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.0.0-M5</version>
|
||||
<version>5.0.0-M6</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<version>3.0.0-M6</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,17 +18,21 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<!-- version of the RestHighLevelClient -->
|
||||
<elasticsearch-rhlc>7.17.4</elasticsearch-rhlc>
|
||||
<!-- version of the new ElasticsearchClient -->
|
||||
<elasticsearch-java>8.3.1</elasticsearch-java>
|
||||
<springdata.commons>3.0.0-M6</springdata.commons>
|
||||
|
||||
<log4j>2.17.2</log4j>
|
||||
<!-- 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-M5</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>
|
||||
|
||||
@@ -303,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-->
|
||||
@@ -328,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>
|
||||
|
||||
|
||||
@@ -29,15 +29,15 @@ 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 Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
|
||||
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 (Turing) | 5.0.x | 8.3.1 | 6.0.x | 3.0.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.xfootnote:oom[Out of maintenance] | 7.12.0 | 5.3.x | 2.5.x
|
||||
@@ -49,5 +49,5 @@ built and tested.
|
||||
| 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
|
||||
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming
|
||||
the usage of the <<elasticsearch.operations,ElasticsearchOperations interface>>.
|
||||
|
||||
@@ -38,16 +38,17 @@ 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.
|
||||
<.> 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.
|
||||
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
|
||||
@@ -81,21 +82,22 @@ 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 `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.
|
||||
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
|
||||
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
|
||||
@@ -231,12 +233,49 @@ 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.
|
||||
<.> a function configuring the low level REST client (the same for the imperative and reactive stack)
|
||||
<.> 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 the deprecated `RestHighLevelClient` and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
[[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.
|
||||
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>>
|
||||
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
|
||||
@@ -20,34 +27,57 @@ Elasticsearch, see <<elasticsearch-migration-guide-4.4-5.0.new-clients>>
|
||||
|
||||
==== 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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`.
|
||||
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`:
|
||||
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]
|
||||
@@ -101,8 +131,7 @@ With this configuration, the following beans will be available in the Spring app
|
||||
[[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:
|
||||
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]
|
||||
@@ -111,7 +140,7 @@ your application as Spring Data Elasticsearch does not pull it in automatically
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
<version>7.17.4</version>
|
||||
<version>7.17.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
@@ -122,4 +151,4 @@ your application as Spring Data Elasticsearch does not pull it in automatically
|
||||
----
|
||||
====
|
||||
|
||||
Make sure to specify the version 7.17.4 explicitly, otherwise maven will resolve to 8.3.1, and this does not exist.
|
||||
Make sure to specify the version 7.17.5 explicitly, otherwise maven will resolve to 8.3.3, and this does not exist.
|
||||
|
||||
@@ -121,7 +121,7 @@ Query searchQuery = NativeQuery.builder()
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
|
||||
SearchHitsIterator<SampleEntity> stream = elasticsearchOperations.searchForStream(searchQuery, SampleEntity.class,
|
||||
SearchHitsIterator<SampleEntity> stream = elasticsearchOperations.searchForStream(searchQuery, SampleEntity.class,
|
||||
index);
|
||||
|
||||
List<SampleEntity> sampleEntities = new ArrayList<>();
|
||||
@@ -134,7 +134,7 @@ 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 `AbstractElasticsearchTemplate` can be used (this is the base implementation for the
|
||||
the following methods of the `AbstractElasticsearchTemplate` can be used (this is the base implementation for the
|
||||
different `ElasticsearchOperations` implementations:
|
||||
|
||||
====
|
||||
@@ -275,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
|
||||
====
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
+1
-35
@@ -26,10 +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.erhlc.ReactiveRestClients;
|
||||
import org.springframework.data.elasticsearch.client.erhlc.RestClients;
|
||||
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;
|
||||
|
||||
@@ -171,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
|
||||
@@ -347,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.
|
||||
*
|
||||
|
||||
+7
-35
@@ -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,9 +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.erhlc.ReactiveRestClients;
|
||||
import org.springframework.data.elasticsearch.client.erhlc.RestClients;
|
||||
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;
|
||||
@@ -52,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;
|
||||
@@ -62,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)
|
||||
@@ -76,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;
|
||||
}
|
||||
|
||||
@@ -202,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) {
|
||||
@@ -251,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,8 +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.http.HttpStatusCode;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -127,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 HttpStatusCode 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +153,10 @@ public abstract class ClientLogger {
|
||||
* @param statusCode the HTTP status code.
|
||||
* @param body body content.
|
||||
*/
|
||||
public static void logResponse(String logId, HttpStatusCode 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,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));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-12
@@ -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;
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+5
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -13,27 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
|
||||
/**
|
||||
* values for the {@link DynamicMapping annotation}
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum DynamicMappingValue {
|
||||
True("true"), False("false"), Strict("strict");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
DynamicMappingValue(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
+19
-25
@@ -130,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.");
|
||||
@@ -176,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]);
|
||||
@@ -195,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.");
|
||||
|
||||
@@ -240,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 //
|
||||
@@ -329,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();
|
||||
|
||||
+2
-4
@@ -288,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 //
|
||||
@@ -310,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 //
|
||||
|
||||
+6
-7
@@ -73,7 +73,7 @@ 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));
|
||||
});
|
||||
|
||||
@@ -101,12 +101,12 @@ final class DocumentAdapters {
|
||||
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
|
||||
|
||||
Map<String, List<Object>> documentFields = new LinkedHashMap<>();
|
||||
hitFieldsAsMap.entrySet().forEach(entry -> {
|
||||
if (entry.getValue() instanceof List) {
|
||||
hitFieldsAsMap.forEach((key, value) -> {
|
||||
if (value instanceof List) {
|
||||
// noinspection unchecked
|
||||
documentFields.put(entry.getKey(), (List<Object>) entry.getValue());
|
||||
documentFields.put(key, (List<Object>) value);
|
||||
} else {
|
||||
documentFields.put(entry.getKey(), Collections.singletonList(entry.getValue()));
|
||||
documentFields.put(key, Collections.singletonList(value));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -117,8 +117,7 @@ final class DocumentAdapters {
|
||||
} 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 {
|
||||
|
||||
|
||||
+31
-15
@@ -18,45 +18,61 @@ 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<List<ElasticsearchAggregation>> {
|
||||
|
||||
private final List<ElasticsearchAggregation> aggregations;
|
||||
private final Map<String, ElasticsearchAggregation> aggregationsAsMap;
|
||||
|
||||
public ElasticsearchAggregations(List<ElasticsearchAggregation> aggregations) {
|
||||
public ElasticsearchAggregations(Map<String, Aggregate> aggregations) {
|
||||
|
||||
Assert.notNull(aggregations, "aggregations must not be null");
|
||||
|
||||
this.aggregations = aggregations;
|
||||
}
|
||||
aggregationsAsMap = new HashMap<>();
|
||||
aggregations.forEach((name, aggregate) -> aggregationsAsMap //
|
||||
.put(name, new ElasticsearchAggregation(new Aggregation(name, aggregate))));
|
||||
|
||||
/**
|
||||
* convenience constructor taking a map as it is returned from the new Elasticsearch client.
|
||||
*
|
||||
* @param aggregationsMap aggregate map
|
||||
*/
|
||||
public ElasticsearchAggregations(Map<String, Aggregate> aggregationsMap) {
|
||||
|
||||
Assert.notNull(aggregationsMap, "aggregationsMap must not be null");
|
||||
|
||||
aggregations = new ArrayList<>(aggregationsMap.size());
|
||||
aggregationsMap
|
||||
.forEach((name, aggregate) -> aggregations.add(new ElasticsearchAggregation(new Aggregation(name, aggregate))));
|
||||
this.aggregations = new ArrayList<>(aggregationsAsMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
@@ -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;
|
||||
}
|
||||
}
|
||||
+97
@@ -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;
|
||||
}
|
||||
}
|
||||
+27
-23
@@ -52,11 +52,9 @@ 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
|
||||
@@ -229,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;
|
||||
}
|
||||
|
||||
@@ -300,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);
|
||||
@@ -331,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,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);
|
||||
}
|
||||
}
|
||||
@@ -360,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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");
|
||||
|
||||
|
||||
+4
-8
@@ -31,7 +31,6 @@ import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
|
||||
@@ -72,14 +71,13 @@ 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) {
|
||||
|
||||
ErrorResponse response = elasticsearchException.response();
|
||||
|
||||
if (response.status() == HttpStatus.NOT_FOUND.value()
|
||||
&& "index_not_found_exception".equals(response.error().type())) {
|
||||
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());
|
||||
@@ -109,9 +107,7 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
Integer status = null;
|
||||
String message = null;
|
||||
|
||||
|
||||
if (exception instanceof ResponseException) {
|
||||
ResponseException responseException = (ResponseException) exception;
|
||||
if (exception instanceof ResponseException responseException) {
|
||||
status = responseException.getResponse().getStatusLine().getStatusCode();
|
||||
message = responseException.getMessage();
|
||||
} else if (exception.getCause() != null) {
|
||||
|
||||
+36
-16
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
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.ElasticsearchClient;
|
||||
@@ -28,6 +27,7 @@ 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;
|
||||
@@ -80,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);
|
||||
|
||||
@@ -275,11 +286,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
@Override
|
||||
public String getClusterVersion() {
|
||||
return execute(client -> {
|
||||
try (var ignored = DANGEROUS_disableRequiredPropertiesCheck(true)) {
|
||||
return client.info().version().number();
|
||||
}
|
||||
});
|
||||
return execute(client -> client.info().version().number());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -480,17 +487,30 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
/**
|
||||
* value class combining the information needed for a single query in a multisearch request.
|
||||
*/
|
||||
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
|
||||
|
||||
+3
-2
@@ -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) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@@ -26,7 +27,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.lang.Nullable;
|
||||
* Elasticsearch Client library.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.4
|
||||
*/
|
||||
public class NativeQuery extends BaseQuery {
|
||||
@@ -46,7 +47,7 @@ public class NativeQuery extends BaseQuery {
|
||||
@Nullable private Suggester suggester;
|
||||
@Nullable private FieldCollapse fieldCollapse;
|
||||
private List<ScriptedField> scriptedFields = Collections.emptyList();
|
||||
private List<RescorerQuery> rescorerQueries = Collections.emptyList();
|
||||
private List<SortOptions> sortOptions = Collections.emptyList();
|
||||
|
||||
public NativeQuery(NativeQueryBuilder builder) {
|
||||
super(builder);
|
||||
@@ -56,7 +57,7 @@ public class NativeQuery extends BaseQuery {
|
||||
this.suggester = builder.getSuggester();
|
||||
this.fieldCollapse = builder.getFieldCollapse();
|
||||
this.scriptedFields = builder.getScriptedFields();
|
||||
this.rescorerQueries = builder.getRescorerQueries();
|
||||
this.sortOptions = builder.getSortOptions();
|
||||
}
|
||||
|
||||
public NativeQuery(@Nullable Query query) {
|
||||
@@ -95,8 +96,7 @@ public class NativeQuery extends BaseQuery {
|
||||
return scriptedFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RescorerQuery> getRescorerQueries() {
|
||||
return rescorerQueries;
|
||||
public List<SortOptions> getSortOptions() {
|
||||
return sortOptions;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-8
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@@ -22,19 +23,21 @@ 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.RescorerQuery;
|
||||
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> {
|
||||
@@ -45,9 +48,10 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
@Nullable private Suggester suggester;
|
||||
@Nullable private FieldCollapse fieldCollapse;
|
||||
private final List<ScriptedField> scriptedFields = new ArrayList<>();
|
||||
private List<RescorerQuery> rescorerQueries = new ArrayList<>();
|
||||
private List<SortOptions> sortOptions = new ArrayList<>();
|
||||
|
||||
public NativeQueryBuilder() {}
|
||||
public NativeQueryBuilder() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Query getQuery() {
|
||||
@@ -77,8 +81,8 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return scriptedFields;
|
||||
}
|
||||
|
||||
public List<RescorerQuery> getRescorerQueries() {
|
||||
return rescorerQueries;
|
||||
public List<SortOptions> getSortOptions() {
|
||||
return sortOptions;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withQuery(Query query) {
|
||||
@@ -135,11 +139,31 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withResorerQuery(RescorerQuery resorerQuery) {
|
||||
public NativeQueryBuilder withSort(List<SortOptions> values) {
|
||||
|
||||
Assert.notNull(resorerQuery, "resorerQuery must not be null");
|
||||
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());
|
||||
|
||||
this.rescorerQueries.add(resorerQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+3
-1
@@ -17,6 +17,7 @@ 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;
|
||||
@@ -27,7 +28,8 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverte
|
||||
* @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,
|
||||
|
||||
+42
@@ -276,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
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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");
|
||||
|
||||
|
||||
+41
-17
@@ -30,6 +30,7 @@ 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;
|
||||
@@ -327,26 +328,25 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
Time scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll() : Time.of(t -> t.time("1m"));
|
||||
|
||||
Flux<ResponseBody<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
|
||||
state -> {
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client1 -> client1
|
||||
.search(searchRequest, EntityAsMap.class))) //
|
||||
.expand(entityAsMapSearchResponse -> {
|
||||
state -> Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client1 -> client1
|
||||
.search(searchRequest, EntityAsMap.class))) //
|
||||
.expand(entityAsMapSearchResponse -> {
|
||||
|
||||
state.updateScrollId(entityAsMapSearchResponse.scrollId());
|
||||
state.updateScrollId(entityAsMapSearchResponse.scrollId());
|
||||
|
||||
if (entityAsMapSearchResponse.hits() == null
|
||||
|| CollectionUtils.isEmpty(entityAsMapSearchResponse.hits().hits())) {
|
||||
return Mono.empty();
|
||||
}
|
||||
if (entityAsMapSearchResponse.hits() == null
|
||||
|| CollectionUtils.isEmpty(entityAsMapSearchResponse.hits().hits())) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return searchResponses.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits())
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
@@ -409,6 +409,30 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
});
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+220
-104
@@ -32,25 +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.MsearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateByQueryRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateRequest;
|
||||
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;
|
||||
@@ -100,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 {
|
||||
@@ -181,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 //
|
||||
@@ -211,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()));
|
||||
@@ -225,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())));
|
||||
@@ -434,7 +426,6 @@ class RequestConverter {
|
||||
* so the code needs to be duplicated.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public IndexRequest<?> documentIndexRequest(IndexQuery query, IndexCoordinates indexCoordinates,
|
||||
@Nullable RefreshPolicy refreshPolicy) {
|
||||
|
||||
@@ -474,12 +465,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,20 +609,18 @@ class RequestConverter {
|
||||
|
||||
Map<String, JsonData> params = new HashMap<>();
|
||||
|
||||
if (scriptData.getParams() != null) {
|
||||
scriptData.getParams().forEach((key, value) -> {
|
||||
params.put(key, JsonData.of(value, jsonpMapper));
|
||||
});
|
||||
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.getType() == ScriptType.INLINE) {
|
||||
if (scriptData.type() == ScriptType.INLINE) {
|
||||
sb.inline(is -> is //
|
||||
.lang(scriptData.getLanguage()) //
|
||||
.source(scriptData.getScript()) //
|
||||
.lang(scriptData.language()) //
|
||||
.source(scriptData.script()) //
|
||||
.params(params)); //
|
||||
} else if (scriptData.getType() == ScriptType.STORED) {
|
||||
} else if (scriptData.type() == ScriptType.STORED) {
|
||||
sb.stored(ss -> ss //
|
||||
.id(scriptData.getScript()) //
|
||||
.id(scriptData.script()) //
|
||||
.params(params) //
|
||||
);
|
||||
}
|
||||
@@ -658,7 +643,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
builder.waitForActiveShards(wasb -> wasb.count(bulkOptions.getWaitForActiveShards().getValue()));
|
||||
builder.waitForActiveShards(wasb -> wasb.count(bulkOptions.getWaitForActiveShards().value()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getPipeline() != null) {
|
||||
@@ -671,16 +656,14 @@ 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) {
|
||||
UpdateQuery updateQuery = (UpdateQuery) query;
|
||||
} else if (query instanceof UpdateQuery updateQuery) {
|
||||
ob.update(bulkUpdateOperation(updateQuery, indexCoordinates, refreshPolicy));
|
||||
}
|
||||
return ob.build();
|
||||
@@ -729,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());
|
||||
|
||||
@@ -885,9 +868,7 @@ class RequestConverter {
|
||||
Map<String, JsonData> params = new HashMap<>();
|
||||
|
||||
if (query.getParams() != null) {
|
||||
query.getParams().forEach((key, value) -> {
|
||||
params.put(key, JsonData.of(value, jsonpMapper));
|
||||
});
|
||||
query.getParams().forEach((key, value) -> params.put(key, JsonData.of(value, jsonpMapper)));
|
||||
}
|
||||
|
||||
uqb.script(sb -> {
|
||||
@@ -1030,6 +1011,7 @@ class RequestConverter {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
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);
|
||||
|
||||
@@ -1047,22 +1029,119 @@ class RequestConverter {
|
||||
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);
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(param.clazz());
|
||||
|
||||
var query = param.query();
|
||||
mrb.searches(sb -> sb //
|
||||
.header(h -> h //
|
||||
.index(Arrays.asList(param.index.getIndexNames())) //
|
||||
// todo #2156 add remaining flags for header
|
||||
) //
|
||||
.body(bb -> bb //
|
||||
.query(getQuery(param.query, param.clazz))//
|
||||
.seqNoPrimaryTerm(persistentEntity.hasSeqNoPrimaryTermProperty()).version(true)
|
||||
// todo #2156 add remaining flags for body
|
||||
.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;
|
||||
});
|
||||
}
|
||||
@@ -1077,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);
|
||||
}
|
||||
@@ -1118,10 +1211,6 @@ class RequestConverter {
|
||||
builder.minScore((double) query.getMinScore());
|
||||
}
|
||||
|
||||
if (query.getPreference() != null) {
|
||||
builder.preference(query.getPreference());
|
||||
}
|
||||
|
||||
builder.searchType(searchType(query.getSearchType()));
|
||||
|
||||
if (query.getSort() != null) {
|
||||
@@ -1146,10 +1235,6 @@ class RequestConverter {
|
||||
builder.trackTotalHits(th -> th.count(query.getTrackTotalHitsUpTo()));
|
||||
}
|
||||
|
||||
if (query.getRoute() != null) {
|
||||
builder.routing(query.getRoute());
|
||||
}
|
||||
|
||||
builder.timeout(timeStringMs(query.getTimeout()));
|
||||
|
||||
if (query.getExplain()) {
|
||||
@@ -1160,20 +1245,18 @@ class RequestConverter {
|
||||
builder.searchAfter(query.getSearchAfter().stream().map(Object::toString).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> {
|
||||
builder.rescore(getRescore(rescorerQuery));
|
||||
});
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
|
||||
|
||||
builder.requestCache(query.getRequestCache());
|
||||
|
||||
if (!query.getRuntimeFields().isEmpty()) {
|
||||
|
||||
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()))))));
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1192,9 +1275,8 @@ class RequestConverter {
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Map<String, Double> boosts = new LinkedHashMap<>();
|
||||
query.getIndicesBoost().forEach(indexBoost -> {
|
||||
boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost());
|
||||
});
|
||||
query.getIndicesBoost()
|
||||
.forEach(indexBoost -> boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost()));
|
||||
// noinspection unchecked
|
||||
builder.indicesBoost(boosts);
|
||||
}
|
||||
@@ -1225,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());
|
||||
}
|
||||
@@ -1235,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();
|
||||
}
|
||||
@@ -1250,8 +1341,7 @@ 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 //
|
||||
@@ -1289,16 +1379,34 @@ class RequestConverter {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private void prepareNativeSearch(NativeQuery query, SearchRequest.Builder builder) {
|
||||
|
||||
query.getScriptedFields().forEach(scriptedField -> {
|
||||
builder.scriptFields(scriptedField.getFieldName(), sf -> sf.script(getScript(scriptedField.getScriptData())));
|
||||
});
|
||||
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
|
||||
}
|
||||
|
||||
@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());
|
||||
@@ -1323,8 +1431,7 @@ class RequestConverter {
|
||||
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();
|
||||
@@ -1398,6 +1505,27 @@ 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
|
||||
@@ -1458,20 +1586,12 @@ 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1510,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
|
||||
|
||||
+30
-29
@@ -17,7 +17,13 @@ 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.*;
|
||||
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;
|
||||
@@ -59,7 +65,6 @@ class SearchDocumentResponseBuilder {
|
||||
* @param jsonpMapper to map JsonData objects
|
||||
* @return the SearchDocumentResponse
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public static <T> SearchDocumentResponse from(ResponseBody<EntityAsMap> responseBody,
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {
|
||||
|
||||
@@ -70,8 +75,9 @@ class SearchDocumentResponseBuilder {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,8 +94,9 @@ class SearchDocumentResponseBuilder {
|
||||
* @return the {@link SearchDocumentResponse}
|
||||
*/
|
||||
public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nullable String scrollId,
|
||||
@Nullable 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");
|
||||
|
||||
@@ -99,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";
|
||||
@@ -126,7 +128,7 @@ class SearchDocumentResponseBuilder {
|
||||
|
||||
Suggest suggest = suggestFrom(suggestES, entityCreator);
|
||||
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, pointInTimeId, searchDocuments,
|
||||
aggregationsContainer, suggest);
|
||||
}
|
||||
|
||||
@@ -145,20 +147,19 @@ class SearchDocumentResponseBuilder {
|
||||
if (!suggestionsES.isEmpty()) {
|
||||
// take the type from the first entry
|
||||
switch (suggestionsES.get(0)._kind()) {
|
||||
case Term: {
|
||||
case Term -> {
|
||||
suggestions.add(getTermSuggestion(name, suggestionsES));
|
||||
break;
|
||||
}
|
||||
case Phrase: {
|
||||
case Phrase -> {
|
||||
suggestions.add(getPhraseSuggestion(name, suggestionsES));
|
||||
break;
|
||||
}
|
||||
case Completion: {
|
||||
case Completion -> {
|
||||
suggestions.add(getCompletionSuggestion(name, suggestionsES, entityCreator));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -173,12 +174,11 @@ class SearchDocumentResponseBuilder {
|
||||
|
||||
List<TermSuggestion.Entry> entries = new ArrayList<>();
|
||||
suggestionsES.forEach(suggestionES -> {
|
||||
TermSuggest termSuggest = suggestionES.term();
|
||||
|
||||
TermSuggestOption optionES = termSuggest.options();
|
||||
var termSuggest = suggestionES.term();
|
||||
var termSuggestOptions = termSuggest.options();
|
||||
List<TermSuggestion.Entry.Option> options = new ArrayList<>();
|
||||
options.add(new TermSuggestion.Entry.Option(optionES.text(), null, optionES.score(), null,
|
||||
Math.toIntExact(optionES.freq())));
|
||||
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);
|
||||
@@ -188,10 +188,11 @@ class SearchDocumentResponseBuilder {
|
||||
|
||||
List<PhraseSuggestion.Entry> entries = new ArrayList<>();
|
||||
suggestionsES.forEach(suggestionES -> {
|
||||
PhraseSuggest phraseSuggest = suggestionES.phrase();
|
||||
PhraseSuggestOption optionES = phraseSuggest.options();
|
||||
var phraseSuggest = suggestionES.phrase();
|
||||
var phraseSuggestOptions = phraseSuggest.options();
|
||||
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
|
||||
options.add(new PhraseSuggestion.Entry.Option(optionES.text(), optionES.highlighted(), null, null));
|
||||
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));
|
||||
});
|
||||
|
||||
@@ -49,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
|
||||
@@ -124,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;
|
||||
@@ -167,16 +135,12 @@ final class TypeUtils {
|
||||
static HighlighterType highlighterType(@Nullable String value) {
|
||||
|
||||
if (value != null) {
|
||||
switch (value.toLowerCase()) {
|
||||
case "unified":
|
||||
return HighlighterType.Unified;
|
||||
case "plain":
|
||||
return HighlighterType.Plain;
|
||||
case "fvh":
|
||||
return HighlighterType.FastVector;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return switch (value.toLowerCase()) {
|
||||
case "unified" -> HighlighterType.Unified;
|
||||
case "plain" -> HighlighterType.Plain;
|
||||
case "fvh" -> HighlighterType.FastVector;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -186,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;
|
||||
@@ -215,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;
|
||||
}
|
||||
@@ -231,15 +190,11 @@ 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
|
||||
@@ -249,20 +204,14 @@ final class TypeUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case Created:
|
||||
return UpdateResponse.Result.CREATED;
|
||||
case Updated:
|
||||
return UpdateResponse.Result.UPDATED;
|
||||
case Deleted:
|
||||
return UpdateResponse.Result.DELETED;
|
||||
case NotFound:
|
||||
return UpdateResponse.Result.NOT_FOUND;
|
||||
case NoOp:
|
||||
return UpdateResponse.Result.NOOP;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -272,22 +221,15 @@ final class TypeUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (scoreMode) {
|
||||
case Default:
|
||||
return null;
|
||||
case Avg:
|
||||
return ScoreMode.Avg;
|
||||
case Max:
|
||||
return ScoreMode.Max;
|
||||
case Min:
|
||||
return ScoreMode.Min;
|
||||
case Total:
|
||||
return ScoreMode.Total;
|
||||
case Multiply:
|
||||
return ScoreMode.Multiply;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -297,14 +239,11 @@ final class TypeUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (searchType) {
|
||||
case QUERY_THEN_FETCH:
|
||||
return SearchType.QueryThenFetch;
|
||||
case DFS_QUERY_THEN_FETCH:
|
||||
return SearchType.DfsQueryThenFetch;
|
||||
}
|
||||
return switch (searchType) {
|
||||
case QUERY_THEN_FETCH -> SearchType.QueryThenFetch;
|
||||
case DFS_QUERY_THEN_FETCH -> SearchType.DfsQueryThenFetch;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -320,18 +259,13 @@ final class TypeUtils {
|
||||
@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;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -359,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;
|
||||
|
||||
+19
-25
@@ -105,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);
|
||||
|
||||
@@ -154,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]);
|
||||
@@ -174,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.");
|
||||
|
||||
@@ -217,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,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 {
|
||||
|
||||
+6
-15
@@ -175,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) {
|
||||
@@ -238,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 {
|
||||
@@ -248,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 {
|
||||
|
||||
+10
-8
@@ -139,7 +139,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -717,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);
|
||||
}
|
||||
|
||||
|
||||
+7
-16
@@ -56,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);
|
||||
}
|
||||
@@ -74,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")),
|
||||
@@ -104,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();
|
||||
}
|
||||
@@ -123,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");
|
||||
|
||||
+4
-6
@@ -133,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());
|
||||
@@ -146,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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+2
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
+2
@@ -72,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) {
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
|
||||
+22
-12
@@ -337,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);
|
||||
@@ -363,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);
|
||||
@@ -501,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());
|
||||
@@ -816,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);
|
||||
@@ -1048,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();
|
||||
}
|
||||
|
||||
@@ -1061,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();
|
||||
}
|
||||
@@ -1129,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));
|
||||
}
|
||||
@@ -1190,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));
|
||||
}
|
||||
@@ -1313,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));
|
||||
}
|
||||
@@ -1423,7 +1433,7 @@ public class RequestConverters {
|
||||
return this;
|
||||
}
|
||||
|
||||
EndpointBuilder addCommaSeparatedPathParts(String[] parts) {
|
||||
EndpointBuilder addCommaSeparatedPathParts(String... parts) {
|
||||
addPathPart(String.join(",", parts));
|
||||
return this;
|
||||
}
|
||||
|
||||
+33
-42
@@ -166,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();
|
||||
@@ -193,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();
|
||||
@@ -202,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();
|
||||
@@ -235,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) {
|
||||
@@ -590,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
|
||||
@@ -617,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);
|
||||
@@ -653,6 +650,7 @@ class RequestFactory {
|
||||
}
|
||||
|
||||
if (query.getOpType() != null) {
|
||||
|
||||
switch (query.getOpType()) {
|
||||
case INDEX:
|
||||
indexRequest.opType(DocWriteRequest.OpType.INDEX);
|
||||
@@ -677,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) {
|
||||
@@ -868,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);
|
||||
}
|
||||
|
||||
@@ -925,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);
|
||||
@@ -940,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();
|
||||
}
|
||||
@@ -956,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());
|
||||
@@ -1031,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);
|
||||
}
|
||||
|
||||
@@ -1171,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());
|
||||
@@ -1191,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;
|
||||
@@ -1207,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
|
||||
|
||||
@@ -46,8 +46,7 @@ import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.RestHighLevelClientBuilder;
|
||||
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.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -128,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);
|
||||
}
|
||||
}
|
||||
@@ -199,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);
|
||||
@@ -220,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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,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);
|
||||
}
|
||||
}
|
||||
@@ -252,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> {
|
||||
|
||||
|
||||
+5
-4
@@ -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.erhlc.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);
|
||||
}
|
||||
+4
-3
@@ -31,16 +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
|
||||
*/
|
||||
@Deprecated
|
||||
public class RestClientFactoryBean implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
|
||||
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";
|
||||
+5
-7
@@ -113,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);
|
||||
}
|
||||
|
||||
@@ -129,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) {
|
||||
@@ -150,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) {
|
||||
@@ -169,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) {
|
||||
|
||||
+4
-10
@@ -35,7 +35,6 @@ import java.util.function.Function;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
@@ -236,13 +235,7 @@ public interface WebClientProvider {
|
||||
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||
.getClientConfigurers()) {
|
||||
|
||||
if (clientConfigurer instanceof ReactiveRestClients.WebClientConfigurationCallback) {
|
||||
ReactiveRestClients.WebClientConfigurationCallback webClientConfigurationCallback = (ReactiveRestClients.WebClientConfigurationCallback) clientConfigurer;
|
||||
webClient = webClientConfigurationCallback.configure(webClient);
|
||||
}
|
||||
|
||||
if (clientConfigurer instanceof ElasticsearchClients.WebClientConfigurationCallback) {
|
||||
ElasticsearchClients.WebClientConfigurationCallback webClientConfigurationCallback = (ElasticsearchClients.WebClientConfigurationCallback) clientConfigurer;
|
||||
if (clientConfigurer instanceof ReactiveRestClients.WebClientConfigurationCallback webClientConfigurationCallback) {
|
||||
webClient = webClientConfigurationCallback.configure(webClient);
|
||||
}
|
||||
}
|
||||
@@ -250,11 +243,12 @@ public interface WebClientProvider {
|
||||
};
|
||||
|
||||
provider = provider //
|
||||
.withDefaultHeaders(clientConfiguration.getDefaultHeaders()) //
|
||||
.withDefaultHeaders(HttpHeaders.readOnlyHttpHeaders(clientConfiguration.getDefaultHeaders())) //
|
||||
.withWebClientConfigurer(webClientConfigurer) //
|
||||
.withRequestConfigurer(requestHeadersSpec -> requestHeadersSpec //
|
||||
.headers(httpHeaders -> {
|
||||
HttpHeaders suppliedHeaders = clientConfiguration.getHeadersSupplier().get();
|
||||
HttpHeaders suppliedHeaders = HttpHeaders
|
||||
.readOnlyHttpHeaders(clientConfiguration.getHeadersSupplier().get());
|
||||
|
||||
if (suppliedHeaders != null && suppliedHeaders != HttpHeaders.EMPTY) {
|
||||
httpHeaders.addAll(suppliedHeaders);
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -52,7 +50,7 @@ public class ScrollState {
|
||||
public List<String> getScrollIds() {
|
||||
|
||||
synchronized (lock) {
|
||||
return Collections.unmodifiableList(new ArrayList<>(pastIds));
|
||||
return List.copyOf(pastIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClientBeanDefinitionParser;
|
||||
import org.springframework.data.elasticsearch.repository.config.ElasticsearchRepositoryConfigExtension;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
@@ -35,6 +36,6 @@ public class ElasticsearchNamespaceHandler extends NamespaceHandlerSupport {
|
||||
RepositoryBeanDefinitionParser parser = new RepositoryBeanDefinitionParser(extension);
|
||||
|
||||
registerBeanDefinitionParser("repositories", parser);
|
||||
registerBeanDefinitionParser("rest-client", new RestClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("elasticsearch-client", new ElasticsearchClientBeanDefinitionParser());
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -28,6 +28,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.convert.EntityReader;
|
||||
import org.springframework.data.elasticsearch.client.UnsupportedClientOperationException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
@@ -313,12 +314,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
return doDelete(id, routingResolver.getRouting(), index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
final public String delete(String id, @Nullable String routing, IndexCoordinates index) {
|
||||
return doDelete(id, routing, index);
|
||||
}
|
||||
|
||||
protected abstract String doDelete(String id, @Nullable String routing, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
@@ -429,6 +424,16 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
abstract public void searchScrollClear(List<String> scrollIds);
|
||||
|
||||
@Override
|
||||
public String openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable) {
|
||||
throw new UnsupportedClientOperationException(getClass(), "openPointInTime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean closePointInTime(String pit) {
|
||||
throw new UnsupportedClientOperationException(getClass(), "closePointInTime");
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Helper methods
|
||||
@@ -465,7 +470,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null && idProperty.isWritable()
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
@@ -604,8 +609,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
protected void maybeCallbackBeforeConvertWithQuery(Object query, IndexCoordinates index) {
|
||||
|
||||
if (query instanceof IndexQuery) {
|
||||
IndexQuery indexQuery = (IndexQuery) query;
|
||||
if (query instanceof IndexQuery indexQuery) {
|
||||
Object queryObject = indexQuery.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
@@ -646,8 +650,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
protected void maybeCallbackAfterSaveWithQuery(Object query, IndexCoordinates index) {
|
||||
|
||||
if (query instanceof IndexQuery) {
|
||||
IndexQuery indexQuery = (IndexQuery) query;
|
||||
if (query instanceof IndexQuery indexQuery) {
|
||||
Object queryObject = indexQuery.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
@@ -689,8 +692,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
for (int i = 0; i < queries.size(); i++) {
|
||||
Object query = queries.get(i);
|
||||
|
||||
if (query instanceof IndexQuery) {
|
||||
IndexQuery indexQuery = (IndexQuery) query;
|
||||
if (query instanceof IndexQuery indexQuery) {
|
||||
Object queryObject = indexQuery.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
|
||||
+25
-39
@@ -19,6 +19,7 @@ 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.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.convert.EntityReader;
|
||||
import org.springframework.data.elasticsearch.client.UnsupportedClientOperationException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
@@ -173,9 +175,8 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
return getVendor() //
|
||||
.zipWith(getRuntimeLibraryVersion()) //
|
||||
.zipWith(getClusterVersion()) //
|
||||
.doOnNext(objects -> {
|
||||
VersionInfo.logVersions(objects.getT1().getT1(), objects.getT1().getT2(), objects.getT2());
|
||||
}).then();
|
||||
.doOnNext(objects -> VersionInfo.logVersions(objects.getT1().getT1(), objects.getT1().getT2(), objects.getT2()))
|
||||
.then();
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -228,8 +229,8 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
SeqNoPrimaryTerm seqNoPrimaryTerm = entity.getSeqNoPrimaryTerm();
|
||||
|
||||
if (seqNoPrimaryTerm != null) {
|
||||
query.setSeqNo(seqNoPrimaryTerm.getSequenceNumber());
|
||||
query.setPrimaryTerm(seqNoPrimaryTerm.getPrimaryTerm());
|
||||
query.setSeqNo(seqNoPrimaryTerm.sequenceNumber());
|
||||
query.setPrimaryTerm(seqNoPrimaryTerm.primaryTerm());
|
||||
usingSeqNo = true;
|
||||
}
|
||||
}
|
||||
@@ -259,7 +260,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null && idProperty.isWritable()
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
@@ -316,10 +317,10 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
T savedEntity = it.getT1();
|
||||
IndexResponseMetaData indexResponseMetaData = it.getT2();
|
||||
return updateIndexedObject(savedEntity, IndexedObjectInformation.of( //
|
||||
indexResponseMetaData.getId(), //
|
||||
indexResponseMetaData.getSeqNo(), //
|
||||
indexResponseMetaData.getPrimaryTerm(), //
|
||||
indexResponseMetaData.getVersion()));
|
||||
indexResponseMetaData.id(), //
|
||||
indexResponseMetaData.seqNo(), //
|
||||
indexResponseMetaData.primaryTerm(), //
|
||||
indexResponseMetaData.version()));
|
||||
}).flatMap(saved -> maybeCallAfterSave(saved, index));
|
||||
}
|
||||
|
||||
@@ -477,6 +478,17 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
}
|
||||
|
||||
abstract protected Mono<Long> doCount(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public Mono<String> openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable) {
|
||||
throw new UnsupportedClientOperationException(getClass(), "openPointInTime");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> closePointInTime(String pit) {
|
||||
throw new UnsupportedClientOperationException(getClass(), "closePointInTime");
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region callbacks
|
||||
@@ -624,34 +636,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
/**
|
||||
* Value class to capture client independent information from a response to an index request.
|
||||
*/
|
||||
public static class IndexResponseMetaData {
|
||||
private final String id;
|
||||
private final long seqNo;
|
||||
private final long primaryTerm;
|
||||
private final long version;
|
||||
|
||||
public IndexResponseMetaData(String id, long seqNo, long primaryTerm, long version) {
|
||||
this.id = id;
|
||||
this.seqNo = seqNo;
|
||||
this.primaryTerm = primaryTerm;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getSeqNo() {
|
||||
return seqNo;
|
||||
}
|
||||
|
||||
public long getPrimaryTerm() {
|
||||
return primaryTerm;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
public record IndexResponseMetaData(String id, long seqNo, long primaryTerm, long version) {
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -670,7 +655,8 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
}
|
||||
|
||||
public List<IndexQuery> indexQueries() {
|
||||
return entities.stream().map(value -> getIndexQuery(value)).collect(Collectors.toList());
|
||||
return entities.stream().map(AbstractReactiveElasticsearchTemplate.this::getIndexQuery)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public T entityAt(long index) {
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.springframework.data.elasticsearch.core;
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ActiveShardCount {
|
||||
public record ActiveShardCount(int value) {
|
||||
private static final int ACTIVE_SHARD_COUNT_DEFAULT = -2;
|
||||
private static final int ALL_ACTIVE_SHARDS = -1;
|
||||
|
||||
@@ -28,14 +28,4 @@ public class ActiveShardCount {
|
||||
public static final ActiveShardCount ALL = new ActiveShardCount(ALL_ACTIVE_SHARDS);
|
||||
public static final ActiveShardCount NONE = new ActiveShardCount(0);
|
||||
public static final ActiveShardCount ONE = new ActiveShardCount(1);
|
||||
|
||||
private final int value;
|
||||
|
||||
public ActiveShardCount(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,20 +245,6 @@ public interface DocumentOperations {
|
||||
*/
|
||||
String delete(String id, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id.
|
||||
*
|
||||
* @param id the document to delete
|
||||
* @param routing the optional routing for the document to be deleted
|
||||
* @param index the index from which to delete
|
||||
* @return documentId of the document deleted
|
||||
* @since 4.1
|
||||
* @deprecated since 4.2, use {@link ElasticsearchOperations#withRouting(RoutingResolver)} and
|
||||
* {@link #delete(String, IndexCoordinates)}
|
||||
*/
|
||||
@Deprecated
|
||||
String delete(String id, @Nullable String routing, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id.
|
||||
*
|
||||
|
||||
@@ -191,12 +191,10 @@ public class Range<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof Range)) {
|
||||
if (!(o instanceof Range<?> range)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Range<?> range = (Range<?>) o;
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(lowerBound, range.lowerBound)) {
|
||||
return false;
|
||||
}
|
||||
@@ -375,12 +373,10 @@ public class Range<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof Bound)) {
|
||||
if (!(o instanceof Bound<?> bound)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bound<?> bound = (Bound<?>) o;
|
||||
|
||||
if (inclusive != bound.inclusive)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -72,4 +72,14 @@ public interface ReactiveSearchHits<T> {
|
||||
* @return wether the {@link SearchHits} has a suggest response.
|
||||
*/
|
||||
boolean hasSuggest();
|
||||
|
||||
/**
|
||||
* When doing a search with a point in time, the response contains a new point in time id value.
|
||||
*
|
||||
* @return the new point in time id, if one was returned from Elasticsearch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
String getPointInTimeId();
|
||||
|
||||
}
|
||||
|
||||
@@ -78,4 +78,13 @@ public class ReactiveSearchHitsImpl<T> implements ReactiveSearchHits<T> {
|
||||
public boolean hasSuggest() {
|
||||
return delegate.hasSuggest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPointInTimeId() {
|
||||
return delegate.getPointInTimeId();
|
||||
}
|
||||
}
|
||||
|
||||
+33
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -271,6 +272,38 @@ public interface ReactiveSearchOperations {
|
||||
*/
|
||||
Mono<Suggest> suggest(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Opens a point in time (pit) in Elasticsearch.
|
||||
*
|
||||
* @param index the index name(s) to use
|
||||
* @param keepAlive the duration the pit shoult be kept alive
|
||||
* @return the pit identifier
|
||||
* @since 5.0
|
||||
*/
|
||||
default Mono<String> openPointInTime(IndexCoordinates index, Duration keepAlive) {
|
||||
return openPointInTime(index, keepAlive, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a point in time (pit) in Elasticsearch.
|
||||
*
|
||||
* @param index the index name(s) to use
|
||||
* @param keepAlive the duration the pit shoult be kept alive
|
||||
* @param ignoreUnavailable if {$literal true} the call will fail if any of the indices is missing or closed
|
||||
* @return the pit identifier
|
||||
* @since 5.0
|
||||
*/
|
||||
Mono<String> openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable);
|
||||
|
||||
/**
|
||||
* Closes a point in time
|
||||
*
|
||||
* @param pit the pit identifier as returned by {@link #openPointInTime(IndexCoordinates, Duration, Boolean)}
|
||||
* @return {@literal true} on success
|
||||
* @since 5.0
|
||||
*/
|
||||
Mono<Boolean> closePointInTime(String pit);
|
||||
|
||||
// region helper
|
||||
/**
|
||||
* Creates a {@link Query} to find all documents. Must be implemented by the concrete implementations to provide an
|
||||
|
||||
@@ -18,25 +18,30 @@ package org.springframework.data.elasticsearch.core;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Defines a runtime field to be added to a Query
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author cdalxndr
|
||||
* @since 4.3
|
||||
*/
|
||||
public class RuntimeField {
|
||||
|
||||
private final String name;
|
||||
private final String type;
|
||||
private final String script;
|
||||
@Nullable private final String script;
|
||||
|
||||
public RuntimeField(String name, String type, String script) {
|
||||
public RuntimeField(String name, String type) {
|
||||
this(name, type, null);
|
||||
}
|
||||
|
||||
public RuntimeField(String name, String type, @Nullable String script) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
Assert.notNull(type, "type must not be null");
|
||||
Assert.notNull(script, "script must not be null");
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
@@ -51,10 +56,12 @@ public class RuntimeField {
|
||||
* @return the mapping as a Map like it is needed for the Elasticsearch client
|
||||
*/
|
||||
public Map<String, Object> getMapping() {
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("type", type);
|
||||
map.put("script", script);
|
||||
|
||||
if (script != null) {
|
||||
map.put("script", script);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -68,7 +75,7 @@ public class RuntimeField {
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public String getScript() {
|
||||
public @Nullable String getScript() {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,16 +50,6 @@ public class SearchHit<T> {
|
||||
@Nullable private final Explanation explanation;
|
||||
private final List<String> matchedQueries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* @deprecated since 4.2 use
|
||||
* {@link #SearchHit(String, String, String, float, Object[], Map, Map, NestedMetaData, Explanation, List, Object)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public SearchHit(@Nullable String index, @Nullable String id, @Nullable String routing, float score,
|
||||
@Nullable Object[] sortValues, @Nullable Map<String, List<String>> highlightFields, T content) {
|
||||
this(index, id, routing, score, sortValues, highlightFields, null, null, null, null, content);
|
||||
}
|
||||
|
||||
public SearchHit(@Nullable String index, @Nullable String id, @Nullable String routing, float score,
|
||||
@Nullable Object[] sortValues, @Nullable Map<String, List<String>> highlightFields,
|
||||
@Nullable Map<String, SearchHits<?>> innerHits, @Nullable NestedMetaData nestedMetaData,
|
||||
|
||||
@@ -85,6 +85,7 @@ public class SearchHitMapping<T> {
|
||||
long totalHits = searchDocumentResponse.getTotalHits();
|
||||
float maxScore = searchDocumentResponse.getMaxScore();
|
||||
String scrollId = searchDocumentResponse.getScrollId();
|
||||
String pointInTimeId = searchDocumentResponse.getPointInTimeId();
|
||||
|
||||
List<SearchHit<T>> searchHits = new ArrayList<>();
|
||||
List<SearchDocument> searchDocuments = searchDocumentResponse.getSearchDocuments();
|
||||
@@ -100,7 +101,8 @@ public class SearchHitMapping<T> {
|
||||
Suggest suggest = searchDocumentResponse.getSuggest();
|
||||
mapHitsInCompletionSuggestion(suggest);
|
||||
|
||||
return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, scrollId, searchHits, aggregations, suggest);
|
||||
return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, scrollId, pointInTimeId, searchHits,
|
||||
aggregations, suggest);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -125,7 +127,7 @@ public class SearchHitMapping<T> {
|
||||
Assert.notNull(searchDocument, "searchDocument is null");
|
||||
Assert.notNull(content, "content is null");
|
||||
|
||||
return new SearchHit<T>(searchDocument.getIndex(), //
|
||||
return new SearchHit<>(searchDocument.getIndex(), //
|
||||
searchDocument.hasId() ? searchDocument.getId() : null, //
|
||||
searchDocument.getRouting(), //
|
||||
searchDocument.getScore(), //
|
||||
@@ -210,7 +212,7 @@ public class SearchHitMapping<T> {
|
||||
SearchDocument searchDocument = searchHit.getContent();
|
||||
|
||||
Object targetObject = converter.read(targetType, searchDocument);
|
||||
convertedSearchHits.add(new SearchHit<Object>(searchDocument.getIndex(), //
|
||||
convertedSearchHits.add(new SearchHit<>(searchDocument.getIndex(), //
|
||||
searchDocument.getId(), //
|
||||
searchDocument.getRouting(), //
|
||||
searchDocument.getScore(), //
|
||||
@@ -232,6 +234,7 @@ public class SearchHitMapping<T> {
|
||||
searchHits.getTotalHitsRelation(), //
|
||||
searchHits.getMaxScore(), //
|
||||
scrollId, //
|
||||
searchHits.getPointInTimeId(), //
|
||||
convertedSearchHits, //
|
||||
searchHits.getAggregations(), //
|
||||
searchHits.getSuggest());
|
||||
|
||||
@@ -67,8 +67,7 @@ public final class SearchHitSupport {
|
||||
return ((Stream<?>) result).map(SearchHitSupport::unwrapSearchHits);
|
||||
}
|
||||
|
||||
if (result instanceof SearchHits<?>) {
|
||||
SearchHits<?> searchHits = (SearchHits<?>) result;
|
||||
if (result instanceof SearchHits<?> searchHits) {
|
||||
return unwrapSearchHits(searchHits.getSearchHits());
|
||||
}
|
||||
|
||||
@@ -76,16 +75,14 @@ public final class SearchHitSupport {
|
||||
return unwrapSearchHitsIterator((SearchHitsIterator<?>) result);
|
||||
}
|
||||
|
||||
if (result instanceof SearchPage<?>) {
|
||||
SearchPage<?> searchPage = (SearchPage<?>) result;
|
||||
if (result instanceof SearchPage<?> searchPage) {
|
||||
List<?> content = (List<?>) SearchHitSupport.unwrapSearchHits(searchPage.getSearchHits());
|
||||
return new PageImpl<>(content, searchPage.getPageable(), searchPage.getTotalElements());
|
||||
}
|
||||
|
||||
if (ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)) {
|
||||
|
||||
if (result instanceof Flux) {
|
||||
Flux<?> flux = (Flux<?>) result;
|
||||
if (result instanceof Flux<?> flux) {
|
||||
return flux.map(SearchHitSupport::unwrapSearchHits);
|
||||
}
|
||||
}
|
||||
@@ -95,7 +92,7 @@ public final class SearchHitSupport {
|
||||
|
||||
private static CloseableIterator<?> unwrapSearchHitsIterator(SearchHitsIterator<?> iterator) {
|
||||
|
||||
return new CloseableIterator<Object>() {
|
||||
return new CloseableIterator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
|
||||
@@ -100,4 +100,12 @@ public interface SearchHits<T> extends Streamable<SearchHit<T>> {
|
||||
return getSearchHits().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* When doing a search with a point in time, the response contains a new point in time id value.
|
||||
*
|
||||
* @return the new point in time id, if one was returned from Elasticsearch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
String getPointInTimeId();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
private final Lazy<List<SearchHit<T>>> unmodifiableSearchHits;
|
||||
@Nullable private final AggregationsContainer<?> aggregations;
|
||||
@Nullable private final Suggest suggest;
|
||||
@Nullable private String pointInTimeId;
|
||||
|
||||
/**
|
||||
* @param totalHits the number of total hits for the search
|
||||
@@ -51,8 +52,8 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
* @param aggregations the aggregations if available
|
||||
*/
|
||||
public SearchHitsImpl(long totalHits, TotalHitsRelation totalHitsRelation, float maxScore, @Nullable String scrollId,
|
||||
List<? extends SearchHit<T>> searchHits, @Nullable AggregationsContainer<?> aggregations,
|
||||
@Nullable Suggest suggest) {
|
||||
@Nullable String pointInTimeId, List<? extends SearchHit<T>> searchHits,
|
||||
@Nullable AggregationsContainer<?> aggregations, @Nullable Suggest suggest) {
|
||||
|
||||
Assert.notNull(searchHits, "searchHits must not be null");
|
||||
|
||||
@@ -60,6 +61,7 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
this.totalHitsRelation = totalHitsRelation;
|
||||
this.maxScore = maxScore;
|
||||
this.scrollId = scrollId;
|
||||
this.pointInTimeId = pointInTimeId;
|
||||
this.searchHits = searchHits;
|
||||
this.aggregations = aggregations;
|
||||
this.suggest = suggest;
|
||||
@@ -110,6 +112,12 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
return suggest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getPointInTimeId() {
|
||||
return pointInTimeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SearchHits{" + //
|
||||
@@ -117,6 +125,7 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
", totalHitsRelation=" + totalHitsRelation + //
|
||||
", maxScore=" + maxScore + //
|
||||
", scrollId='" + scrollId + '\'' + //
|
||||
", pointInTimeId='" + pointInTimeId + '\'' + //
|
||||
", searchHits={" + searchHits.size() + " elements}" + //
|
||||
", aggregations=" + aggregations + //
|
||||
'}';
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
@@ -216,4 +217,35 @@ public interface SearchOperations {
|
||||
* @since 4.3
|
||||
*/
|
||||
Query idsQuery(List<String> ids);
|
||||
|
||||
/**
|
||||
* Opens a point in time (pit) in Elasticsearch.
|
||||
*
|
||||
* @param index the index name(s) to use
|
||||
* @param keepAlive the duration the pit shoult be kept alive
|
||||
* @return the pit identifier
|
||||
* @since 5.0
|
||||
*/
|
||||
default String openPointInTime(IndexCoordinates index, Duration keepAlive) {
|
||||
return openPointInTime(index, keepAlive, false);
|
||||
}
|
||||
/**
|
||||
* Opens a point in time (pit) in Elasticsearch.
|
||||
*
|
||||
* @param index the index name(s) to use
|
||||
* @param keepAlive the duration the pit shoult be kept alive
|
||||
* @param ignoreUnavailable if {$literal true} the call will fail if any of the indices is missing or closed
|
||||
* @return the pit identifier
|
||||
* @since 5.0
|
||||
*/
|
||||
String openPointInTime(IndexCoordinates index, Duration keepAlive, Boolean ignoreUnavailable);
|
||||
|
||||
/**
|
||||
* Closes a point in time
|
||||
*
|
||||
* @param pit the pit identifier as returned by {@link #openPointInTime(IndexCoordinates, Duration, Boolean)}
|
||||
* @return {@literal true} on success
|
||||
* @since 5.0
|
||||
*/
|
||||
Boolean closePointInTime(String pit);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ abstract class StreamQueries {
|
||||
long totalHits = searchHits.getTotalHits();
|
||||
TotalHitsRelation totalHitsRelation = searchHits.getTotalHitsRelation();
|
||||
|
||||
return new SearchHitsIterator<T>() {
|
||||
return new SearchHitsIterator<>() {
|
||||
|
||||
private volatile AtomicInteger currentCount = new AtomicInteger();
|
||||
private volatile Iterator<SearchHit<T>> currentScrollHits = searchHits.iterator();
|
||||
|
||||
+1
-2
@@ -26,7 +26,6 @@ import org.springframework.data.convert.TypeInformationMapper;
|
||||
import org.springframework.data.mapping.Alias;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -41,7 +40,7 @@ public class DefaultElasticsearchTypeMapper extends DefaultTypeMapper<Map<String
|
||||
implements ElasticsearchTypeMapper {
|
||||
|
||||
@SuppressWarnings("rawtypes") //
|
||||
private static final TypeInformation<Map> MAP_TYPE_INFO = ClassTypeInformation.from(Map.class);
|
||||
private static final TypeInformation<Map> MAP_TYPE_INFO = TypeInformation.of(Map.class);
|
||||
|
||||
private final @Nullable String typeKey;
|
||||
|
||||
|
||||
+1
-3
@@ -87,8 +87,7 @@ final public class ElasticsearchDateConverter {
|
||||
|
||||
Assert.notNull(accessor, "accessor must not be null");
|
||||
|
||||
if (accessor instanceof Instant) {
|
||||
Instant instant = (Instant) accessor;
|
||||
if (accessor instanceof Instant instant) {
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
return dateFormatter.format(zonedDateTime);
|
||||
}
|
||||
@@ -157,7 +156,6 @@ final public class ElasticsearchDateConverter {
|
||||
case weekyear:
|
||||
case weekyear_week:
|
||||
case weekyear_week_day:
|
||||
case custom:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+10
-18
@@ -171,24 +171,16 @@ public class GeoConverters {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
|
||||
switch (type) {
|
||||
case "point":
|
||||
return MapToGeoJsonPointConverter.INSTANCE.convert(source);
|
||||
case "multipoint":
|
||||
return MapToGeoJsonMultiPointConverter.INSTANCE.convert(source);
|
||||
case "linestring":
|
||||
return MapToGeoJsonLineStringConverter.INSTANCE.convert(source);
|
||||
case "multilinestring":
|
||||
return MapToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
|
||||
case "polygon":
|
||||
return MapToGeoJsonPolygonConverter.INSTANCE.convert(source);
|
||||
case "multipolygon":
|
||||
return MapToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
|
||||
case "geometrycollection":
|
||||
return MapToGeoJsonGeometryCollectionConverter.INSTANCE.convert(source);
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown GeoJson type " + type);
|
||||
}
|
||||
return switch (type) {
|
||||
case "point" -> MapToGeoJsonPointConverter.INSTANCE.convert(source);
|
||||
case "multipoint" -> MapToGeoJsonMultiPointConverter.INSTANCE.convert(source);
|
||||
case "linestring" -> MapToGeoJsonLineStringConverter.INSTANCE.convert(source);
|
||||
case "multilinestring" -> MapToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
|
||||
case "polygon" -> MapToGeoJsonPolygonConverter.INSTANCE.convert(source);
|
||||
case "multipolygon" -> MapToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
|
||||
case "geometrycollection" -> MapToGeoJsonGeometryCollectionConverter.INSTANCE.convert(source);
|
||||
default -> throw new IllegalArgumentException("unknown GeoJson type " + type);
|
||||
};
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
+119
-42
@@ -30,6 +30,7 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
@@ -53,8 +54,16 @@ import org.springframework.data.mapping.Parameter;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.*;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.EntityInstantiator;
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
|
||||
import org.springframework.data.mapping.model.PropertyValueProvider;
|
||||
import org.springframework.data.mapping.model.SpELContext;
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.format.datetime.DateFormatterRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -192,7 +201,7 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to do the actual writing. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* Class to do the actual reading. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* refactored to allow for keeping state during the conversion of an object.
|
||||
*/
|
||||
private static class Reader extends Base {
|
||||
@@ -211,10 +220,17 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* Reads the given source into the given type.
|
||||
*
|
||||
* @param type they type to convert the given source to.
|
||||
* @param source the source to create an object of the given type from.
|
||||
* @return the object that was read
|
||||
*/
|
||||
<R> R read(Class<R> type, Document source) {
|
||||
|
||||
TypeInformation<R> typeHint = ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeHint, source);
|
||||
TypeInformation<R> typeInformation = TypeInformation.of((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeInformation, source);
|
||||
|
||||
if (r == null) {
|
||||
throw new ConversionException("could not convert into object of class " + type);
|
||||
@@ -225,11 +241,11 @@ public class MappingElasticsearchConverter
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R read(TypeInformation<R> type, Map<String, Object> source) {
|
||||
private <R> R read(TypeInformation<R> typeInformation, Map<String, Object> source) {
|
||||
|
||||
Assert.notNull(source, "Source must not be null!");
|
||||
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type);
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, typeInformation);
|
||||
Class<? extends R> rawType = typeToUse.getType();
|
||||
|
||||
if (conversions.hasCustomReadTarget(source.getClass(), rawType)) {
|
||||
@@ -244,11 +260,11 @@ public class MappingElasticsearchConverter
|
||||
return readMap(typeToUse, source);
|
||||
}
|
||||
|
||||
if (typeToUse.equals(ClassTypeInformation.OBJECT)) {
|
||||
if (typeToUse.equals(TypeInformation.OBJECT)) {
|
||||
return (R) source;
|
||||
}
|
||||
// Retrieve persistent entity info
|
||||
|
||||
// Retrieve persistent entity info
|
||||
ElasticsearchPersistentEntity<?> entity = mappingContext.getPersistentEntity(typeToUse);
|
||||
|
||||
if (entity == null) {
|
||||
@@ -286,13 +302,13 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
Object value = entry.getValue();
|
||||
TypeInformation<?> defaultedValueType = valueType != null ? valueType : ClassTypeInformation.OBJECT;
|
||||
TypeInformation<?> defaultedValueType = valueType != null ? valueType : TypeInformation.OBJECT;
|
||||
|
||||
if (value instanceof Map) {
|
||||
map.put(key, read(defaultedValueType, (Map<String, Object>) value));
|
||||
} else if (value instanceof List) {
|
||||
map.put(key,
|
||||
readCollectionOrArray(valueType != null ? valueType : ClassTypeInformation.LIST, (List<Object>) value));
|
||||
readCollectionOrArray(valueType != null ? valueType : TypeInformation.LIST, (List<Object>) value));
|
||||
} else {
|
||||
map.put(key, getPotentiallyConvertedSimpleRead(value, rawValueType));
|
||||
}
|
||||
@@ -326,14 +342,13 @@ public class MappingElasticsearchConverter
|
||||
ElasticsearchPropertyValueProvider valueProvider = new ElasticsearchPropertyValueProvider(accessor, evaluator);
|
||||
R result = readProperties(targetEntity, instance, valueProvider);
|
||||
|
||||
if (source instanceof Document) {
|
||||
Document document = (Document) source;
|
||||
if (source instanceof Document document) {
|
||||
if (document.hasId()) {
|
||||
ElasticsearchPersistentProperty idProperty = targetEntity.getIdProperty();
|
||||
PersistentPropertyAccessor<R> propertyAccessor = new ConvertingPropertyAccessor<>(
|
||||
targetEntity.getPropertyAccessor(result), conversionService);
|
||||
// Only deal with String because ES generated Ids are strings !
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
if (idProperty != null && idProperty.isWritable() && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, document.getId());
|
||||
}
|
||||
}
|
||||
@@ -359,13 +374,11 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
if (source instanceof SearchDocument) {
|
||||
SearchDocument searchDocument = (SearchDocument) source;
|
||||
if (source instanceof SearchDocument searchDocument) {
|
||||
populateScriptFields(targetEntity, result, searchDocument);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
|
||||
@@ -397,7 +410,7 @@ public class MappingElasticsearchConverter
|
||||
|
||||
for (ElasticsearchPersistentProperty prop : entity) {
|
||||
|
||||
if (entity.isConstructorArgument(prop) || !prop.isReadable()) {
|
||||
if (entity.isCreatorArgument(prop) || !prop.isReadable() || !prop.isWritable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -455,12 +468,44 @@ public class MappingElasticsearchConverter
|
||||
} else if (value.getClass().isArray()) {
|
||||
return (T) readCollectionOrArray(type, Arrays.asList((Object[]) value));
|
||||
} else if (value instanceof Map) {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null) {
|
||||
Object o = read(collectionComponentType, (Map<String, Object>) value);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
return (T) read(type, (Map<String, Object>) value);
|
||||
} else {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null
|
||||
&& collectionComponentType.isAssignableFrom(TypeInformation.of(value.getClass()))) {
|
||||
Object o = getPotentiallyConvertedSimpleRead(value, collectionComponentType);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
|
||||
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getCollectionWithSingleElement(TypeInformation<?> collectionType,
|
||||
TypeInformation<?> componentType, Object element) {
|
||||
Collection<Object> collection = CollectionFactory.createCollection(collectionType.getType(),
|
||||
componentType.getType(), 1);
|
||||
collection.add(element);
|
||||
return (T) collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the type to check
|
||||
* @return true if type is a collectoin, null otherwise,
|
||||
*/
|
||||
@Nullable
|
||||
TypeInformation<?> getCollectionComponentType(TypeInformation<?> type) {
|
||||
return type.isCollectionLike() ? type.getComponentType() : null;
|
||||
}
|
||||
|
||||
private Object propertyConverterRead(ElasticsearchPersistentProperty property, Object source) {
|
||||
PropertyValueConverter propertyValueConverter = Objects.requireNonNull(property.getPropertyValueConverter());
|
||||
|
||||
@@ -504,7 +549,7 @@ public class MappingElasticsearchConverter
|
||||
|
||||
TypeInformation<?> componentType = targetType.getComponentType() != null //
|
||||
? targetType.getComponentType() //
|
||||
: ClassTypeInformation.OBJECT;
|
||||
: TypeInformation.OBJECT;
|
||||
Class<?> rawComponentType = componentType.getType();
|
||||
|
||||
Collection<Object> items = targetType.getType().isArray() //
|
||||
@@ -559,6 +604,29 @@ public class MappingElasticsearchConverter
|
||||
return Enum.valueOf((Class<Enum>) target, value.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
return conversionService.convert(value, target);
|
||||
} catch (ConverterNotFoundException e) {
|
||||
return convertFromCollectionToObject(value, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* we need the conversion from a collection to the first element for example in the case when reading the
|
||||
* constructor parameter of an entity from a scripted return. Originally this was handle in the conversionService,
|
||||
* but will be removed from spring-data-commons, so we do it here
|
||||
*/
|
||||
@Nullable
|
||||
private Object convertFromCollectionToObject(Object value, @Nullable Class<?> target) {
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
value = Arrays.asList(value);
|
||||
}
|
||||
|
||||
if (value instanceof Collection<?> collection && !collection.isEmpty()) {
|
||||
value = collection.iterator().next();
|
||||
}
|
||||
|
||||
return conversionService.convert(value, target);
|
||||
}
|
||||
|
||||
@@ -649,7 +717,8 @@ public class MappingElasticsearchConverter
|
||||
* @see org.springframework.data.mapping.model.SpELExpressionParameterValueProvider#potentiallyConvertSpelValue(java.lang.Object, org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||
*/
|
||||
@Override
|
||||
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
protected <T> T potentiallyConvertSpelValue(Object object,
|
||||
Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
return readValue(object, parameter.getType());
|
||||
}
|
||||
}
|
||||
@@ -694,7 +763,7 @@ public class MappingElasticsearchConverter
|
||||
writeTypeHints = entity.writeTypeHints();
|
||||
}
|
||||
|
||||
TypeInformation<?> typeInformation = ClassTypeInformation.from(entityType);
|
||||
TypeInformation<?> typeInformation = TypeInformation.of(entityType);
|
||||
|
||||
if (writeTypeHints && requiresTypeHint(entityType)) {
|
||||
typeMapper.writeType(typeInformation, sink);
|
||||
@@ -707,7 +776,7 @@ public class MappingElasticsearchConverter
|
||||
* Internal write conversion method which should be used for nested invocations.
|
||||
*
|
||||
* @param source the object to write
|
||||
* @param sink the write destination
|
||||
* @param sink the destination
|
||||
* @param typeInformation type information for the source
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -731,12 +800,12 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
if (Map.class.isAssignableFrom(entityType)) {
|
||||
writeMapInternal((Map<Object, Object>) source, sink, ClassTypeInformation.MAP);
|
||||
writeMapInternal((Map<Object, Object>) source, sink, TypeInformation.MAP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Collection.class.isAssignableFrom(entityType)) {
|
||||
writeCollectionInternal((Collection<?>) source, ClassTypeInformation.LIST, (Collection<?>) sink);
|
||||
writeCollectionInternal((Collection<?>) source, TypeInformation.LIST, (Collection<?>) sink);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -749,7 +818,7 @@ public class MappingElasticsearchConverter
|
||||
* Internal write conversion method which should be used for nested invocations.
|
||||
*
|
||||
* @param source the object to write
|
||||
* @param sink the write destination
|
||||
* @param sink the destination
|
||||
* @param entity entity for the source
|
||||
*/
|
||||
private void writeInternal(@Nullable Object source, Map<String, Object> sink,
|
||||
@@ -813,7 +882,7 @@ public class MappingElasticsearchConverter
|
||||
} else {
|
||||
Map<String, Object> document = Document.create();
|
||||
TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
|
||||
: ClassTypeInformation.OBJECT;
|
||||
: TypeInformation.OBJECT;
|
||||
writeInternal(value, document, valueTypeInfo);
|
||||
|
||||
sink.put(simpleKey, document);
|
||||
@@ -923,7 +992,7 @@ public class MappingElasticsearchConverter
|
||||
return;
|
||||
}
|
||||
|
||||
TypeInformation<?> valueType = ClassTypeInformation.from(value.getClass());
|
||||
TypeInformation<?> valueType = TypeInformation.of(value.getClass());
|
||||
TypeInformation<?> type = property.getTypeInformation();
|
||||
|
||||
if (valueType.isCollectionLike()) {
|
||||
@@ -955,7 +1024,7 @@ public class MappingElasticsearchConverter
|
||||
Map<String, Object> document = existingValue instanceof Map ? (Map<String, Object>) existingValue
|
||||
: Document.create();
|
||||
|
||||
addCustomTypeKeyIfNecessary(value, document, ClassTypeInformation.from(property.getRawType()));
|
||||
addCustomTypeKeyIfNecessary(value, document, TypeInformation.of(property.getRawType()));
|
||||
writeInternal(value, document, entity);
|
||||
sink.set(property, document);
|
||||
}
|
||||
@@ -1117,17 +1186,18 @@ public class MappingElasticsearchConverter
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
if (domainClass != null) {
|
||||
if (domainClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateFieldsAndSourceFilter(query, domainClass);
|
||||
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
|
||||
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updateCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updatePropertiesInCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||
|
||||
@@ -1165,14 +1235,22 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> updateFieldNames(List<String> fields, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fields.stream().map(fieldName -> {
|
||||
/**
|
||||
* relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
|
||||
* fieldname. If no such property exists, the original fieldName is kept.
|
||||
*
|
||||
* @param fieldNames list of fieldnames
|
||||
* @param persistentEntity the persistent entity to check
|
||||
* @return an updated list of field names
|
||||
*/
|
||||
private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fieldNames.stream().map(fieldName -> {
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
|
||||
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
|
||||
Assert.notNull(criteriaQuery, "criteriaQuery must not be null");
|
||||
Assert.notNull(domainClass, "domainClass must not be null");
|
||||
@@ -1181,17 +1259,17 @@ public class MappingElasticsearchConverter
|
||||
|
||||
if (persistentEntity != null) {
|
||||
for (Criteria chainedCriteria : criteriaQuery.getCriteria().getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
for (Criteria subCriteria : criteriaQuery.getCriteria().getSubCriteria()) {
|
||||
for (Criteria chainedCriteria : subCriteria.getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Field field = criteria.getField();
|
||||
|
||||
@@ -1288,10 +1366,9 @@ public class MappingElasticsearchConverter
|
||||
|
||||
String fieldName = property.getFieldName();
|
||||
|
||||
if (target instanceof Document) {
|
||||
if (target instanceof Document document) {
|
||||
// nested objects may have properties like 'id' which are recognized as isIdProperty() but they are not
|
||||
// Documents
|
||||
Document document = (Document) target;
|
||||
|
||||
if (property.isIdProperty() && document.hasId()) {
|
||||
Object id = null;
|
||||
|
||||
+1
-2
@@ -258,10 +258,9 @@ public class SearchDocumentAdapter implements SearchDocument {
|
||||
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
-2
@@ -39,13 +39,16 @@ public class SearchDocumentResponse {
|
||||
@Nullable private final AggregationsContainer<?> aggregations;
|
||||
@Nullable private final Suggest suggest;
|
||||
|
||||
@Nullable String pointInTimeId;
|
||||
|
||||
public SearchDocumentResponse(long totalHits, String totalHitsRelation, float maxScore, @Nullable String scrollId,
|
||||
List<SearchDocument> searchDocuments, @Nullable AggregationsContainer<?> aggregationsContainer,
|
||||
@Nullable Suggest suggest) {
|
||||
@Nullable String pointInTimeId, List<SearchDocument> searchDocuments,
|
||||
@Nullable AggregationsContainer<?> aggregationsContainer, @Nullable Suggest suggest) {
|
||||
this.totalHits = totalHits;
|
||||
this.totalHitsRelation = totalHitsRelation;
|
||||
this.maxScore = maxScore;
|
||||
this.scrollId = scrollId;
|
||||
this.pointInTimeId = pointInTimeId;
|
||||
this.searchDocuments = searchDocuments;
|
||||
this.aggregations = aggregationsContainer;
|
||||
this.suggest = suggest;
|
||||
@@ -82,6 +85,14 @@ public class SearchDocumentResponse {
|
||||
return suggest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
public String getPointInTimeId() {
|
||||
return pointInTimeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to convert a {@link SearchDocument} async into an entity. Asynchronous so that it can be used from the
|
||||
* imperative and the reactive code.
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class GeoJsonMultiPolygon implements GeoJson<Iterable<GeoJsonPolygon>> {
|
||||
|
||||
public static final String TYPE = "MultiPolygon";
|
||||
|
||||
private List<GeoJsonPolygon> coordinates = new ArrayList<GeoJsonPolygon>();
|
||||
private List<GeoJsonPolygon> coordinates = new ArrayList<>();
|
||||
|
||||
private GeoJsonMultiPolygon(List<GeoJsonPolygon> polygons) {
|
||||
this.coordinates.addAll(polygons);
|
||||
|
||||
+18
-16
@@ -157,7 +157,7 @@ public class MappingBuilder {
|
||||
private class InternalBuilder {
|
||||
|
||||
private boolean writeTypeHints = true;
|
||||
private List<String> excludeFromSource = new ArrayList<>();
|
||||
private final List<String> excludeFromSource = new ArrayList<>();
|
||||
private String nestedPropertyPrefix = "";
|
||||
|
||||
protected String buildPropertyMapping(ElasticsearchPersistentEntity<?> entity,
|
||||
@@ -172,8 +172,11 @@ public class MappingBuilder {
|
||||
// Dynamic templates
|
||||
addDynamicTemplatesMapping(objectNode, entity);
|
||||
|
||||
mapEntity(objectNode, entity, true, "", false, FieldType.Auto, null,
|
||||
entity.findAnnotation(DynamicMapping.class), runtimeFields);
|
||||
org.springframework.data.elasticsearch.annotations.Document docAnnotation = entity
|
||||
.findAnnotation(org.springframework.data.elasticsearch.annotations.Document.class);
|
||||
var dynamicMapping = docAnnotation != null ? docAnnotation.dynamic() : null;
|
||||
|
||||
mapEntity(objectNode, entity, true, "", false, FieldType.Auto, null, dynamicMapping, runtimeFields);
|
||||
|
||||
if (!excludeFromSource.isEmpty()) {
|
||||
ObjectNode sourceNode = objectNode.putObject(SOURCE);
|
||||
@@ -209,8 +212,8 @@ public class MappingBuilder {
|
||||
|
||||
private void mapEntity(ObjectNode objectNode, @Nullable ElasticsearchPersistentEntity<?> entity,
|
||||
boolean isRootObject, String nestedObjectFieldName, boolean nestedOrObjectField, FieldType fieldType,
|
||||
@Nullable Field parentFieldAnnotation, @Nullable DynamicMapping dynamicMapping,
|
||||
@Nullable Document runtimeFields) throws IOException {
|
||||
@Nullable Field parentFieldAnnotation, @Nullable Dynamic dynamicMapping, @Nullable Document runtimeFields)
|
||||
throws IOException {
|
||||
|
||||
if (entity != null && entity.isAnnotationPresent(Mapping.class)) {
|
||||
Mapping mappingAnnotation = entity.getRequiredAnnotation(Mapping.class);
|
||||
@@ -258,8 +261,8 @@ public class MappingBuilder {
|
||||
|
||||
if (entity != null && entity.dynamic() != Dynamic.INHERIT) {
|
||||
objectNode.put(TYPE_DYNAMIC, entity.dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
objectNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
} else if (dynamicMapping != null && dynamicMapping != Dynamic.INHERIT) {
|
||||
objectNode.put(TYPE_DYNAMIC, dynamicMapping.getMappedName());
|
||||
}
|
||||
|
||||
ObjectNode propertiesNode = objectNode.putObject(FIELD_PROPERTIES);
|
||||
@@ -339,7 +342,7 @@ public class MappingBuilder {
|
||||
|
||||
boolean isCompletionProperty = property.isCompletionProperty();
|
||||
boolean isNestedOrObjectProperty = isNestedOrObjectProperty(property);
|
||||
DynamicMapping dynamicMapping = property.findAnnotation(DynamicMapping.class);
|
||||
Dynamic dynamicMapping = fieldAnnotation != null ? fieldAnnotation.dynamic() : null;
|
||||
|
||||
if (!isCompletionProperty && property.isEntity() && hasRelevantAnnotation(property)) {
|
||||
|
||||
@@ -475,7 +478,7 @@ public class MappingBuilder {
|
||||
* @throws IOException
|
||||
*/
|
||||
private void addSingleFieldMapping(ObjectNode propertiesNode, ElasticsearchPersistentProperty property,
|
||||
Field annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping) throws IOException {
|
||||
Field annotation, boolean nestedOrObjectField, @Nullable Dynamic dynamicMapping) throws IOException {
|
||||
|
||||
// build the property json, if empty skip it as this is no valid mapping
|
||||
ObjectNode fieldNode = objectMapper.createObjectNode();
|
||||
@@ -490,8 +493,8 @@ public class MappingBuilder {
|
||||
if (nestedOrObjectField) {
|
||||
if (annotation.dynamic() != Dynamic.INHERIT) {
|
||||
fieldNode.put(TYPE_DYNAMIC, annotation.dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
fieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
} else if (dynamicMapping != null && dynamicMapping != Dynamic.INHERIT) {
|
||||
fieldNode.put(TYPE_DYNAMIC, dynamicMapping.getMappedName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,8 +534,7 @@ public class MappingBuilder {
|
||||
* @throws IOException
|
||||
*/
|
||||
private void addMultiFieldMapping(ObjectNode propertyNode, ElasticsearchPersistentProperty property,
|
||||
MultiField annotation, boolean nestedOrObjectField, @Nullable DynamicMapping dynamicMapping)
|
||||
throws IOException {
|
||||
MultiField annotation, boolean nestedOrObjectField, @Nullable Dynamic dynamicMapping) throws IOException {
|
||||
|
||||
// main field
|
||||
ObjectNode mainFieldNode = objectMapper.createObjectNode();
|
||||
@@ -541,8 +543,8 @@ public class MappingBuilder {
|
||||
if (nestedOrObjectField) {
|
||||
if (annotation.mainField().dynamic() != Dynamic.INHERIT) {
|
||||
mainFieldNode.put(TYPE_DYNAMIC, annotation.mainField().dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
mainFieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
} else if (dynamicMapping != null && dynamicMapping != Dynamic.INHERIT) {
|
||||
mainFieldNode.put(TYPE_DYNAMIC, dynamicMapping.getMappedName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +596,7 @@ public class MappingBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnyPropertyAnnotatedWithField(@Nullable ElasticsearchPersistentEntity entity) {
|
||||
private boolean isAnyPropertyAnnotatedWithField(@Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
|
||||
return entity != null && entity.getPersistentProperty(Field.class) != null;
|
||||
}
|
||||
|
||||
+5
-16
@@ -244,9 +244,6 @@ public final class MappingParameters {
|
||||
|
||||
// built-in formats
|
||||
for (DateFormat dateFormat : dateFormats) {
|
||||
if (dateFormat == DateFormat.none || dateFormat == DateFormat.custom) {
|
||||
continue;
|
||||
}
|
||||
formats.add(dateFormat.toString());
|
||||
}
|
||||
|
||||
@@ -321,19 +318,11 @@ public final class MappingParameters {
|
||||
|
||||
if (StringUtils.hasLength(nullValue)) {
|
||||
switch (nullValueType) {
|
||||
case Integer:
|
||||
objectNode.put(FIELD_PARAM_NULL_VALUE, Integer.valueOf(nullValue));
|
||||
break;
|
||||
case Long:
|
||||
objectNode.put(FIELD_PARAM_NULL_VALUE, Long.valueOf(nullValue));
|
||||
break;
|
||||
case Double:
|
||||
objectNode.put(FIELD_PARAM_NULL_VALUE, Double.valueOf(nullValue));
|
||||
break;
|
||||
case String:
|
||||
default:
|
||||
objectNode.put(FIELD_PARAM_NULL_VALUE, nullValue);
|
||||
break;
|
||||
case Integer -> objectNode.put(FIELD_PARAM_NULL_VALUE, Integer.valueOf(nullValue));
|
||||
case Long -> objectNode.put(FIELD_PARAM_NULL_VALUE, Long.valueOf(nullValue));
|
||||
case Double -> objectNode.put(FIELD_PARAM_NULL_VALUE, Double.valueOf(nullValue));
|
||||
case String -> objectNode.put(FIELD_PARAM_NULL_VALUE, nullValue);
|
||||
default -> objectNode.put(FIELD_PARAM_NULL_VALUE, nullValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,8 +114,7 @@ public class Settings extends DefaultStringObjectMap<Settings> {
|
||||
*/
|
||||
static private Stream<Map.Entry<String, Object>> doFlatten(Map.Entry<String, Object> entry) {
|
||||
|
||||
if (entry.getValue() instanceof Map<?, ?>) {
|
||||
Map<?, ?> nested = (Map<?, ?>) entry.getValue();
|
||||
if (entry.getValue()instanceof Map<?, ?> nested) {
|
||||
|
||||
// noinspection unchecked
|
||||
return nested.entrySet().stream() //
|
||||
|
||||
@@ -86,7 +86,7 @@ public class TemplateData {
|
||||
|
||||
private TemplateDataBuilder() {}
|
||||
|
||||
public TemplateDataBuilder withIndexPatterns(String[] indexPatterns) {
|
||||
public TemplateDataBuilder withIndexPatterns(String... indexPatterns) {
|
||||
this.indexPatterns = indexPatterns;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -68,10 +68,9 @@ public class JoinField<ID> {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof JoinField)) {
|
||||
if (!(obj instanceof JoinField<?> other)) {
|
||||
return false;
|
||||
}
|
||||
JoinField other = (JoinField) obj;
|
||||
return Objects.equals(name, other.name) && Objects.equals(parent, other.parent);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.mapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -35,15 +33,9 @@ public class ElasticsearchSimpleTypes {
|
||||
static final Set<Class<?>> AUTOGENERATED_ID_TYPES;
|
||||
|
||||
static {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
classes.add(String.class);
|
||||
AUTOGENERATED_ID_TYPES = Collections.unmodifiableSet(classes);
|
||||
AUTOGENERATED_ID_TYPES = Set.of(String.class);
|
||||
|
||||
Set<Class<?>> simpleTypes = new HashSet<>();
|
||||
simpleTypes.add(Document.class);
|
||||
simpleTypes.add(Map.class);
|
||||
|
||||
ELASTICSEARCH_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
|
||||
ELASTICSEARCH_SIMPLE_TYPES = Set.of(Document.class, Map.class);
|
||||
}
|
||||
|
||||
private static final Set<Class<?>> ELASTICSEARCH_SIMPLE_TYPES;
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class IndexCoordinates {
|
||||
return new IndexCoordinates(indexNames);
|
||||
}
|
||||
|
||||
private IndexCoordinates(String[] indexNames) {
|
||||
private IndexCoordinates(String... indexNames) {
|
||||
Assert.notEmpty(indexNames, "indexNames may not be null or empty");
|
||||
this.indexNames = indexNames;
|
||||
}
|
||||
|
||||
+6
-8
@@ -395,16 +395,14 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
private SettingsParameter buildSettingsParameter(Class<?> clazz) {
|
||||
|
||||
SettingsParameter settingsParameter = new SettingsParameter();
|
||||
Document documentAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Document.class);
|
||||
Setting settingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Setting.class);
|
||||
|
||||
if (documentAnnotation != null) {
|
||||
settingsParameter.useServerConfiguration = documentAnnotation.useServerConfiguration();
|
||||
settingsParameter.shards = documentAnnotation.shards();
|
||||
settingsParameter.replicas = documentAnnotation.replicas();
|
||||
settingsParameter.refreshIntervall = documentAnnotation.refreshInterval();
|
||||
settingsParameter.indexStoreType = documentAnnotation.indexStoreType();
|
||||
}
|
||||
// default values
|
||||
settingsParameter.useServerConfiguration = false;
|
||||
settingsParameter.shards = 1;
|
||||
settingsParameter.replicas = 1;
|
||||
settingsParameter.refreshIntervall = "1s";
|
||||
settingsParameter.indexStoreType = "fs";
|
||||
|
||||
if (settingAnnotation != null) {
|
||||
processSettingAnnotation(settingAnnotation, settingsParameter);
|
||||
|
||||
+4
-13
@@ -261,19 +261,10 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
// register converters for built-in formats
|
||||
for (DateFormat dateFormat : dateFormats) {
|
||||
switch (dateFormat) {
|
||||
case none:
|
||||
case custom:
|
||||
break;
|
||||
case weekyear:
|
||||
case weekyear_week:
|
||||
case weekyear_week_day:
|
||||
LOGGER.warn(String.format(
|
||||
"No default converter available for '%s' and date format '%s'. Use a custom converter instead.",
|
||||
actualType.getName(), dateFormat.name()));
|
||||
break;
|
||||
default:
|
||||
converters.add(ElasticsearchDateConverter.of(dateFormat));
|
||||
break;
|
||||
case weekyear, weekyear_week, weekyear_week_day -> LOGGER.warn(String.format(
|
||||
"No default converter available for '%s' and date format '%s'. Use a custom converter instead.",
|
||||
actualType.getName(), dateFormat.name()));
|
||||
default -> converters.add(ElasticsearchDateConverter.of(dateFormat));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BaseQuery implements Query {
|
||||
|
||||
protected Pageable pageable = DEFAULT_PAGE;
|
||||
@Nullable protected Sort sort;
|
||||
protected Pageable pageable = DEFAULT_PAGE;
|
||||
protected List<String> fields = new ArrayList<>();
|
||||
@Nullable protected List<String> storedFields;
|
||||
@Nullable protected SourceFilter sourceFilter;
|
||||
@@ -67,11 +67,12 @@ public class BaseQuery implements Query {
|
||||
@Nullable protected Duration timeout;
|
||||
private boolean explain = false;
|
||||
@Nullable protected List<Object> searchAfter;
|
||||
@Nullable protected List<IndexBoost> indicesBoost;
|
||||
protected List<RescorerQuery> rescorerQueries = new ArrayList<>();
|
||||
@Nullable protected Boolean requestCache;
|
||||
protected List<IdWithRouting> idsWithRouting = Collections.emptyList();
|
||||
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
|
||||
@Nullable protected List<IndexBoost> indicesBoost;
|
||||
@Nullable protected PointInTime pointInTime;
|
||||
|
||||
public BaseQuery() {}
|
||||
|
||||
@@ -79,17 +80,29 @@ public class BaseQuery implements Query {
|
||||
this.sort = builder.getSort();
|
||||
// do a setPageable after setting the sort, because the pageable may contain an additional sort
|
||||
this.setPageable(builder.getPageable() != null ? builder.getPageable() : DEFAULT_PAGE);
|
||||
this.ids = builder.getIds();
|
||||
this.trackScores = builder.getTrackScores();
|
||||
this.maxResults = builder.getMaxResults();
|
||||
this.indicesOptions = builder.getIndicesOptions();
|
||||
this.minScore = builder.getMinScore();
|
||||
this.preference = builder.getPreference();
|
||||
this.sourceFilter = builder.getSourceFilter();
|
||||
this.fields = builder.getFields();
|
||||
this.highlightQuery = builder.highlightQuery;
|
||||
this.storedFields = builder.getStoredFields();
|
||||
this.sourceFilter = builder.getSourceFilter();
|
||||
this.minScore = builder.getMinScore();
|
||||
this.ids = builder.getIds() == null ? null : builder.getIds();
|
||||
this.route = builder.getRoute();
|
||||
this.searchType = builder.getSearchType();
|
||||
this.indicesOptions = builder.getIndicesOptions();
|
||||
this.trackScores = builder.getTrackScores();
|
||||
this.preference = builder.getPreference();
|
||||
this.maxResults = builder.getMaxResults();
|
||||
this.highlightQuery = builder.getHighlightQuery();
|
||||
this.trackTotalHits = builder.getTrackTotalHits();
|
||||
this.trackTotalHitsUpTo = builder.getTrackTotalHitsUpTo();
|
||||
this.scrollTime = builder.getScrollTime();
|
||||
this.timeout = builder.getTimeout();
|
||||
this.explain = builder.getExplain();
|
||||
this.searchAfter = builder.getSearchAfter();
|
||||
this.indicesBoost = builder.getIndicesBoost();
|
||||
this.rescorerQueries = builder.getRescorerQueries();
|
||||
this.requestCache = builder.getRequestCache();
|
||||
this.idsWithRouting = builder.getIdsWithRouting();
|
||||
this.pointInTime = builder.getPointInTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,7 +287,6 @@ public class BaseQuery implements Query {
|
||||
/**
|
||||
* Configures whether to track scores.
|
||||
*
|
||||
* @param trackScores
|
||||
* @since 3.1
|
||||
*/
|
||||
public void setTrackScores(boolean trackScores) {
|
||||
@@ -359,7 +371,6 @@ public class BaseQuery implements Query {
|
||||
/**
|
||||
* set the query timeout
|
||||
*
|
||||
* @param timeout
|
||||
* @since 4.2
|
||||
*/
|
||||
public void setTimeout(@Nullable Duration timeout) {
|
||||
@@ -440,4 +451,19 @@ public class BaseQuery implements Query {
|
||||
public List<IndexBoost> getIndicesBoost() {
|
||||
return indicesBoost;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
public PointInTime getPointInTime() {
|
||||
return pointInTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setPointInTime(@Nullable PointInTime pointInTime) {
|
||||
this.pointInTime = pointInTime;
|
||||
}
|
||||
}
|
||||
|
||||
+199
-18
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -23,7 +24,9 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.RuntimeField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* base class for query builders. The different implementations of {@link Query} should derive from this class and then
|
||||
@@ -34,28 +37,52 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQueryBuilder<Q, SELF>> {
|
||||
|
||||
@Nullable private Pageable pageable;
|
||||
@Nullable private Sort sort;
|
||||
@Nullable private Integer maxResults;
|
||||
@Nullable private Collection<String> ids;
|
||||
private boolean trackScores;
|
||||
@Nullable protected IndicesOptions indicesOptions;
|
||||
private float minScore;
|
||||
@Nullable private String preference;
|
||||
@Nullable private Pageable pageable;
|
||||
private final List<String> fields = new ArrayList<>();
|
||||
@Nullable private List<String> storedFields;
|
||||
@Nullable private SourceFilter sourceFilter;
|
||||
private List<String> fields = new ArrayList<>();
|
||||
@Nullable protected HighlightQuery highlightQuery;
|
||||
private float minScore;
|
||||
private final Collection<String> ids = new ArrayList<>();
|
||||
@Nullable private String route;
|
||||
protected Query.SearchType searchType = Query.SearchType.QUERY_THEN_FETCH;
|
||||
@Nullable protected IndicesOptions indicesOptions;
|
||||
private boolean trackScores;
|
||||
@Nullable private String preference;
|
||||
@Nullable private Integer maxResults;
|
||||
@Nullable protected HighlightQuery highlightQuery;
|
||||
@Nullable private Boolean trackTotalHits;
|
||||
@Nullable protected Integer trackTotalHitsUpTo;
|
||||
@Nullable protected Duration scrollTime;
|
||||
@Nullable protected Duration timeout;
|
||||
boolean explain = false;
|
||||
@Nullable protected List<Object> searchAfter;
|
||||
|
||||
@Nullable private List<IndexBoost> indicesBoost;
|
||||
protected final List<RescorerQuery> rescorerQueries = new ArrayList<>();
|
||||
|
||||
@Nullable protected Boolean requestCache;
|
||||
protected final List<Query.IdWithRouting> idsWithRouting = new ArrayList<>();
|
||||
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
|
||||
@Nullable protected Query.PointInTime pointInTime;
|
||||
|
||||
@Nullable
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Pageable getPageable() {
|
||||
return pageable;
|
||||
}
|
||||
|
||||
public List<String> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
public List<String> getStoredFields() {
|
||||
return storedFields;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -91,10 +118,6 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
return sourceFilter;
|
||||
}
|
||||
|
||||
public List<String> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HighlightQuery getHighlightQuery() {
|
||||
return highlightQuery;
|
||||
@@ -110,6 +133,64 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
return indicesBoost;
|
||||
}
|
||||
|
||||
public Query.SearchType getSearchType() {
|
||||
return searchType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getTrackTotalHits() {
|
||||
return trackTotalHits;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getTrackTotalHitsUpTo() {
|
||||
return trackTotalHitsUpTo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getScrollTime() {
|
||||
return scrollTime;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public boolean getExplain() {
|
||||
return explain;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Object> getSearchAfter() {
|
||||
return searchAfter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getRequestCache() {
|
||||
return requestCache;
|
||||
}
|
||||
|
||||
public List<Query.IdWithRouting> getIdsWithRouting() {
|
||||
return idsWithRouting;
|
||||
}
|
||||
|
||||
public List<RuntimeField> getRuntimeFields() {
|
||||
return runtimeFields;
|
||||
}
|
||||
|
||||
public List<RescorerQuery> getRescorerQueries() {
|
||||
return rescorerQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
public Query.PointInTime getPointInTime() {
|
||||
return pointInTime;
|
||||
}
|
||||
|
||||
public SELF withPageable(Pageable pageable) {
|
||||
this.pageable = pageable;
|
||||
return self();
|
||||
@@ -130,12 +211,18 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
}
|
||||
|
||||
public SELF withIds(String... ids) {
|
||||
this.ids = Arrays.asList(ids);
|
||||
|
||||
this.ids.clear();
|
||||
this.ids.addAll(Arrays.asList(ids));
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIds(Collection<String> ids) {
|
||||
this.ids = ids;
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
this.ids.clear();
|
||||
this.ids.addAll(ids);
|
||||
return self();
|
||||
}
|
||||
|
||||
@@ -165,11 +252,17 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
}
|
||||
|
||||
public SELF withFields(String... fields) {
|
||||
|
||||
this.fields.clear();
|
||||
Collections.addAll(this.fields, fields);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withFields(Collection<String> fields) {
|
||||
|
||||
Assert.notNull(fields, "fields must not be null");
|
||||
|
||||
this.fields.clear();
|
||||
this.fields.addAll(fields);
|
||||
return self();
|
||||
}
|
||||
@@ -184,16 +277,104 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIndicesBoost(List<IndexBoost> indicesBoost) {
|
||||
public SELF withIndicesBoost(@Nullable List<IndexBoost> indicesBoost) {
|
||||
this.indicesBoost = indicesBoost;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withStoredFields(@Nullable List<String> storedFields) {
|
||||
this.storedFields = storedFields;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIndicesBoost(IndexBoost... indicesBoost) {
|
||||
this.indicesBoost = Arrays.asList(indicesBoost);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withSearchType(Query.SearchType searchType) {
|
||||
this.searchType = searchType;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withTrackTotalHits(@Nullable Boolean trackTotalHits) {
|
||||
this.trackTotalHits = trackTotalHits;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withTrackTotalHitsUpTo(@Nullable Integer trackTotalHitsUpTo) {
|
||||
this.trackTotalHitsUpTo = trackTotalHitsUpTo;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withTimeout(@Nullable Duration timeout) {
|
||||
this.timeout = timeout;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withScrollTime(@Nullable Duration scrollTime) {
|
||||
this.scrollTime = scrollTime;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withExplain(boolean explain) {
|
||||
this.explain = explain;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withSearchAfter(@Nullable List<Object> searchAfter) {
|
||||
this.searchAfter = searchAfter;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withRequestCache(@Nullable Boolean requestCache) {
|
||||
this.requestCache = requestCache;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIdsWithRouting(List<Query.IdWithRouting> idsWithRouting) {
|
||||
|
||||
Assert.notNull(idsWithRouting, "idsWithRouting must not be null");
|
||||
|
||||
this.idsWithRouting.clear();
|
||||
this.idsWithRouting.addAll(idsWithRouting);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withRuntimeFields(List<RuntimeField> runtimeFields) {
|
||||
|
||||
Assert.notNull(runtimeFields, "runtimeFields must not be null");
|
||||
|
||||
this.runtimeFields.clear();
|
||||
this.runtimeFields.addAll(runtimeFields);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withRescorerQueries(List<RescorerQuery> rescorerQueries) {
|
||||
|
||||
Assert.notNull(rescorerQueries, "rescorerQueries must not be null");
|
||||
|
||||
this.rescorerQueries.clear();
|
||||
this.rescorerQueries.addAll(rescorerQueries);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withRescorerQuery(RescorerQuery rescorerQuery) {
|
||||
|
||||
Assert.notNull(rescorerQuery, "rescorerQuery must not be null");
|
||||
|
||||
this.rescorerQueries.add(rescorerQuery);
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.0
|
||||
*/
|
||||
public SELF withPointInTime(@Nullable Query.PointInTime pointInTime) {
|
||||
this.pointInTime = pointInTime;
|
||||
return self();
|
||||
}
|
||||
|
||||
public abstract Q build();
|
||||
|
||||
private SELF self() {
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ public class IndexQueryBuilder {
|
||||
}
|
||||
|
||||
public IndexQueryBuilder withSeqNoPrimaryTerm(SeqNoPrimaryTerm seqNoPrimaryTerm) {
|
||||
this.seqNo = seqNoPrimaryTerm.getSequenceNumber();
|
||||
this.primaryTerm = seqNoPrimaryTerm.getPrimaryTerm();
|
||||
this.seqNo = seqNoPrimaryTerm.sequenceNumber();
|
||||
this.primaryTerm = seqNoPrimaryTerm.primaryTerm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -439,6 +439,15 @@ public interface Query {
|
||||
@Nullable
|
||||
List<IndexBoost> getIndicesBoost();
|
||||
|
||||
/**
|
||||
* @return the point in time id to use in the query
|
||||
* @since 5.0
|
||||
*/
|
||||
@Nullable
|
||||
default PointInTime getPointInTime() {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
@@ -451,25 +460,22 @@ public interface Query {
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
final class IdWithRouting {
|
||||
private final String id;
|
||||
@Nullable private final String routing;
|
||||
|
||||
public IdWithRouting(String id, @Nullable String routing) {
|
||||
record IdWithRouting(String id, @Nullable String routing) {
|
||||
public IdWithRouting {
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
|
||||
this.id = id;
|
||||
this.routing = routing;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRouting() {
|
||||
return routing;
|
||||
}
|
||||
/**
|
||||
* Desscribes the point in time parameters for a query
|
||||
*
|
||||
* @param id the point in time id
|
||||
* @param keepAlive the new keep alive value to be sent with the query
|
||||
* @since 5.0
|
||||
*/
|
||||
record PointInTime(String id, Duration keepAlive) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,45 +26,6 @@ import org.springframework.lang.Nullable;
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class ScriptData {
|
||||
@Nullable private final ScriptType type;
|
||||
@Nullable private final String language;
|
||||
@Nullable private final String script;
|
||||
@Nullable private final String scriptName;
|
||||
@Nullable private final Map<String, Object> params;
|
||||
|
||||
public ScriptData(@Nullable ScriptType type, @Nullable String language, @Nullable String script,
|
||||
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
||||
|
||||
this.type = type;
|
||||
this.language = language;
|
||||
this.script = script;
|
||||
this.scriptName = scriptName;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ScriptType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
public record ScriptData(@Nullable ScriptType type, @Nullable String language, @Nullable String script,
|
||||
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user