Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d4247312d | |||
| 81f52244a8 | |||
| e741df9c7c | |||
| 3875bb6abf | |||
| 1860d7353a | |||
| 4f30a492b9 | |||
| 6805fff1fa | |||
| 0971acfe25 | |||
| 36805c3ecb | |||
| b75d2eb01f | |||
| f94f2b0618 | |||
| 82607b3d4d | |||
| cf9b106c31 | |||
| b0c2ce3084 | |||
| b3f9bdb80f | |||
| d9bf76fb31 | |||
| 4d11a56e84 | |||
| 73d5d623dd | |||
| 5a36f5e1e8 | |||
| 44a5c7545f | |||
| 28489ffee8 | |||
| 605c83f628 | |||
| f3a6a42b82 | |||
| efd394370a | |||
| 4d7d0955f9 | |||
| 2fb90621a2 | |||
| bc7667fdbc | |||
| 2ea568d2e4 | |||
| c460a5f37d | |||
| e1c8a2adeb | |||
| 014aa3dbf6 | |||
| 9446d726bc | |||
| 1fa6c9f3e5 | |||
| 3c7ce7413a | |||
| f6c6f64f04 | |||
| 9f63369c4b | |||
| bae182b162 | |||
| 8db4e430d7 | |||
| c1de8b1e85 | |||
| c7b8848afe | |||
| 0cf1c08d6e | |||
| 81cac1f505 | |||
| ab8cbdf4d9 | |||
| d7e42fcb76 | |||
| f8ddf16c0c | |||
| f79a8016d1 | |||
| 7d38d6c615 | |||
| ceca98492b | |||
| 08f86d8c22 | |||
| 487a599bbb |
@@ -24,3 +24,4 @@ target
|
||||
|
||||
|
||||
/zap.env
|
||||
.localdocker-env
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
#Fri Jun 03 09:32:51 CEST 2022
|
||||
#Mon Jan 30 10:48:19 CET 2023
|
||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
|
||||
|
||||
@@ -169,7 +169,6 @@ We’d love to help!
|
||||
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/elasticsearch/docs/current/api/[Javadocs].
|
||||
* Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation.
|
||||
If you are just starting out with Spring, try one of the https://spring.io/guides[guides].
|
||||
* If you are upgrading, check out the https://docs.spring.io/spring-data/elasticsearch/docs/current/changelog.txt[changelog] for "`new and noteworthy`" features.
|
||||
* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-elasticsearch`].
|
||||
You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter].
|
||||
* Report bugs with Spring Data for Elasticsearch at https://github.com/spring-projects/spring-data-elasticsearch/issues[https://github.com/spring-projects/spring-data-elasticsearch/issues].
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Java versions
|
||||
java.main.tag=17.0.4.1_1-jdk-focal
|
||||
java.main.tag=17.0.6_10-jdk-focal
|
||||
|
||||
# Docker container images - standard
|
||||
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
|
||||
|
||||
# Supported versions of MongoDB
|
||||
docker.mongodb.4.4.version=4.4.17
|
||||
docker.mongodb.5.0.version=5.0.13
|
||||
docker.mongodb.6.0.version=6.0.2
|
||||
docker.mongodb.4.4.version=4.4.18
|
||||
docker.mongodb.5.0.version=5.0.14
|
||||
docker.mongodb.6.0.version=6.0.4
|
||||
|
||||
# Supported versions of Redis
|
||||
docker.redis.6.version=6.2.6
|
||||
docker.redis.6.version=6.2.10
|
||||
|
||||
# Supported versions of Cassandra
|
||||
docker.cassandra.3.version=3.11.14
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.0.0-RC2</version>
|
||||
<version>5.1.0-M1</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.0.0-RC2</version>
|
||||
<version>3.1.0-M1</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,12 +18,12 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<springdata.commons>3.0.0-RC2</springdata.commons>
|
||||
<springdata.commons>3.1.0-M1</springdata.commons>
|
||||
|
||||
<!-- version of the RestHighLevelClient -->
|
||||
<elasticsearch-rhlc>7.17.7</elasticsearch-rhlc>
|
||||
<elasticsearch-rhlc>7.17.9</elasticsearch-rhlc>
|
||||
<!-- version of the new ElasticsearchClient -->
|
||||
<elasticsearch-java>8.5.0</elasticsearch-java>
|
||||
<elasticsearch-java>8.6.1</elasticsearch-java>
|
||||
|
||||
<log4j>2.18.0</log4j>
|
||||
<netty>4.1.65.Final</netty>
|
||||
@@ -193,13 +193,6 @@
|
||||
</dependency>
|
||||
|
||||
<!-- CDI -->
|
||||
<!-- Dependency order required to build against CDI 1.0 and test with CDI 2.0 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jcdi_2.0_spec</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
@@ -211,7 +204,7 @@
|
||||
<dependency>
|
||||
<groupId>jakarta.enterprise</groupId>
|
||||
<artifactId>jakarta.enterprise.cdi-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
@@ -459,7 +452,7 @@
|
||||
</jvmArgs>
|
||||
<excludedGroups>integration-test</excludedGroups>
|
||||
<targetClasses>
|
||||
<param>org.springframework.data.elasticsearch.core.geo.*</param>
|
||||
<param>org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter*</param>
|
||||
</targetClasses>
|
||||
<excludedMethods>toString</excludedMethods>
|
||||
</configuration>
|
||||
|
||||
@@ -12,6 +12,8 @@ NOTE: Copies of this document may be made for your own use and for distribution
|
||||
toc::[]
|
||||
|
||||
include::preface.adoc[]
|
||||
include::{spring-data-commons-docs}/upgrade.adoc[leveloffset=+1]
|
||||
|
||||
|
||||
:leveloffset: +1
|
||||
include::{spring-data-commons-docs}/repositories.adoc[]
|
||||
|
||||
@@ -37,7 +37,8 @@ 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.5.0 | 6.0.x | 3.0.x?
|
||||
| 2022.1 | 5.1.x | 8.6.1 | 6.0.x | 3.0.x
|
||||
| 2022.0 (Turing) | 5.0.x | 8.5.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
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
This chapter illustrates configuration and usage of supported Elasticsearch client implementations.
|
||||
|
||||
Spring Data Elasticsearch operates upon an Elasticsearch client that is connected to a single Elasticsearch node or a cluster.
|
||||
Although the Elasticsearch Client can be used to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
|
||||
Spring Data Elasticsearch operates upon an Elasticsearch client (provided by Elasticsearch client libraries) that is
|
||||
connected to a single Elasticsearch node or a cluster.
|
||||
Although the Elasticsearch Client can be used directly to work with the cluster, applications using Spring Data
|
||||
Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
|
||||
|
||||
[[elasticsearch.clients.restclient]]
|
||||
== Imperative Rest Client
|
||||
@@ -21,14 +23,20 @@ public class MyClientConfig extends ElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public ClientConfiguration clientConfiguration() {
|
||||
return ClientConfiguration.builder() //
|
||||
.connectedTo("localhost:9200") //
|
||||
return ClientConfiguration.builder() <.>
|
||||
.connectedTo("localhost:9200")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
<.> for a detailed description of the builder methods see <<elasticsearch.clients.configuration>>
|
||||
====
|
||||
|
||||
// ...
|
||||
The following beans can then be injected in other Spring components:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
ElasticsearchOperations operations; <.>
|
||||
|
||||
@@ -39,11 +47,8 @@ ElasticsearchClient elasticsearchClient; <.>
|
||||
RestClient restClient; <.>
|
||||
----
|
||||
|
||||
the following can be injected:
|
||||
|
||||
<.> an implementation of `ElasticsearchOperations`
|
||||
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
|
||||
This is new Elasticsearch client implementation.
|
||||
<.> the low level `RestClient` from the Elasticsearch libraries
|
||||
====
|
||||
|
||||
@@ -61,18 +66,24 @@ When working with the reactive stack, the configuration must be derived from a d
|
||||
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration;
|
||||
|
||||
@Configuration
|
||||
public class MyClientConfig extends ElasticsearchConfiguration {
|
||||
public class MyClientConfig extends ReactiveElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public ClientConfiguration clientConfiguration() {
|
||||
return ClientConfiguration.builder() //
|
||||
.connectedTo("localhost:9200") //
|
||||
return ClientConfiguration.builder() <.>
|
||||
.connectedTo("localhost:9200")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
<.> for a detailed description of the builder methods see <<elasticsearch.clients.configuration>>
|
||||
====
|
||||
|
||||
// ...
|
||||
The following beans can then be injected in other Spring components:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
ReactiveElasticsearchOperations operations; <.>
|
||||
|
||||
@@ -85,9 +96,9 @@ RestClient restClient; <.>
|
||||
|
||||
the following can be injected:
|
||||
|
||||
<.> an implementation of `ElasticsearchOperations`
|
||||
<.> an implementation of `ReactiveElasticsearchOperations`
|
||||
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
|
||||
This is based on the new Elasticsearch client implementation.
|
||||
This is a reactive implementation based on the Elasticsearch client implementation.
|
||||
<.> the low level `RestClient` from the Elasticsearch libraries
|
||||
====
|
||||
|
||||
@@ -97,8 +108,14 @@ When using repositories, this instance is used under the hood as well.
|
||||
[[elasticsearch.clients.resthighlevelclient]]
|
||||
== High Level REST Client (deprecated)
|
||||
|
||||
The Java RestHighLevelClient is deprecated, but still can be configured like shown (read
|
||||
<<elasticsearch-migration-guide-4.4-5.0.old-client>> as well):
|
||||
[CAUTION]
|
||||
====
|
||||
The Elasticsearch Java RestHighLevelClient is deprecated, but still can be configured like shown (make sure to read
|
||||
<<elasticsearch-migration-guide-4.4-5.0.old-client>> as well).
|
||||
|
||||
It should only be used to access an Elasticsearch cluster running version 7, even with the compatibility headers set
|
||||
there are cases where the `RestHighLevelClient` cannot read the responses sent from a version 8 cluster.
|
||||
====
|
||||
|
||||
.RestHighLevelClient
|
||||
====
|
||||
@@ -127,15 +144,6 @@ public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
RestHighLevelClient highLevelClient;
|
||||
|
||||
RestClient lowLevelClient = highLevelClient.lowLevelClient(); <3>
|
||||
|
||||
// ...
|
||||
|
||||
IndexRequest request = new IndexRequest("spring-data")
|
||||
.id(randomID())
|
||||
.source(singletonMap("feature", "high-level-rest-client"))
|
||||
.setRefreshPolicy(IMMEDIATE);
|
||||
|
||||
IndexResponse response = highLevelClient.index(request,RequestOptions.DEFAULT);
|
||||
----
|
||||
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
@@ -150,8 +158,12 @@ The `org.springframework.data.elasticsearch.client.erhlc.ReactiveElasticsearchCl
|
||||
It uses the request/response objects provided by the Elasticsearch core project.
|
||||
Calls are directly operated on the reactive stack, **not** wrapping async (thread pool bound) responses into reactive types.
|
||||
|
||||
This was the first reactive implementation Spring Data Elasticsearch provided, but now is deprecated in favour of the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient`
|
||||
[CAUTION]
|
||||
====
|
||||
This was the first reactive implementation Spring Data Elasticsearch provided, but now is deprecated in favour
|
||||
of the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient`
|
||||
which uses the functionality offered by the new Elasticsearch client libraries.
|
||||
====
|
||||
|
||||
.Reactive REST Client (deprecated)
|
||||
====
|
||||
@@ -194,6 +206,11 @@ Client behaviour can be changed via the `ClientConfiguration` that allows to set
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.data.elasticsearch.support.HttpHeaders;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.ElasticsearchClients.*;
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("some-header", "on every request") <.>
|
||||
|
||||
@@ -212,7 +229,7 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
return headers;
|
||||
})
|
||||
.withClientConfigurer( <.>
|
||||
ElasticsearchClients.ElasticsearchClientConfigurationCallback.from(clientBuilder -> {
|
||||
ElasticsearchClientConfigurationCallback.from(clientBuilder -> {
|
||||
// ...
|
||||
return clientBuilder;
|
||||
}))
|
||||
@@ -227,12 +244,10 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
<.> Optionally set a proxy.
|
||||
<.> Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
|
||||
<.> Set the connection timeout.
|
||||
Default is 10 sec.
|
||||
<.> Set the socket timeout.
|
||||
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 `Supplier<HttpHeaders>` 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 to configure the created client (see <<elasticsearch.clients.configuration.callbacks>>), can be added
|
||||
multiple times.
|
||||
====
|
||||
@@ -251,6 +266,8 @@ The following callbacks are provided:
|
||||
[[elasticsearch.clients.configuration.callbacks.rest]]
|
||||
==== Configuration of the low level Elasticsearch `RestClient`:
|
||||
|
||||
This callback provides a `org.elasticsearch.client.RestClientBuilder` that can be used to configure the Elasticsearch
|
||||
`RestClient`:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -266,6 +283,9 @@ ClientConfiguration.builder()
|
||||
[[elasticsearch.clients.configurationcallbacks.httpasync]]
|
||||
==== Configuration of the HttpAsyncClient used by the low level Elasticsearch `RestClient`:
|
||||
|
||||
This callback provides a `org.apache.http.impl.nio.client.HttpAsyncClientBuilder` to configure the HttpCLient that is
|
||||
used by the `RestClient`.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -287,6 +307,9 @@ documentation].
|
||||
|
||||
For the imperative client this must be done by setting the default headers, for the reactive code this must be done using a header supplier:
|
||||
|
||||
CAUTION: Even when these headers are set, there are cases where the response returned from the cluster cannot be
|
||||
parsed with the client. This is not an error in Spring Data Elasticsearch.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -310,12 +333,13 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
[[elasticsearch.clients.logging]]
|
||||
== Client Logging
|
||||
|
||||
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level needs to be turned on as outlined in the snippet below.
|
||||
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level
|
||||
needs to be turned on as outlined in the snippet below. This can be enabled in the Elasticsearch client by setting
|
||||
the level of the `tracer` package to "trace" (see
|
||||
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/java-rest-low-usage-logging.html)
|
||||
|
||||
.Enable transport layer logging
|
||||
[source,xml]
|
||||
----
|
||||
<logger name="org.springframework.data.elasticsearch.client.WIRE" level="trace"/>
|
||||
<logger name="tracer" level="trace"/>
|
||||
----
|
||||
|
||||
NOTE: The above applies to both the `RestHighLevelClient` and `ReactiveElasticsearchClient` when obtained via `RestClients` respectively `ReactiveRestClients`.
|
||||
|
||||
@@ -224,11 +224,11 @@ The following code shows as an example how to retrieve all entries that have a _
|
||||
SearchHits<Statement> hasVotes() {
|
||||
|
||||
Query query = NativeQuery.builder()
|
||||
.withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query.of(qb -> qb //
|
||||
.withQuery(co.elastic.clients.elasticsearch._types.query_dsl.Query.of(qb -> qb
|
||||
.hasChild(hc -> hc
|
||||
.queryName("vote") //
|
||||
.query(matchAllQueryAsQuery()) //
|
||||
.scoreMode(ChildScoreMode.None)//
|
||||
.queryName("vote")
|
||||
.query(matchAllQueryAsQuery())
|
||||
.scoreMode(ChildScoreMode.None)
|
||||
)))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@ This section describes breaking changes from version 4.4.x to 5.0.x and how remo
|
||||
[[elasticsearch-migration-guide-4.4-4.5.deprecations]]
|
||||
== Deprecations
|
||||
|
||||
=== Custom trace level logging
|
||||
|
||||
Logging by setting the property `logging.level.org.springframework.data.elasticsearch.client.WIRE=trace` is
|
||||
deprecated now, the Elasticsearch `RestClient` provides a better solution that can be activated by setting the
|
||||
logging level of the `tracer` package to "trace".
|
||||
|
||||
[[elasticsearch-migration-guide-4.4-4.5.deprecations.package]]
|
||||
=== `org.springframework.data.elasticsearch.client.erhlc` package
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[[elasticsearch-migration-guide-5.0-5.1]]
|
||||
= Upgrading from 5.0.x to 5.1.x
|
||||
|
||||
This section describes breaking changes from version 5.0.x to 5.1.x and how removed features can be replaced by new introduced features.
|
||||
|
||||
[[elasticsearch-migration-guide-5.0-5.1.breaking-changes]]
|
||||
== Breaking Changes
|
||||
|
||||
In the `org.springframework.data.elasticsearch.core.index.AliasData` class, which is used for alias information returned from Elasticsearch, the property `filter` (of type `Document`) is replaced by `filterQuery` which is of type
|
||||
`org.springframework.data.elasticsearch.core.query.Query`.
|
||||
|
||||
`org.springframework.data.elasticsearch.annotations.Similarity` was an enum class until 5.1. This enum was used in the `@Field` annotation to specify a similarity value.
|
||||
But besides the values defined by the enum, it is possible to have similarities with custom names in Elasticsearch.
|
||||
Therefore, the annotation property was changed from the type of the enum to a simple `String`.
|
||||
The previous enum values like `Similarity.Default` do still exist as String constants, so existing code will compile unmodified.
|
||||
Adaptions are necessary when this enum was used at other places than as a property of the `@Field` annotation.
|
||||
@@ -17,7 +17,6 @@ The following arguments are available:
|
||||
* `refreshIntervall`, defaults to _"1s"_
|
||||
* `indexStoreType`, defaults to _"fs"_
|
||||
|
||||
|
||||
It is as well possible to define https://www.elastic.co/guide/en/elasticsearch/reference/7.11/index-modules-index-sorting.html[index sorting] (check the linked Elasticsearch documentation for the possible field types and values):
|
||||
|
||||
====
|
||||
@@ -133,9 +132,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
|
||||
different `ElasticsearchOperations` implementations:
|
||||
There are no methods in the `SearchOperations` API to access the scroll id, if it should be necessary to access this, the following methods of the `AbstractElasticsearchTemplate` can be used (this is the base implementation for the different `ElasticsearchOperations` implementations):
|
||||
|
||||
====
|
||||
[source,java]
|
||||
@@ -281,9 +278,8 @@ 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:
|
||||
`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]
|
||||
@@ -311,8 +307,115 @@ SearchHits<Person> searchHits2 = operations.search(query2, Person.class);
|
||||
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
|
||||
====
|
||||
|
||||
[[elasticsearch.misc.searchtemplates]]
|
||||
== Search Template support
|
||||
|
||||
Use of the search template API is supported.
|
||||
To use this, it first is necessary to create a stored script.
|
||||
The `ElasticsearchOperations` interface extends `ScriptOperations` which provides the necessary functions.
|
||||
The example used here assumes that we have `Person` entity with a property named `firstName`.
|
||||
A search template script can be saved like this:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.script.Script;
|
||||
|
||||
operations.putScript( <.>
|
||||
Script.builder()
|
||||
.withId("person-firstname") <.>
|
||||
.withLanguage("mustache") <.>
|
||||
.withSource(""" <.>
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"match": {
|
||||
"firstName": "{{firstName}}" <.>
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": "{{from}}", <.>
|
||||
"size": "{{size}}" <.>
|
||||
}
|
||||
""")
|
||||
.build()
|
||||
);
|
||||
----
|
||||
|
||||
<.> Use the `putScript()` method to store a search template script
|
||||
<.> The name / id of the script
|
||||
<.> Scripts that are used in search templates must be in the _mustache_ language.
|
||||
<.> The script source
|
||||
<.> The search parameter in the script
|
||||
<.> Paging request offset
|
||||
<.> Paging request size
|
||||
====
|
||||
|
||||
To use a search template in a search query, Spring Data Elasticsearch provides the `SearchTemplateQuery`, an implementation of the `org.springframework.data.elasticsearch.core.query.Query` interface.
|
||||
|
||||
In the following code, we will add a call using a search template query to a custom repository implementation (see
|
||||
<<repositories.custom-implementations>>) as
|
||||
an example how this can be integrated into a repository call.
|
||||
|
||||
We first define the custom repository fragment interface:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface PersonCustomRepository {
|
||||
SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The implementation of this repository fragment looks like this:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class PersonCustomRepositoryImpl implements PersonCustomRepository {
|
||||
|
||||
private final ElasticsearchOperations operations;
|
||||
|
||||
public PersonCustomRepositoryImpl(ElasticsearchOperations operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchPage<Person> findByFirstNameWithSearchTemplate(String firstName, Pageable pageable) {
|
||||
|
||||
var query = SearchTemplateQuery.builder() <.>
|
||||
.withId("person-firstname") <.>
|
||||
.withParams(
|
||||
Map.of( <.>
|
||||
"firstName", firstName,
|
||||
"from", pageable.getOffset(),
|
||||
"size", pageable.getPageSize()
|
||||
)
|
||||
)
|
||||
.build();
|
||||
|
||||
SearchHits<Person> searchHits = operations.search(query, Person.class); <.>
|
||||
|
||||
return SearchHitSupport.searchPageFor(searchHits, pageable);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<.> Create a `SearchTemplateQuery`
|
||||
<.> Provide the id of the search template
|
||||
<.> The parameters are passed in a `Map<String,Object>`
|
||||
<.> Do the search in the same way as with the other query types.
|
||||
====
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
[[new-features]]
|
||||
= What's new
|
||||
|
||||
[[new-features.5-1-0]]
|
||||
== New in Spring Data Elasticsearch 5.1
|
||||
|
||||
* Upgrade to Elasticsearch 8.6.1
|
||||
|
||||
[[new-features.5-0-0]]
|
||||
== New in Spring Data Elasticsearch 5.0
|
||||
|
||||
* Upgrade to Java 17 baseline
|
||||
* Upgrade to Spring Framework 6
|
||||
* Upgrade to Elasticsearch 8.5.0
|
||||
* Use the new Elasticsearch client library
|
||||
|
||||
[[new-features.4-4-0]]
|
||||
|
||||
@@ -2,19 +2,8 @@
|
||||
= Elasticsearch Object Mapping
|
||||
|
||||
Spring Data Elasticsearch Object Mapping is the process that maps a Java object - the domain entity - into the JSON representation that is stored in Elasticsearch and back.
|
||||
|
||||
Earlier versions of Spring Data Elasticsearch used a Jackson based conversion, Spring Data Elasticsearch 3.2.x introduced the <<elasticsearch.mapping.meta-model>>.
|
||||
As of version 4.0 only the Meta Object Mapping is used, the Jackson based mapper is not available anymore and the `MappingElasticsearchConverter` is used.
|
||||
|
||||
The main reasons for the removal of the Jackson based mapper are:
|
||||
|
||||
* Custom mappings of fields needed to be done with annotations like `@JsonFormat` or `@JsonInclude`.
|
||||
This often caused problems when the same object was used in different JSON based datastores or sent over a JSON based API.
|
||||
* Custom field types and formats also need to be stored into the Elasticsearch index mappings.
|
||||
The Jackson based annotations did not fully provide all the information that is necessary to represent the types of Elasticsearch.
|
||||
* Fields must be mapped not only when converting from and to entities, but also in query argument, returned data and on other places.
|
||||
|
||||
Using the `MappingElasticsearchConverter` now covers all these cases.
|
||||
The class that is internally used for this mapping is the
|
||||
`MappingElasticsearchConverter`.
|
||||
|
||||
[[elasticsearch.mapping.meta-model]]
|
||||
== Meta Model Object Mapping
|
||||
@@ -31,17 +20,16 @@ The metadata is taken from the entity's properties which can be annotated.
|
||||
The following annotations are available:
|
||||
|
||||
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database.
|
||||
The most important attributes are:
|
||||
The most important attributes are (check the API documentation for the complete list of attributes):
|
||||
** `indexName`: the name of the index to store this entity in.
|
||||
This can contain a SpEL template expression like `"log-#{T(java.time.LocalDate).now().toString()}"`
|
||||
** `createIndex`: flag whether to create an index on repository bootstrapping.
|
||||
Default value is _true_.
|
||||
See <<elasticsearch.repositories.autocreation>>
|
||||
** `versionType`: Configuration of version management.
|
||||
Default value is _EXTERNAL_.
|
||||
|
||||
|
||||
* `@Id`: Applied at the field level to mark the field used for identity purpose.
|
||||
* `@Transient`: By default all fields are mapped to the document when it is stored or retrieved, this annotation excludes the field.
|
||||
* `@Transient`, `@ReadOnlyProperty`, `@WriteOnlyProperty`: see the following section <<elasticsearch.mapping.meta-model.annotations.read-write>> for detailed information.
|
||||
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database.
|
||||
Constructor arguments are mapped by name to the key values in the retrieved Document.
|
||||
* `@Field`: Applied at the field level and defines properties of the field, most of the attributes map to the respective https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html[Elasticsearch Mapping] definitions (the following list is not complete, check the annotation Javadoc for a complete reference):
|
||||
@@ -61,6 +49,19 @@ In difference to a registered Spring `Converter` this only converts the annotate
|
||||
|
||||
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations.read-write]]
|
||||
==== Controlling which properties are written to and read from Elasticsearch
|
||||
|
||||
This section details the annotations that define if the value of a property is written to or read from Elasticsearch.
|
||||
|
||||
`@Transient`: A property annotated with this annotation will not be written to the mapping, it's value will not be sent to Elasticsearch and when documents are returned from Elasticsearch, this property will not be set in the resulting entity.
|
||||
|
||||
`@ReadOnlyProperty`: A property with this annotaiton will not have its value written to Elasticsearch, but when returning data, the proeprty will be filled with the value returned in the document from Elasticsearch.
|
||||
One use case for this are runtime fields defined in the index mapping.
|
||||
|
||||
`@WriteOnlyProperty`: A property with this annotaiton will have its value stored in Elasticsearch but will not be set with any value when reading document.
|
||||
This can be used for example for synthesized fields which should go into the Elasticsearch index but are not used elsewhere.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations.date-formats]]
|
||||
==== Date format mapping
|
||||
|
||||
@@ -103,6 +104,8 @@ The following table shows the different attributes and the mapping created from
|
||||
NOTE: If you are using a custom date format, you need to use _uuuu_ for the year instead of _yyyy_.
|
||||
This is due to a https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats[change in Elasticsearch 7].
|
||||
|
||||
Check the code of the `org.springframework.data.elasticsearch.annotations.DateFormat` enum for a complete list of predefined values and their patterns.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations.range]]
|
||||
==== Range types
|
||||
|
||||
@@ -162,12 +165,13 @@ A `FieldNamingStrategy` applies to all entities; it can be overwritten by settin
|
||||
[[elasticsearch.mapping.meta-model.annotations.non-field-backed-properties]]
|
||||
==== Non-field-backed properties
|
||||
|
||||
Normally the properties used in an entity are fields of the entity class. There might be cases, when a property value
|
||||
is calculated in the entity and should be stored in Elasticsearch. In this case, the getter method (`getProperty()`) can be
|
||||
annotated
|
||||
with the `@Field` annotation, in addition to that the method must be annotated with `@AccessType(AccessType.Type
|
||||
.PROPERTY)`. The third annotation that is needed in such a case is `@WriteOnlyProperty`, as such a value is only
|
||||
written to Elasticsearch. A full example:
|
||||
Normally the properties used in an entity are fields of the entity class.
|
||||
There might be cases, when a property value is calculated in the entity and should be stored in Elasticsearch.
|
||||
In this case, the getter method (`getProperty()`) can be annotated with the `@Field` annotation, in addition to that the method must be annotated with `@AccessType(AccessType.Type
|
||||
.PROPERTY)`.
|
||||
The third annotation that is needed in such a case is `@WriteOnlyProperty`, as such a value is only written to Elasticsearch.
|
||||
A full example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -180,6 +184,19 @@ public String getProperty() {
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations.misc]]
|
||||
==== Other property annotations
|
||||
|
||||
===== @IndexedIndexName
|
||||
|
||||
This annotation can be set on a String property of an entity.
|
||||
This property will not be written to the mapping, it will not be stored in Elasticsearch and its value will not be read from an Elasticsearch document.
|
||||
After an entity is persisted, for example with a call to `ElasticsearchOperations.save(T entity)`, the entity
|
||||
returned from that call will contain the name of the index that an entity was saved to in that property.
|
||||
This is useful when the index name is dynamically set by a bean, or when writing to a write alias.
|
||||
|
||||
Putting some value into such a property does not set the index into which an entity is stored!
|
||||
|
||||
[[elasticsearch.mapping.meta-model.rules]]
|
||||
=== Mapping Rules
|
||||
|
||||
@@ -194,7 +211,6 @@ Those type hints are represented as `_class` attributes within the document and
|
||||
[source,java]
|
||||
----
|
||||
public class Person { <1>
|
||||
|
||||
@Id String id;
|
||||
String firstname;
|
||||
String lastname;
|
||||
@@ -274,7 +290,6 @@ Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
|
||||
[source,java]
|
||||
----
|
||||
public class Address {
|
||||
|
||||
String city, street;
|
||||
Point location;
|
||||
}
|
||||
@@ -404,12 +419,15 @@ Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, prev
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config extends AbstractElasticsearchConfiguration {
|
||||
public class Config extends ElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public ClientConfiguration clientConfiguration() {
|
||||
return ClientConfiguration.builder() //
|
||||
.connectedTo("localhost:9200") //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
@@ -420,7 +438,7 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@WritingConverter <2>
|
||||
static class AddressToMap implements Converter<Address, Map<String, Object>> {
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> convert(Address source) {
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ There is support for automatic creation of indices and writing the mappings when
|
||||
== Usage examples
|
||||
|
||||
The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller.
|
||||
The example assumes that `Person` is a class that is annotated with `@Document`, `@Id` etc (see <<elasticsearch.mapping.meta-model.annotations>>).
|
||||
|
||||
.ElasticsearchOperations usage
|
||||
====
|
||||
@@ -45,34 +46,29 @@ public class TestController {
|
||||
|
||||
private ElasticsearchOperations elasticsearchOperations;
|
||||
|
||||
public TestController(ElasticsearchOperations elasticsearchOperations) { <1>
|
||||
public TestController(ElasticsearchOperations elasticsearchOperations) { <.>
|
||||
this.elasticsearchOperations = elasticsearchOperations;
|
||||
}
|
||||
|
||||
@PostMapping("/person")
|
||||
public String save(@RequestBody Person person) { <2>
|
||||
|
||||
IndexQuery indexQuery = new IndexQueryBuilder()
|
||||
.withId(person.getId().toString())
|
||||
.withObject(person)
|
||||
.build();
|
||||
String documentId = elasticsearchOperations.index(indexQuery);
|
||||
return documentId;
|
||||
public String save(@RequestBody Person person) { <.>
|
||||
Person savedEntity = elasticsearchOperations.save(person);
|
||||
return savedEntity.getId();
|
||||
}
|
||||
|
||||
@GetMapping("/person/{id}")
|
||||
public Person findById(@PathVariable("id") Long id) { <3>
|
||||
Person person = elasticsearchOperations
|
||||
.queryForObject(GetQuery.getById(id.toString()), Person.class);
|
||||
public Person findById(@PathVariable("id") Long id) { <.>
|
||||
Person person = elasticsearchOperations.get(id.toString(), Person.class);
|
||||
return person;
|
||||
}
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
<1> Let Spring inject the provided `ElasticsearchOperations` bean in the constructor.
|
||||
<2> Store some entity in the Elasticsearch cluster.
|
||||
<3> Retrieve the entity with a query by id.
|
||||
<.> Let Spring inject the provided `ElasticsearchOperations` bean in the constructor.
|
||||
<.> Store some entity in the Elasticsearch cluster.
|
||||
The id is read from the returned entity, as it might have been null in the `person` object and been created by Elasticsearch.
|
||||
<.> Retrieve the entity with a get by id.
|
||||
====
|
||||
|
||||
To see the full possibilities of `ElasticsearchOperations` please refer to the API documentation.
|
||||
@@ -151,7 +147,7 @@ Conditions for the same field can be chained, they will be combined with a logic
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0L);
|
||||
Criteria criteria = new Criteria("price").greaterThan(42.0).lessThan(34.0);
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
====
|
||||
@@ -238,3 +234,9 @@ Query query = NativeQuery.builder()
|
||||
SearchHits<Person> searchHits = operations.search(query, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.operations.searchtemplateScOp§query]]
|
||||
=== SearchTemplateQuery
|
||||
|
||||
This is a special implementation of the `Query` interface to be used in combination with a stored search template.
|
||||
See <<elasticsearch.misc.searchtemplates>> for further information.
|
||||
|
||||
@@ -5,80 +5,22 @@
|
||||
|
||||
The `ReactiveElasticsearchTemplate` is the default implementation of `ReactiveElasticsearchOperations`.
|
||||
|
||||
[[elasticsearch.reactive.template]]
|
||||
== Reactive Elasticsearch Template
|
||||
[[elasticsearch.reactive.operations]]
|
||||
== Reactive Elasticsearch Operations
|
||||
|
||||
To get started the `ReactiveElasticsearchTemplate` needs to know about the actual client to work with.
|
||||
Please see <<elasticsearch.clients.reactive>> for details on the client.
|
||||
To get started the `ReactiveElasticsearchOperations` needs to know about the actual client to work with.
|
||||
Please see <<elasticsearch.clients.reactiverestclient>> for details on the client and how to configure it.
|
||||
|
||||
[[elasticsearch.reactive.template.configuration]]
|
||||
=== Reactive Template Configuration
|
||||
[[elasticsearch.reactive.operations.usage]]
|
||||
=== Reactive Operations Usage
|
||||
|
||||
The easiest way of setting up the `ReactiveElasticsearchTemplate` is via `AbstractReactiveElasticsearchConfiguration` providing
|
||||
dedicated configuration method hooks for `base package`, the `initial entity set` etc.
|
||||
|
||||
.The AbstractReactiveElasticsearchConfiguration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config extends AbstractReactiveElasticsearchConfiguration {
|
||||
|
||||
@Bean <1>
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
|
||||
====
|
||||
|
||||
NOTE: If applicable set default `HttpHeaders` via the `ClientConfiguration` of the `ReactiveElasticsearchClient`. See <<elasticsearch.clients.configuration>>.
|
||||
|
||||
TIP: If needed the `ReactiveElasticsearchTemplate` can be configured with default `RefreshPolicy` and `IndicesOptions` that get applied to the related requests by overriding the defaults of `refreshPolicy()` and `indicesOptions()`.
|
||||
|
||||
However one might want to be more in control over the actual components and use a more verbose approach.
|
||||
|
||||
.Configure the ReactiveElasticsearchTemplate
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean <1>
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
@Bean <2>
|
||||
public ElasticsearchConverter elasticsearchConverter() {
|
||||
return new MappingElasticsearchConverter(elasticsearchMappingContext());
|
||||
}
|
||||
@Bean <3>
|
||||
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
|
||||
return new SimpleElasticsearchMappingContext();
|
||||
}
|
||||
@Bean <4>
|
||||
public ReactiveElasticsearchOperations reactiveElasticsearchOperations() {
|
||||
return new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(), elasticsearchConverter());
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
|
||||
<2> Set up the `ElasticsearchConverter` used for domain type mapping utilizing metadata provided by the mapping context.
|
||||
<3> The Elasticsearch specific mapping context for domain type metadata.
|
||||
<4> The actual template based on the client and conversion infrastructure.
|
||||
====
|
||||
|
||||
[[elasticsearch.reactive.template.usage]]
|
||||
=== Reactive Template Usage
|
||||
|
||||
`ReactiveElasticsearchTemplate` lets you save, find and delete your domain objects and map those objects to documents stored in Elasticsearch.
|
||||
`ReactiveElasticsearchOperations` lets you save, find and delete your domain objects and map those objects to documents
|
||||
stored
|
||||
in Elasticsearch.
|
||||
|
||||
Consider the following:
|
||||
|
||||
.Use the ReactiveElasticsearchTemplate
|
||||
.Use the ReactiveElasticsearchOperations
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -94,15 +36,20 @@ public class Person {
|
||||
|
||||
[source,java]
|
||||
----
|
||||
template.save(new Person("Bruce Banner", 42)) <1>
|
||||
|
||||
ReactiveElasticsearchOperations operations;
|
||||
|
||||
// ...
|
||||
|
||||
operations.save(new Person("Bruce Banner", 42)) <.>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.findById(person.id, Person.class)) <2>
|
||||
.flatMap(person -> operations.get(person.id, Person.class)) <.>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.delete(person)) <3>
|
||||
.flatMap(person -> operations.delete(person)) <.>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(id -> template.count(Person.class)) <4>
|
||||
.flatMap(id -> operations.count(Person.class)) <.>
|
||||
.doOnNext(System.out::println)
|
||||
.subscribe(); <5>
|
||||
.subscribe(); <.>
|
||||
----
|
||||
|
||||
The above outputs the following sequence on the console.
|
||||
@@ -114,11 +61,10 @@ The above outputs the following sequence on the console.
|
||||
> QjWCWWcBXiLAnp77ksfR
|
||||
> 0
|
||||
----
|
||||
<1> Insert a new `Person` document into the _marvel_ index under type _characters_. The `id` is generated on server side and set into the instance returned.
|
||||
<2> Lookup the `Person` with matching `id` in the _marvel_ index under type _characters_.
|
||||
<3> Delete the `Person` with matching `id`, extracted from the given instance, in the _marvel_ index under type _characters_.
|
||||
<4> Count the total number of documents in the _marvel_ index under type _characters_.
|
||||
<5> Don't forget to _subscribe()_.
|
||||
<.> Insert a new `Person` document into the _marvel_ index . The `id` is generated on server
|
||||
side and set into the instance returned.
|
||||
<.> Lookup the `Person` with matching `id` in the _marvel_ index.
|
||||
<.> Delete the `Person` with matching `id`, extracted from the given instance, in the _marvel_ index.
|
||||
<.> Count the total number of documents in the _marvel_ index.
|
||||
<.> Don't forget to _subscribe()_.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2022-2023 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;
|
||||
|
||||
import org.springframework.dao.NonTransientDataAccessResourceException;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 5.1
|
||||
*/
|
||||
public class ResourceNotFoundException extends NonTransientDataAccessResourceException {
|
||||
|
||||
public ResourceNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
@@ -77,6 +77,14 @@ public @interface Document {
|
||||
*/
|
||||
Dynamic dynamic() default Dynamic.INHERIT;
|
||||
|
||||
/**
|
||||
* Specifies if the id property should also be stored in the Elasticsearch document source. Default value is
|
||||
* {@literal true}
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
boolean storeIdInSource() default true;
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -46,7 +46,7 @@ public enum Dynamic {
|
||||
/**
|
||||
* Inherit the dynamic setting from their parent object or from the mapping type.
|
||||
*/
|
||||
INHERIT("nherit");
|
||||
INHERIT("inherit");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
@@ -140,7 +140,7 @@ public @interface Field {
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
Similarity similarity() default Similarity.Default;
|
||||
String similarity() default Similarity.Default;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
* Copyright 2017-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark a String property of an entity to be filled with the name of the index where the entity was
|
||||
* stored after it is indexed into Elasticsearch. This can be used when the name of the index is dynamically created
|
||||
* or when a document was indexed into a write alias.
|
||||
*
|
||||
* This can not be used to specify the index where an entity should be written to.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 5.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@Field(type = FieldType.Auto) // prevents the property being written to the index mapping
|
||||
public @interface IndexedIndexName {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2023 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.
|
||||
@@ -109,7 +109,7 @@ public @interface InnerField {
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
Similarity similarity() default Similarity.Default;
|
||||
String similarity() default Similarity.Default;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2022 the original author or authors.
|
||||
* Copyright 2020-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
* Copyright 2013-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
* Copyright 2014-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
@@ -19,18 +19,9 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public enum Similarity {
|
||||
Default("default"), BM25("BM25"), classic("classic"), Boolean("boolean");
|
||||
|
||||
// need to use a custom name because 'boolean' can't be used as enum name
|
||||
private final String toStringName;
|
||||
|
||||
Similarity(String name) {
|
||||
this.toStringName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringName;
|
||||
}
|
||||
public final class Similarity {
|
||||
public final static String Default = "default";
|
||||
public final static String BM25 = "BM25";
|
||||
public final static String classic = "classic";
|
||||
public final static String Boolean = "boolean";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
* Copyright 2019-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2023 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.aot;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 5.1
|
||||
*/
|
||||
public class ElasticsearchAotPredicates {
|
||||
|
||||
public static final Predicate<ReactiveWrappers.ReactiveLibrary> IS_REACTIVE_LIBARARY_AVAILABLE = (
|
||||
lib) -> ReactiveWrappers.isAvailable(lib);
|
||||
|
||||
public static boolean isReactorPresent() {
|
||||
return IS_REACTIVE_LIBARARY_AVAILABLE.test(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR);
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2023 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.aot;
|
||||
|
||||
import static org.springframework.data.elasticsearch.aot.ElasticsearchAotPredicates.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.data.elasticsearch.client.elc.EntityAsMap;
|
||||
import org.springframework.data.elasticsearch.core.event.AfterConvertCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.AfterLoadCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.AfterSaveCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.AuditingEntityCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.BeforeConvertCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.ReactiveAfterConvertCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.ReactiveAfterLoadCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.ReactiveAfterSaveCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.ReactiveAuditingEntityCallback;
|
||||
import org.springframework.data.elasticsearch.core.event.ReactiveBeforeConvertCallback;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 5.1
|
||||
*/
|
||||
public class ElasticsearchRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection().registerTypes( //
|
||||
Arrays.asList( //
|
||||
TypeReference.of(AfterConvertCallback.class), //
|
||||
TypeReference.of(AfterLoadCallback.class), //
|
||||
TypeReference.of(AfterSaveCallback.class), //
|
||||
TypeReference.of(BeforeConvertCallback.class), //
|
||||
TypeReference.of(EntityAsMap.class) //
|
||||
), //
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
|
||||
if (isReactorPresent()) {
|
||||
hints.reflection().registerTypes( //
|
||||
Arrays.asList( //
|
||||
TypeReference.of(ReactiveAfterConvertCallback.class), //
|
||||
TypeReference.of(ReactiveAfterLoadCallback.class), //
|
||||
TypeReference.of(ReactiveAfterSaveCallback.class), //
|
||||
TypeReference.of(ReactiveBeforeConvertCallback.class) //
|
||||
), //
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
}
|
||||
|
||||
// properties needed to log the different versions
|
||||
hints.resources().registerPattern("versions.properties");
|
||||
hints.resources().registerPattern("co/elastic/clients/version.properties");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.aot;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
@@ -31,7 +31,9 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Oliver Drotbohm
|
||||
* @since 3.2
|
||||
* @deprecated since 5.0, Elasticsearch's RestClient has a trace level logging available.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ClientLogger {
|
||||
|
||||
private static final Log WIRE_LOGGER = LogFactory.getLog("org.springframework.data.elasticsearch.client.WIRE");
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
* Copyright 2018-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.Queries.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
|
||||
* query.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Ezequiel Antúnez Camacho
|
||||
* @since 4.4
|
||||
*/
|
||||
class CriteriaQueryProcessor {
|
||||
@@ -329,6 +330,13 @@ class CriteriaQueryProcessor {
|
||||
throw new CriteriaQueryException("value for " + fieldName + " is not an Iterable");
|
||||
}
|
||||
break;
|
||||
case REGEXP:
|
||||
queryBuilder //
|
||||
.regexp(rb -> rb //
|
||||
.field(fieldName) //
|
||||
.value(value.toString()) //
|
||||
.boost(boost)); //
|
||||
break;
|
||||
default:
|
||||
throw new CriteriaQueryException("Could not build query for " + entry);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -290,7 +290,9 @@ public final class ElasticsearchClients {
|
||||
*
|
||||
* @see ClientLogger
|
||||
* @since 4.4
|
||||
* @deprecated since 5.0
|
||||
*/
|
||||
@Deprecated
|
||||
private static class HttpLoggingInterceptor implements HttpResponseInterceptor, HttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+18
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.ResourceNotFoundException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
|
||||
/**
|
||||
@@ -74,22 +75,28 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
if (ex instanceof ElasticsearchException elasticsearchException) {
|
||||
|
||||
ErrorResponse response = elasticsearchException.response();
|
||||
var errorType = response.error().type();
|
||||
var errorReason = response.error().reason() != null ? response.error().reason() : "undefined reason";
|
||||
|
||||
if (response.status() == 404 && "index_not_found_exception".equals(response.error().type())) {
|
||||
if (response.status() == 404) {
|
||||
|
||||
// noinspection RegExpRedundantEscape
|
||||
Pattern pattern = Pattern.compile(".*no such index \\[(.*)\\]");
|
||||
String index = "";
|
||||
Matcher matcher = pattern.matcher(response.error().reason());
|
||||
if (matcher.matches()) {
|
||||
index = matcher.group(1);
|
||||
if ("index_not_found_exception".equals(errorType)) {
|
||||
// noinspection RegExpRedundantEscape
|
||||
Pattern pattern = Pattern.compile(".*no such index \\[(.*)\\]");
|
||||
String index = "";
|
||||
Matcher matcher = pattern.matcher(errorReason);
|
||||
if (matcher.matches()) {
|
||||
index = matcher.group(1);
|
||||
}
|
||||
return new NoSuchIndexException(index);
|
||||
}
|
||||
return new NoSuchIndexException(index);
|
||||
|
||||
return new ResourceNotFoundException(errorReason);
|
||||
}
|
||||
String body = JsonUtils.toJson(response, jsonpMapper);
|
||||
|
||||
if (response.error().type().contains("validation_exception")) {
|
||||
return new DataIntegrityViolationException(response.error().reason());
|
||||
if (errorType != null && errorType.contains("validation_exception")) {
|
||||
return new DataIntegrityViolationException(errorReason);
|
||||
}
|
||||
|
||||
return new UncategorizedElasticsearchException(ex.getMessage(), response.status(), body, ex);
|
||||
|
||||
+85
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -55,10 +55,12 @@ import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
|
||||
import org.springframework.data.elasticsearch.core.script.Script;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -67,6 +69,7 @@ import org.springframework.util.Assert;
|
||||
* Elasticsearch client.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Hamid Rahimi
|
||||
* @since 4.4
|
||||
*/
|
||||
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
@@ -215,8 +218,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.id(),
|
||||
indexResponse.seqNo(), indexResponse.primaryTerm(), indexResponse.version())));
|
||||
query.setObject(updateIndexedObject(queryObject, new IndexedObjectInformation(indexResponse.id(),
|
||||
indexResponse.index(), indexResponse.seqNo(), indexResponse.primaryTerm(), indexResponse.version())));
|
||||
}
|
||||
|
||||
return indexResponse.id();
|
||||
@@ -296,7 +299,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
@Override
|
||||
public String getRuntimeLibraryVersion() {
|
||||
return Version.VERSION.toString();
|
||||
return Version.VERSION != null ? Version.VERSION.toString() : "0.0.0.?";
|
||||
}
|
||||
|
||||
// region search operations
|
||||
@@ -306,7 +309,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, true, false);
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, true);
|
||||
|
||||
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
|
||||
|
||||
@@ -317,11 +320,21 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
|
||||
if (query instanceof SearchTemplateQuery searchTemplateQuery) {
|
||||
return doSearch(searchTemplateQuery, clazz, index);
|
||||
} else {
|
||||
return doSearch(query, clazz, index);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> SearchHits<T> doSearch(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false);
|
||||
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
|
||||
|
||||
// noinspection DuplicatedCode
|
||||
ReadDocumentCallback<T> readDocumentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator = getEntityCreator(readDocumentCallback);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
@@ -329,6 +342,18 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return callback.doWith(SearchDocumentResponseBuilder.from(searchResponse, entityCreator, jsonpMapper));
|
||||
}
|
||||
|
||||
protected <T> SearchHits<T> doSearch(SearchTemplateQuery query, Class<T> clazz, IndexCoordinates index) {
|
||||
var searchTemplateRequest = requestConverter.searchTemplate(query, index);
|
||||
var searchTemplateResponse = execute(client -> client.searchTemplate(searchTemplateRequest, EntityAsMap.class));
|
||||
|
||||
// noinspection DuplicatedCode
|
||||
ReadDocumentCallback<T> readDocumentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator = getEntityCreator(readDocumentCallback);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
|
||||
return callback.doWith(SearchDocumentResponseBuilder.from(searchTemplateResponse, entityCreator, jsonpMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> SearchHits<T> doSearch(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
@@ -441,6 +466,29 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return doMultiSearch(multiSearchQueryParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
|
||||
List<IndexCoordinates> indexes) {
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(classes, "classes must not be null");
|
||||
Assert.notNull(indexes, "indexes must not be null");
|
||||
Assert.isTrue(queries.size() == classes.size() && queries.size() == indexes.size(),
|
||||
"queries, classes and indexes must have the same size");
|
||||
|
||||
List<MultiSearchQueryParameter> multiSearchQueryParameters = new ArrayList<>(queries.size());
|
||||
Iterator<Class<?>> it = classes.iterator();
|
||||
Iterator<IndexCoordinates> indexesIt = indexes.iterator();
|
||||
|
||||
for (Query query : queries) {
|
||||
Class<?> clazz = it.next();
|
||||
IndexCoordinates index = indexesIt.next();
|
||||
multiSearchQueryParameters.add(new MultiSearchQueryParameter(query, clazz, index));
|
||||
}
|
||||
|
||||
return doMultiSearch(multiSearchQueryParameters);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private List<SearchHits<?>> doMultiSearch(List<MultiSearchQueryParameter> multiSearchQueryParameters) {
|
||||
|
||||
@@ -513,6 +561,35 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
// endregion
|
||||
|
||||
// region script methods
|
||||
@Override
|
||||
public boolean putScript(Script script) {
|
||||
|
||||
Assert.notNull(script, "script must not be null");
|
||||
|
||||
var request = requestConverter.scriptPut(script);
|
||||
return execute(client -> client.putScript(request)).acknowledged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Script getScript(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
var request = requestConverter.scriptGet(name);
|
||||
return responseConverter.scriptResponse(execute(client -> client.getScript(request)));
|
||||
}
|
||||
|
||||
public boolean deleteScript(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
DeleteScriptRequest request = requestConverter.scriptDelete(name);
|
||||
return execute(client -> client.deleteScript(request)).acknowledged();
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region client callback
|
||||
/**
|
||||
* Callback interface to be used with {@link #execute(ElasticsearchTemplate.ClientCallback)} for operating directly on
|
||||
@@ -575,8 +652,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
failedDocuments);
|
||||
}
|
||||
|
||||
return bulkResponse.items().stream()
|
||||
.map(item -> IndexedObjectInformation.of(item.id(), item.seqNo(), item.primaryTerm(), item.version()))
|
||||
return bulkResponse.items().stream().map(
|
||||
item -> new IndexedObjectInformation(item.id(), item.index(), item.seqNo(), item.primaryTerm(), item.version()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.KnnQuery;
|
||||
import co.elastic.clients.elasticsearch._types.SortOptions;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
@@ -30,6 +31,7 @@ import java.util.Map;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.data.elasticsearch.core.query.Query} implementation using query builders from the new
|
||||
@@ -42,6 +44,7 @@ import org.springframework.lang.Nullable;
|
||||
public class NativeQuery extends BaseQuery {
|
||||
|
||||
@Nullable private final Query query;
|
||||
@Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
|
||||
@Nullable private Query filter;
|
||||
// note: the new client does not have pipeline aggs, these are just set up as normal aggs
|
||||
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
|
||||
@@ -51,6 +54,7 @@ public class NativeQuery extends BaseQuery {
|
||||
private List<SortOptions> sortOptions = Collections.emptyList();
|
||||
|
||||
private Map<String, JsonData> searchExtensions = Collections.emptyMap();
|
||||
@Nullable private KnnQuery knnQuery;
|
||||
|
||||
public NativeQuery(NativeQueryBuilder builder) {
|
||||
super(builder);
|
||||
@@ -62,6 +66,13 @@ public class NativeQuery extends BaseQuery {
|
||||
this.scriptedFields = builder.getScriptedFields();
|
||||
this.sortOptions = builder.getSortOptions();
|
||||
this.searchExtensions = builder.getSearchExtensions();
|
||||
|
||||
if (builder.getSpringDataQuery() != null) {
|
||||
Assert.isTrue(!NativeQuery.class.isAssignableFrom(builder.getSpringDataQuery().getClass()),
|
||||
"Cannot add an NativeQuery in a NativeQuery");
|
||||
}
|
||||
this.springDataQuery = builder.getSpringDataQuery();
|
||||
this.knnQuery = builder.getKnnQuery();
|
||||
}
|
||||
|
||||
public NativeQuery(@Nullable Query query) {
|
||||
@@ -107,4 +118,25 @@ public class NativeQuery extends BaseQuery {
|
||||
public Map<String, JsonData> getSearchExtensions() {
|
||||
return searchExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NativeQueryBuilder#withQuery(org.springframework.data.elasticsearch.core.query.Query).
|
||||
* @since 5.1
|
||||
*/
|
||||
public void setSpringDataQuery(@Nullable org.springframework.data.elasticsearch.core.query.Query springDataQuery) {
|
||||
this.springDataQuery = springDataQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
@Nullable
|
||||
public KnnQuery getKnnQuery() {
|
||||
return knnQuery;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
|
||||
return springDataQuery;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.KnnQuery;
|
||||
import co.elastic.clients.elasticsearch._types.SortOptions;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
@@ -51,6 +52,9 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
private List<SortOptions> sortOptions = new ArrayList<>();
|
||||
private Map<String, JsonData> searchExtensions = new LinkedHashMap<>();
|
||||
|
||||
@Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
|
||||
@Nullable private KnnQuery knnQuery;
|
||||
|
||||
public NativeQueryBuilder() {}
|
||||
|
||||
@Nullable
|
||||
@@ -89,6 +93,16 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return this.searchExtensions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KnnQuery getKnnQuery() {
|
||||
return knnQuery;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
|
||||
return springDataQuery;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withQuery(Query query) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
@@ -188,7 +202,28 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to use a {@link org.springframework.data.elasticsearch.core.query.Query} within a NativeQuery. Cannot be
|
||||
* used together with {@link #withQuery(Query)} that sets an Elasticsearch query. Passing in a {@link NativeQuery}
|
||||
* will result in an exception when {@link #build()} is called.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
public NativeQueryBuilder withQuery(org.springframework.data.elasticsearch.core.query.Query query) {
|
||||
this.springDataQuery = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public NativeQueryBuilder withKnnQuery(KnnQuery knnQuery) {
|
||||
this.knnQuery = knnQuery;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQuery build() {
|
||||
Assert.isTrue(query == null || springDataQuery == null, "Cannot have both a native query and a Spring Data query");
|
||||
return new NativeQuery(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.LatLonGeoLocation;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.IdsQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.WildcardQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.WrapperQuery;
|
||||
import co.elastic.clients.util.ObjectBuilder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class simplifying the creation of some more complex queries and type.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class Queries {
|
||||
|
||||
private Queries() {}
|
||||
|
||||
public static IdsQuery idsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
return IdsQuery.of(i -> i.values(ids));
|
||||
}
|
||||
|
||||
public static Query idsQueryAsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.ids(idsQuery(ids));
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static MatchQuery matchQuery(String fieldName, String query, @Nullable Operator operator,
|
||||
@Nullable Float boost) {
|
||||
|
||||
Assert.notNull(fieldName, "fieldName must not be null");
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
return MatchQuery.of(mb -> mb.field(fieldName).query(FieldValue.of(query)).operator(operator).boost(boost));
|
||||
}
|
||||
|
||||
public static Query matchQueryAsQuery(String fieldName, String query, @Nullable Operator operator,
|
||||
@Nullable Float boost) {
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.match(matchQuery(fieldName, query, operator, boost));
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static MatchAllQuery matchAllQuery() {
|
||||
|
||||
return MatchAllQuery.of(b -> b);
|
||||
}
|
||||
|
||||
public static Query matchAllQueryAsQuery() {
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.matchAll(matchAllQuery());
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static QueryStringQuery queryStringQuery(String fieldName, String query, @Nullable Float boost) {
|
||||
return queryStringQuery(fieldName, query, null, null, boost);
|
||||
}
|
||||
|
||||
public static QueryStringQuery queryStringQuery(String fieldName, String query, Operator defaultOperator,
|
||||
@Nullable Float boost) {
|
||||
return queryStringQuery(fieldName, query, null, defaultOperator, boost);
|
||||
}
|
||||
|
||||
public static QueryStringQuery queryStringQuery(String fieldName, String query, @Nullable Boolean analyzeWildcard,
|
||||
@Nullable Float boost) {
|
||||
return queryStringQuery(fieldName, query, analyzeWildcard, null, boost);
|
||||
}
|
||||
|
||||
public static QueryStringQuery queryStringQuery(String fieldName, String query, @Nullable Boolean analyzeWildcard,
|
||||
@Nullable Operator defaultOperator, @Nullable Float boost) {
|
||||
|
||||
Assert.notNull(fieldName, "fieldName must not be null");
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
return QueryStringQuery.of(qs -> qs.fields(fieldName).query(query).analyzeWildcard(analyzeWildcard)
|
||||
.defaultOperator(defaultOperator).boost(boost));
|
||||
}
|
||||
|
||||
public static TermQuery termQuery(String fieldName, String value) {
|
||||
|
||||
Assert.notNull(fieldName, "fieldName must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return TermQuery.of(t -> t.field(fieldName).value(FieldValue.of(value)));
|
||||
}
|
||||
|
||||
public static Query termQueryAsQuery(String fieldName, String value) {
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = q -> q.term(termQuery(fieldName, value));
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static WildcardQuery wildcardQuery(String field, String value) {
|
||||
|
||||
Assert.notNull(field, "field must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return WildcardQuery.of(w -> w.field(field).wildcard(value));
|
||||
}
|
||||
|
||||
public static Query wildcardQueryAsQuery(String field, String value) {
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = q -> q.wildcard(wildcardQuery(field, value));
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static Query wrapperQueryAsQuery(String query) {
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = q -> q.wrapper(wrapperQuery(query));
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static WrapperQuery wrapperQuery(String query) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
String encodedValue = Base64.getEncoder().encodeToString(query.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return WrapperQuery.of(wq -> wq.query(encodedValue));
|
||||
}
|
||||
|
||||
public static LatLonGeoLocation latLon(GeoPoint geoPoint) {
|
||||
|
||||
Assert.notNull(geoPoint, "geoPoint must not be null");
|
||||
|
||||
return latLon(geoPoint.getLat(), geoPoint.getLon());
|
||||
}
|
||||
|
||||
public static LatLonGeoLocation latLon(double lat, double lon) {
|
||||
return LatLonGeoLocation.of(_0 -> _0.lat(lat).lon(lon));
|
||||
}
|
||||
|
||||
public static org.springframework.data.elasticsearch.core.query.Query getTermsAggsQuery(String aggsName,
|
||||
String aggsField) {
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(Queries.matchAllQueryAsQuery()) //
|
||||
.withAggregation(aggsName, Aggregation.of(a -> a //
|
||||
.terms(ta -> ta.field(aggsField)))) //
|
||||
.withMaxResults(0) //
|
||||
.build();
|
||||
}
|
||||
|
||||
public static org.springframework.data.elasticsearch.core.query.Query queryWithIds(String... ids) {
|
||||
return NativeQuery.builder().withIds(ids).build();
|
||||
}
|
||||
|
||||
public static BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
|
||||
return NativeQuery.builder().withQuery(matchAllQueryAsQuery());
|
||||
}
|
||||
|
||||
public static BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
|
||||
return NativeQuery.builder().withQuery(termQueryAsQuery(field, value));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
@@ -42,7 +42,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
* @deprecated since 5.1, use {@link Queries} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public final class QueryBuilders {
|
||||
|
||||
private QueryBuilders() {}
|
||||
@@ -62,6 +64,7 @@ public final class QueryBuilders {
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
|
||||
public static MatchQuery matchQuery(String fieldName, String query, @Nullable Operator operator,
|
||||
@Nullable Float boost) {
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+87
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -237,6 +237,29 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
return search(fn.apply(new SearchRequest.Builder()).build(), tDocumentClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public <T> Mono<SearchTemplateResponse<T>> searchTemplate(SearchTemplateRequest request, Class<T> tDocumentClass) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
Assert.notNull(tDocumentClass, "tDocumentClass must not be null");
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request,
|
||||
SearchTemplateRequest.createSearchTemplateEndpoint(this.getDeserializer(tDocumentClass)), transportOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public <T> Mono<SearchTemplateResponse<T>> searchTemplate(
|
||||
Function<SearchTemplateRequest.Builder, ObjectBuilder<SearchTemplateRequest>> fn, Class<T> tDocumentClass) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return searchTemplate(fn.apply(new SearchTemplateRequest.Builder()).build(), tDocumentClass);
|
||||
}
|
||||
|
||||
public <T> Mono<ScrollResponse<T>> scroll(ScrollRequest request, Class<T> tDocumentClass) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
@@ -320,4 +343,67 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region script api
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<PutScriptResponse> putScript(PutScriptRequest request) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, PutScriptRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<PutScriptResponse> putScript(Function<PutScriptRequest.Builder, ObjectBuilder<PutScriptRequest>> fn) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return putScript(fn.apply(new PutScriptRequest.Builder()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<GetScriptResponse> getScript(GetScriptRequest request) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, GetScriptRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<GetScriptResponse> getScript(Function<GetScriptRequest.Builder, ObjectBuilder<GetScriptRequest>> fn) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return getScript(fn.apply(new GetScriptRequest.Builder()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<DeleteScriptResponse> deleteScript(DeleteScriptRequest request) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, DeleteScriptRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
public Mono<DeleteScriptResponse> deleteScript(
|
||||
Function<DeleteScriptRequest.Builder, ObjectBuilder<DeleteScriptRequest>> fn) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return deleteScript(fn.apply(new DeleteScriptRequest.Builder()).build());
|
||||
}
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
|
||||
+211
-94
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -19,7 +19,6 @@ import static co.elastic.clients.util.ApiTypeHelper.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Result;
|
||||
import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
|
||||
import co.elastic.clients.elasticsearch.core.get.GetResult;
|
||||
@@ -35,14 +34,19 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.BulkFailureException;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
|
||||
import org.springframework.data.elasticsearch.client.erhlc.ReactiveClusterOperations;
|
||||
import org.springframework.data.elasticsearch.client.util.ScrollState;
|
||||
import org.springframework.data.elasticsearch.core.AbstractReactiveElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.AggregationContainer;
|
||||
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
|
||||
@@ -54,16 +58,20 @@ import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
|
||||
import org.springframework.data.elasticsearch.core.script.Script;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations} using the new
|
||||
@@ -74,6 +82,8 @@ import org.springframework.util.CollectionUtils;
|
||||
*/
|
||||
public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearchTemplate {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveElasticsearchTemplate.class);
|
||||
|
||||
private final ReactiveElasticsearchClient client;
|
||||
private final RequestConverter requestConverter;
|
||||
private final ResponseConverter responseConverter;
|
||||
@@ -101,7 +111,9 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return Mono.just(entity) //
|
||||
.zipWith(//
|
||||
Mono.from(execute((ClientCallback<Publisher<IndexResponse>>) client -> client.index(indexRequest))) //
|
||||
.map(indexResponse -> new IndexResponseMetaData(indexResponse.id(), //
|
||||
.map(indexResponse -> new IndexResponseMetaData(
|
||||
indexResponse.id(), //
|
||||
indexResponse.index(), //
|
||||
indexResponse.seqNo(), //
|
||||
indexResponse.primaryTerm(), //
|
||||
indexResponse.version() //
|
||||
@@ -115,7 +127,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
return entitiesPublisher //
|
||||
.flatMapMany(entities -> Flux.fromIterable(entities) //
|
||||
.concatMap(entity -> maybeCallBeforeConvert(entity, index)) //
|
||||
.concatMap(entity -> maybeCallbackBeforeConvert(entity, index)) //
|
||||
).collectList() //
|
||||
.map(Entities::new) //
|
||||
.flatMapMany(entities -> {
|
||||
@@ -129,13 +141,43 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
.flatMap(indexAndResponse -> {
|
||||
T savedEntity = entities.entityAt(indexAndResponse.getT1());
|
||||
BulkResponseItem response = indexAndResponse.getT2();
|
||||
updateIndexedObject(savedEntity, IndexedObjectInformation.of(response.id(), response.seqNo(),
|
||||
response.primaryTerm(), response.version()));
|
||||
return maybeCallAfterSave(savedEntity, index);
|
||||
updateIndexedObject(savedEntity, new IndexedObjectInformation( //
|
||||
response.id(), //
|
||||
response.index(), //
|
||||
response.seqNo(), //
|
||||
response.primaryTerm(), //
|
||||
response.version()));
|
||||
return maybeCallbackAfterSave(savedEntity, index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Boolean> doExists(String id, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, true);
|
||||
|
||||
return Mono.from(execute(
|
||||
((ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class))))
|
||||
.map(GetResult::found) //
|
||||
.onErrorReturn(NoSuchIndexException.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, entityType, index,
|
||||
getRefreshPolicy());
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
|
||||
.map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> get(String id, Class<T> entityType, IndexCoordinates index) {
|
||||
|
||||
@@ -183,6 +225,29 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
: Mono.just(response.task()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<UpdateResponse> update(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(updateQuery, "UpdateQuery must not be null");
|
||||
Assert.notNull(index, "Index must not be null");
|
||||
|
||||
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
|
||||
routingResolver.getRouting());
|
||||
|
||||
return Mono.from(execute(
|
||||
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.UpdateResponse<Document>>>) client -> client
|
||||
.update(request, Document.class)))
|
||||
.flatMap(response -> {
|
||||
UpdateResponse.Result result = result(response.result());
|
||||
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
|
||||
@@ -279,83 +344,114 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return new ReactiveElasticsearchTemplate(client, converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Boolean> doExists(String id, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, true);
|
||||
|
||||
return Mono.from(execute(
|
||||
((ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class))))
|
||||
.map(GetResult::found) //
|
||||
.onErrorReturn(NoSuchIndexException.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, entityType, index,
|
||||
getRefreshPolicy());
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
|
||||
.map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
// region search operations
|
||||
|
||||
@Override
|
||||
protected Flux<SearchDocument> doFind(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
return Flux.defer(() -> {
|
||||
boolean useScroll = !(query.getPageable().isPaged() || query.isLimiting());
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, useScroll);
|
||||
|
||||
if (useScroll) {
|
||||
return doScroll(searchRequest);
|
||||
} else {
|
||||
return doFind(searchRequest);
|
||||
}
|
||||
});
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
if (query instanceof SearchTemplateQuery searchTemplateQuery) {
|
||||
return Flux.defer(() -> doSearch(searchTemplateQuery, clazz, index));
|
||||
} else {
|
||||
return Flux.defer(() -> {
|
||||
boolean queryIsUnbounded = !(query.getPageable().isPaged() || query.isLimiting());
|
||||
return queryIsUnbounded ? doFindUnbounded(query, clazz, index) : doFindBounded(query, clazz, index);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Flux<SearchDocument> doScroll(SearchRequest searchRequest) {
|
||||
private Flux<SearchDocument> doFindUnbounded(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
Time scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll() : Time.of(t -> t.time("1m"));
|
||||
if (query instanceof BaseQuery baseQuery) {
|
||||
var pitKeepAlive = Duration.ofMinutes(5);
|
||||
// setup functions for Flux.usingWhen()
|
||||
Mono<PitSearchAfter> resourceSupplier = openPointInTime(index, pitKeepAlive, true)
|
||||
.map(pit -> new PitSearchAfter(baseQuery, pit));
|
||||
|
||||
Flux<ResponseBody<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
|
||||
state -> Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client1 -> client1
|
||||
.search(searchRequest, EntityAsMap.class))) //
|
||||
.expand(entityAsMapSearchResponse -> {
|
||||
Function<PitSearchAfter, Publisher<?>> asyncComplete = this::cleanupPit;
|
||||
|
||||
state.updateScrollId(entityAsMapSearchResponse.scrollId());
|
||||
BiFunction<PitSearchAfter, Throwable, Publisher<?>> asyncError = (psa, ex) -> {
|
||||
if (LOGGER.isErrorEnabled()) {
|
||||
LOGGER.error(String.format("Error during pit/search_after"), ex);
|
||||
}
|
||||
return cleanupPit(psa);
|
||||
};
|
||||
|
||||
if (entityAsMapSearchResponse.hits() == null
|
||||
|| CollectionUtils.isEmpty(entityAsMapSearchResponse.hits().hits())) {
|
||||
Function<PitSearchAfter, Publisher<?>> asyncCancel = psa -> {
|
||||
if (LOGGER.isWarnEnabled()) {
|
||||
LOGGER.warn(String.format("pit/search_after was cancelled"));
|
||||
}
|
||||
return cleanupPit(psa);
|
||||
};
|
||||
|
||||
Function<PitSearchAfter, Publisher<? extends ResponseBody<EntityAsMap>>> resourceClosure = psa -> {
|
||||
|
||||
baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive));
|
||||
baseQuery.addSort(Sort.by("_shard_doc"));
|
||||
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, clazz, index, false, true);
|
||||
|
||||
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
|
||||
.search(firstSearchRequest, EntityAsMap.class))).expand(entityAsMapSearchResponse -> {
|
||||
|
||||
var hits = entityAsMapSearchResponse.hits().hits();
|
||||
if (CollectionUtils.isEmpty(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);
|
||||
List<Object> sortOptions = hits.get(hits.size() - 1).sort().stream().map(TypeUtils::toObject)
|
||||
.collect(Collectors.toList());
|
||||
baseQuery.setSearchAfter(sortOptions);
|
||||
SearchRequest followSearchRequest = requestConverter.searchRequest(baseQuery, clazz, index, false, true);
|
||||
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
|
||||
.search(followSearchRequest, EntityAsMap.class)));
|
||||
});
|
||||
|
||||
return searchResponses.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits())
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
};
|
||||
|
||||
Flux<ResponseBody<EntityAsMap>> searchResponses = Flux.usingWhen(resourceSupplier, resourceClosure, asyncComplete,
|
||||
asyncError, asyncCancel);
|
||||
return searchResponses.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits())
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
} else {
|
||||
return Flux.error(new IllegalArgumentException("Query must be derived from BaseQuery"));
|
||||
}
|
||||
}
|
||||
|
||||
private Publisher<?> cleanupScroll(ScrollState state) {
|
||||
private Publisher<?> cleanupPit(PitSearchAfter psa) {
|
||||
var baseQuery = psa.getBaseQuery();
|
||||
baseQuery.setPointInTime(null);
|
||||
baseQuery.setSearchAfter(null);
|
||||
baseQuery.setSort(psa.getSort());
|
||||
var pit = psa.getPit();
|
||||
return StringUtils.hasText(pit) ? closePointInTime(pit) : Mono.empty();
|
||||
}
|
||||
|
||||
return execute((ClientCallback<Publisher<ClearScrollResponse>>) client -> client
|
||||
.clearScroll(ClearScrollRequest.of(csr -> csr.scrollId(state.getScrollIds()))));
|
||||
static private class PitSearchAfter {
|
||||
private final BaseQuery baseQuery;
|
||||
@Nullable private final Sort sort;
|
||||
private final String pit;
|
||||
|
||||
PitSearchAfter(BaseQuery baseQuery, String pit) {
|
||||
this.baseQuery = baseQuery;
|
||||
this.sort = baseQuery.getSort();
|
||||
this.pit = pit;
|
||||
}
|
||||
|
||||
public BaseQuery getBaseQuery() {
|
||||
return baseQuery;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public String getPit() {
|
||||
return pit;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -364,7 +460,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, entityType, index, true, false);
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, entityType, index, true);
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
@@ -372,7 +468,9 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
.map(searchResponse -> searchResponse.hits().total() != null ? searchResponse.hits().total().value() : 0L);
|
||||
}
|
||||
|
||||
private Flux<SearchDocument> doFind(SearchRequest searchRequest) {
|
||||
private Flux<SearchDocument> doFindBounded(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
@@ -381,13 +479,24 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
}
|
||||
|
||||
private Flux<SearchDocument> doSearch(SearchTemplateQuery query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
var request = requestConverter.searchTemplate(query, index);
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<SearchTemplateResponse<EntityAsMap>>>) client -> client
|
||||
.searchTemplate(request, EntityAsMap.class))) //
|
||||
.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits()) //
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false);
|
||||
|
||||
// noinspection unchecked
|
||||
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>((Class<T>) clazz, index);
|
||||
@@ -435,6 +544,37 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
// endregion
|
||||
|
||||
// region script operations
|
||||
@Override
|
||||
public Mono<Boolean> putScript(Script script) {
|
||||
|
||||
Assert.notNull(script, "script must not be null");
|
||||
|
||||
var request = requestConverter.scriptPut(script);
|
||||
return Mono.from(execute((ClientCallback<Publisher<PutScriptResponse>>) client -> client.putScript(request)))
|
||||
.map(PutScriptResponse::acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Script> getScript(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
var request = requestConverter.scriptGet(name);
|
||||
return Mono.from(execute((ClientCallback<Publisher<GetScriptResponse>>) client -> client.getScript(request)))
|
||||
.mapNotNull(responseConverter::scriptResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> deleteScript(String name) {
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
var request = requestConverter.scriptDelete(name);
|
||||
return Mono.from(execute((ClientCallback<Publisher<DeleteScriptResponse>>) client -> client.deleteScript(request)))
|
||||
.map(DeleteScriptResponse::acknowledged);
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
public Mono<String> getVendor() {
|
||||
return Mono.just("Elasticsearch");
|
||||
@@ -454,29 +594,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
})).map(infoResponse -> infoResponse.version().number());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<UpdateResponse> update(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(updateQuery, "UpdateQuery must not be null");
|
||||
Assert.notNull(index, "Index must not be null");
|
||||
|
||||
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
|
||||
routingResolver.getRouting());
|
||||
|
||||
return Mono.from(execute(
|
||||
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.UpdateResponse<Document>>>) client -> client
|
||||
.update(request, Document.class)))
|
||||
.flatMap(response -> {
|
||||
UpdateResponse.Result result = result(response.result());
|
||||
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public <T> Publisher<T> execute(ReactiveElasticsearchOperations.ClientCallback<Publisher<T>> callback) {
|
||||
@@ -510,12 +627,12 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
@Override
|
||||
public Query matchAllQuery() {
|
||||
return NativeQuery.builder().withQuery(QueryBuilders.matchAllQueryAsQuery()).build();
|
||||
return NativeQuery.builder().withQuery(Queries.matchAllQueryAsQuery()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query idsQuery(List<String> ids) {
|
||||
return NativeQuery.builder().withQuery(QueryBuilders.idsQueryAsQuery(ids)).build();
|
||||
return NativeQuery.builder().withQuery(Queries.idsQueryAsQuery(ids)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -154,7 +154,8 @@ public class ReactiveIndicesTemplate
|
||||
|
||||
@Override
|
||||
public Mono<Void> refresh() {
|
||||
return Mono.from(execute(ReactiveElasticsearchIndicesClient::refresh)).then();
|
||||
RefreshRequest refreshRequest = requestConverter.indicesRefreshRequest(getIndexCoordinates());
|
||||
return Mono.from(execute(client -> client.refresh(refreshRequest))).then();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+113
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -31,6 +31,7 @@ import co.elastic.clients.elasticsearch._types.mapping.Property;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.FieldAndFormat;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Like;
|
||||
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
@@ -85,9 +86,11 @@ import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.data.elasticsearch.core.reindex.Remote;
|
||||
import org.springframework.data.elasticsearch.core.script.Script;
|
||||
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -96,8 +99,10 @@ import org.springframework.util.StringUtils;
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @author cdalxndr
|
||||
* @author scoobyzhang
|
||||
* @since 4.4
|
||||
*/
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
class RequestConverter {
|
||||
|
||||
// the default max result window size of Elasticsearch
|
||||
@@ -435,12 +440,12 @@ class RequestConverter {
|
||||
|
||||
IndexRequest.Builder<Object> builder = new IndexRequest.Builder<>();
|
||||
|
||||
builder.index(indexCoordinates.getIndexName());
|
||||
builder.index(query.getIndexName() != null ? query.getIndexName() : indexCoordinates.getIndexName());
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
String id = !StringUtils.hasText(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
|
||||
builder //
|
||||
.id(id) //
|
||||
.document(elasticsearchConverter.mapObject(queryObject));
|
||||
@@ -487,12 +492,12 @@ class RequestConverter {
|
||||
|
||||
IndexOperation.Builder<Object> builder = new IndexOperation.Builder<>();
|
||||
|
||||
builder.index(indexCoordinates.getIndexName());
|
||||
builder.index(query.getIndexName() != null ? query.getIndexName() : indexCoordinates.getIndexName());
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
String id = StringUtils.hasText(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
|
||||
builder //
|
||||
.id(id) //
|
||||
.document(elasticsearchConverter.mapObject(queryObject));
|
||||
@@ -528,12 +533,12 @@ class RequestConverter {
|
||||
|
||||
CreateOperation.Builder<Object> builder = new CreateOperation.Builder<>();
|
||||
|
||||
builder.index(indexCoordinates.getIndexName());
|
||||
builder.index(query.getIndexName() != null ? query.getIndexName() : indexCoordinates.getIndexName());
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
String id = StringUtils.hasText(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
|
||||
builder //
|
||||
.id(id) //
|
||||
.document(elasticsearchConverter.mapObject(queryObject));
|
||||
@@ -994,18 +999,22 @@ class RequestConverter {
|
||||
// region search
|
||||
|
||||
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
boolean forCount, long scrollTimeInMillis) {
|
||||
boolean forCount) {
|
||||
return searchRequest(query, clazz, indexCoordinates, forCount, false, null);
|
||||
}
|
||||
|
||||
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
boolean forCount, long scrollTimeInMillis) {
|
||||
return searchRequest(query, clazz, indexCoordinates, forCount, true, scrollTimeInMillis);
|
||||
}
|
||||
|
||||
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
boolean forCount, boolean useScroll) {
|
||||
return searchRequest(query, clazz, indexCoordinates, forCount, useScroll, null);
|
||||
boolean forCount, boolean forBatchedSearch) {
|
||||
return searchRequest(query, clazz, indexCoordinates, forCount, forBatchedSearch, null);
|
||||
}
|
||||
|
||||
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
boolean forCount, boolean useScroll, @Nullable Long scrollTimeInMillis) {
|
||||
boolean forCount, boolean forBatchedSearch, @Nullable Long scrollTimeInMillis) {
|
||||
|
||||
String[] indexNames = indexCoordinates.getIndexNames();
|
||||
Assert.notNull(query, "query must not be null");
|
||||
@@ -1013,7 +1022,7 @@ class RequestConverter {
|
||||
|
||||
elasticsearchConverter.updateQuery(query, clazz);
|
||||
SearchRequest.Builder builder = new SearchRequest.Builder();
|
||||
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, useScroll);
|
||||
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, forBatchedSearch);
|
||||
|
||||
if (scrollTimeInMillis != null) {
|
||||
builder.scroll(t -> t.time(scrollTimeInMillis + "ms"));
|
||||
@@ -1148,7 +1157,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
private <T> void prepareSearchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
SearchRequest.Builder builder, boolean forCount, boolean useScroll) {
|
||||
SearchRequest.Builder builder, boolean forCount, boolean forBatchedSearch) {
|
||||
|
||||
String[] indexNames = indexCoordinates.getIndexNames();
|
||||
|
||||
@@ -1158,13 +1167,26 @@ class RequestConverter {
|
||||
|
||||
builder //
|
||||
.version(true) //
|
||||
.trackScores(query.getTrackScores());
|
||||
.trackScores(query.getTrackScores()) //
|
||||
.allowNoIndices(query.getAllowNoIndices()) //
|
||||
.source(getSourceConfig(query)) //
|
||||
.searchType(searchType(query.getSearchType())) //
|
||||
.timeout(timeStringMs(query.getTimeout())) //
|
||||
.requestCache(query.getRequestCache()) //
|
||||
;
|
||||
|
||||
var pointInTime = query.getPointInTime();
|
||||
if (pointInTime != null) {
|
||||
builder.pit(pb -> pb.id(pointInTime.id()).keepAlive(time(pointInTime.keepAlive())));
|
||||
} else {
|
||||
builder.index(Arrays.asList(indexNames));
|
||||
builder //
|
||||
.index(Arrays.asList(indexNames)) //
|
||||
;
|
||||
|
||||
var expandWildcards = query.getExpandWildcards();
|
||||
if (expandWildcards != null && !expandWildcards.isEmpty()) {
|
||||
builder.expandWildcards(expandWildcards(expandWildcards));
|
||||
}
|
||||
|
||||
if (query.getRoute() != null) {
|
||||
builder.routing(query.getRoute());
|
||||
@@ -1187,8 +1209,6 @@ class RequestConverter {
|
||||
builder.from(0).size(INDEX_MAX_RESULT_WINDOW);
|
||||
}
|
||||
|
||||
builder.source(getSourceConfig(query));
|
||||
|
||||
if (!isEmpty(query.getFields())) {
|
||||
builder.fields(fb -> {
|
||||
query.getFields().forEach(fb::field);
|
||||
@@ -1212,8 +1232,6 @@ class RequestConverter {
|
||||
builder.minScore((double) query.getMinScore());
|
||||
}
|
||||
|
||||
builder.searchType(searchType(query.getSearchType()));
|
||||
|
||||
if (query.getSort() != null) {
|
||||
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
||||
|
||||
@@ -1236,8 +1254,6 @@ class RequestConverter {
|
||||
builder.trackTotalHits(th -> th.count(query.getTrackTotalHitsUpTo()));
|
||||
}
|
||||
|
||||
builder.timeout(timeStringMs(query.getTimeout()));
|
||||
|
||||
if (query.getExplain()) {
|
||||
builder.explain(true);
|
||||
}
|
||||
@@ -1249,8 +1265,6 @@ class RequestConverter {
|
||||
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
|
||||
|
||||
builder.requestCache(query.getRequestCache());
|
||||
|
||||
if (!query.getRuntimeFields().isEmpty()) {
|
||||
|
||||
Map<String, RuntimeField> runtimeMappings = new HashMap<>();
|
||||
@@ -1271,13 +1285,11 @@ class RequestConverter {
|
||||
builder.size(0) //
|
||||
.trackTotalHits(th -> th.count(Integer.MAX_VALUE)) //
|
||||
.source(SourceConfig.of(sc -> sc.fetch(false)));
|
||||
} else if (useScroll) {
|
||||
} else if (forBatchedSearch) {
|
||||
// request_cache is not allowed on scroll requests.
|
||||
builder.requestCache(null);
|
||||
Duration scrollTimeout = query.getScrollTime() != null ? query.getScrollTime() : Duration.ofMinutes(1);
|
||||
builder.scroll(time(scrollTimeout));
|
||||
// limit the number of documents in a batch
|
||||
builder.size(500);
|
||||
builder.size(query.getReactiveBatchSize());
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
@@ -1287,6 +1299,12 @@ class RequestConverter {
|
||||
// noinspection unchecked
|
||||
builder.indicesBoost(boosts);
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getDocValueFields())) {
|
||||
builder.docvalueFields(query.getDocValueFields().stream() //
|
||||
.map(docValueField -> FieldAndFormat.of(b -> b.field(docValueField.field()).format(docValueField.format())))
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
private Rescore getRescore(RescorerQuery rescorerQuery) {
|
||||
@@ -1353,7 +1371,7 @@ class RequestConverter {
|
||||
return SortOptions.of(so -> so //
|
||||
.geoDistance(gd -> gd //
|
||||
.field(fieldName) //
|
||||
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint())))//
|
||||
.location(loc -> loc.latlon(Queries.latLon(geoDistanceOrder.getGeoPoint())))//
|
||||
.distanceType(TypeUtils.geoDistanceType(geoDistanceOrder.getDistanceType()))
|
||||
.mode(TypeUtils.sortMode(finalMode)) //
|
||||
.unit(TypeUtils.distanceUnit(geoDistanceOrder.getUnit())) //
|
||||
@@ -1395,7 +1413,8 @@ class RequestConverter {
|
||||
builder //
|
||||
.suggest(query.getSuggester()) //
|
||||
.collapse(query.getFieldCollapse()) //
|
||||
.sort(query.getSortOptions());
|
||||
.sort(query.getSortOptions()) //
|
||||
.knn(query.getKnnQuery());
|
||||
|
||||
if (!isEmpty(query.getAggregations())) {
|
||||
builder.aggregations(query.getAggregations());
|
||||
@@ -1415,7 +1434,8 @@ class RequestConverter {
|
||||
builder //
|
||||
.suggest(query.getSuggester()) //
|
||||
.collapse(query.getFieldCollapse()) //
|
||||
.sort(query.getSortOptions());
|
||||
.sort(query.getSortOptions()) //
|
||||
.knn(query.getKnnQuery());
|
||||
|
||||
if (!isEmpty(query.getAggregations())) {
|
||||
builder.aggregations(query.getAggregations());
|
||||
@@ -1441,11 +1461,13 @@ class RequestConverter {
|
||||
if (query instanceof CriteriaQuery) {
|
||||
esQuery = CriteriaQueryProcessor.createQuery(((CriteriaQuery) query).getCriteria());
|
||||
} else if (query instanceof StringQuery) {
|
||||
esQuery = QueryBuilders.wrapperQueryAsQuery(((StringQuery) query).getSource());
|
||||
esQuery = Queries.wrapperQueryAsQuery(((StringQuery) query).getSource());
|
||||
} else if (query instanceof NativeQuery nativeQuery) {
|
||||
|
||||
if (nativeQuery.getQuery() != null) {
|
||||
esQuery = nativeQuery.getQuery();
|
||||
} else if (nativeQuery.getSpringDataQuery() != null) {
|
||||
esQuery = getQuery(nativeQuery.getSpringDataQuery(), clazz);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
|
||||
@@ -1537,8 +1559,68 @@ class RequestConverter {
|
||||
return ClosePointInTimeRequest.of(cpit -> cpit.id(pit));
|
||||
}
|
||||
|
||||
public SearchTemplateRequest searchTemplate(SearchTemplateQuery query, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
return SearchTemplateRequest.of(builder -> {
|
||||
builder //
|
||||
.allowNoIndices(query.getAllowNoIndices()) //
|
||||
.explain(query.getExplain()) //
|
||||
.id(query.getId()) //
|
||||
.index(Arrays.asList(index.getIndexNames())) //
|
||||
.preference(query.getPreference()) //
|
||||
.routing(query.getRoute()) //
|
||||
.searchType(searchType(query.getSearchType())).source(query.getSource()) //
|
||||
;
|
||||
|
||||
var expandWildcards = query.getExpandWildcards();
|
||||
if (!expandWildcards.isEmpty()) {
|
||||
builder.expandWildcards(expandWildcards(expandWildcards));
|
||||
}
|
||||
|
||||
if (query.hasScrollTime()) {
|
||||
builder.scroll(time(query.getScrollTime()));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(query.getParams())) {
|
||||
Function<Map.Entry<String, Object>, String> keyMapper = Map.Entry::getKey;
|
||||
Function<Map.Entry<String, Object>, JsonData> valueMapper = entry -> JsonData.of(entry.getValue(), jsonpMapper);
|
||||
Map<String, JsonData> params = query.getParams().entrySet().stream()
|
||||
.collect(Collectors.toMap(keyMapper, valueMapper));
|
||||
builder.params(params);
|
||||
}
|
||||
|
||||
return builder;
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
public PutScriptRequest scriptPut(Script script) {
|
||||
|
||||
Assert.notNull(script, "script must not be null");
|
||||
|
||||
return PutScriptRequest.of(b -> b //
|
||||
.id(script.id()) //
|
||||
.script(sb -> sb //
|
||||
.lang(script.language()) //
|
||||
.source(script.source())));
|
||||
}
|
||||
|
||||
public GetScriptRequest scriptGet(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
return GetScriptRequest.of(b -> b.id(name));
|
||||
}
|
||||
|
||||
public DeleteScriptRequest scriptDelete(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
return DeleteScriptRequest.of(b -> b.id(name));
|
||||
}
|
||||
// region helper functions
|
||||
|
||||
public <T> T fromJson(String json, JsonpDeserializer<T> deserializer) {
|
||||
|
||||
+36
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -23,10 +23,20 @@ import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
|
||||
import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
|
||||
import co.elastic.clients.elasticsearch.core.GetScriptResponse;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateByQueryResponse;
|
||||
import co.elastic.clients.elasticsearch.core.mget.MultiGetError;
|
||||
import co.elastic.clients.elasticsearch.core.mget.MultiGetResponseItem;
|
||||
import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.elasticsearch.indices.Alias;
|
||||
import co.elastic.clients.elasticsearch.indices.AliasDefinition;
|
||||
import co.elastic.clients.elasticsearch.indices.GetAliasResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.GetIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.GetIndicesSettingsResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.GetMappingResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.GetTemplateResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.IndexSettings;
|
||||
import co.elastic.clients.elasticsearch.indices.IndexState;
|
||||
import co.elastic.clients.elasticsearch.indices.TemplateMapping;
|
||||
import co.elastic.clients.elasticsearch.indices.get_mapping.IndexMappingRecord;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
|
||||
@@ -51,7 +61,9 @@ import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
|
||||
import org.springframework.data.elasticsearch.core.script.Script;
|
||||
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -190,18 +202,19 @@ class ResponseConverter {
|
||||
}
|
||||
|
||||
private AliasData indicesGetAliasData(String aliasName, Alias alias) {
|
||||
|
||||
Query filter = alias.filter();
|
||||
String filterJson = filter != null ? toJson(filter, jsonpMapper) : null;
|
||||
Document filterDocument = filterJson != null ? Document.parse(filterJson) : null;
|
||||
return AliasData.of(aliasName, filterDocument, alias.indexRouting(), alias.searchRouting(), alias.isWriteIndex(),
|
||||
var filterQuery = filterJson != null ? StringQuery.builder(filterJson).build() : null;
|
||||
return AliasData.of(aliasName, filterQuery, alias.indexRouting(), alias.searchRouting(), alias.isWriteIndex(),
|
||||
alias.isHidden());
|
||||
}
|
||||
|
||||
private AliasData indicesGetAliasData(String aliasName, AliasDefinition alias) {
|
||||
Query filter = alias.filter();
|
||||
String filterJson = filter != null ? toJson(filter, jsonpMapper) : null;
|
||||
Document filterDocument = filterJson != null ? Document.parse(filterJson) : null;
|
||||
return AliasData.of(aliasName, filterDocument, alias.indexRouting(), alias.searchRouting(), alias.isWriteIndex(),
|
||||
var filterQuery = filterJson != null ? StringQuery.builder(filterJson).build() : null;
|
||||
return AliasData.of(aliasName, filterQuery, alias.indexRouting(), alias.searchRouting(), alias.isWriteIndex(),
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -280,7 +293,7 @@ class ResponseConverter {
|
||||
.withThrottledMillis(reindexResponse.throttledMillis()) //
|
||||
.withRequestsPerSecond(reindexResponse.requestsPerSecond()) //
|
||||
.withThrottledUntilMillis(reindexResponse.throttledUntilMillis()) //
|
||||
.withFailures(failures) //
|
||||
.withFailures(failures) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -410,6 +423,22 @@ class ResponseConverter {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region script API
|
||||
@Nullable
|
||||
public Script scriptResponse(GetScriptResponse response) {
|
||||
|
||||
Assert.notNull(response, "response must not be null");
|
||||
|
||||
return response.found() //
|
||||
? Script.builder() //
|
||||
.withId(response.id()) //
|
||||
.withLanguage(response.script().lang()) //
|
||||
.withSource(response.script().source()).build() //
|
||||
: null;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region helper functions
|
||||
|
||||
private long timeToLong(Time time) {
|
||||
|
||||
+28
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -17,6 +17,7 @@ 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.SearchTemplateResponse;
|
||||
import co.elastic.clients.elasticsearch.core.search.CompletionSuggest;
|
||||
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
@@ -70,6 +71,7 @@ class SearchDocumentResponseBuilder {
|
||||
|
||||
Assert.notNull(responseBody, "responseBody must not be null");
|
||||
Assert.notNull(entityCreator, "entityCreator must not be null");
|
||||
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||
|
||||
HitsMetadata<EntityAsMap> hitsMetadata = responseBody.hits();
|
||||
String scrollId = responseBody.scrollId();
|
||||
@@ -80,6 +82,31 @@ class SearchDocumentResponseBuilder {
|
||||
return from(hitsMetadata, scrollId, pointInTimeId, aggregations, suggest, entityCreator, jsonpMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a SearchDocumentResponse from the {@link SearchTemplateResponse}
|
||||
*
|
||||
* @param response the Elasticsearch response body
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param jsonpMapper to map JsonData objects
|
||||
* @return the SearchDocumentResponse
|
||||
* @since 5.1
|
||||
*/
|
||||
public static <T> SearchDocumentResponse from(SearchTemplateResponse<EntityAsMap> response,
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {
|
||||
|
||||
Assert.notNull(response, "response must not be null");
|
||||
Assert.notNull(entityCreator, "entityCreator must not be null");
|
||||
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||
|
||||
var hitsMetadata = response.hits();
|
||||
var scrollId = response.scrollId();
|
||||
var aggregations = response.aggregations();
|
||||
var suggest = response.suggest();
|
||||
var pointInTimeId = response.pitId();
|
||||
|
||||
return from(hitsMetadata, scrollId, pointInTimeId, aggregations, suggest, entityCreator, jsonpMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a {@link SearchDocumentResponseBuilder} from {@link HitsMetadata} with the given scrollId aggregations and
|
||||
* suggestES
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user