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

Compare commits

..

36 Commits

Author SHA1 Message Date
Greg L. Turnquist de437cd7da Release version 5.1 RC1 (2023.0.0).
See #2500
2023-04-14 11:54:01 -05:00
Greg L. Turnquist 5ac83f696e Prepare 5.1 RC1 (2023.0.0).
See #2500
2023-04-14 11:53:25 -05:00
Peter-Josef Meisch 6100c2491a Cleanup.
Original Pull Request #2526
Closes #2525
2023-04-12 22:19:19 +02:00
Peter-Josef Meisch d597baccd3 Upgrade to Elasticsearch 8.7.0.
Original Pull Request #2524
Closes #2523
2023-04-11 20:45:52 +02:00
Peter-Josef Meisch dfc1be286c Polishing 2023-04-10 11:22:45 +02:00
JKatzwinkel 1f7fa77c15 Fix inner hits metadata mapping.
Original Pull Request #2522
Closes #2521
2023-04-10 11:18:26 +02:00
Mark Paluch 699c0ef265 Upgrade to Maven Wrapper 3.9.1.
See #2518
2023-04-06 16:16:37 +02:00
Peter-Josef Meisch 9771b9c1d6 clarify PR submission 2023-04-05 08:07:27 +02:00
Peter-Josef Meisch 437279f637 remove erroneous linebreak 2023-04-04 17:45:28 +02:00
Peter-Josef Meisch ed1c416cd3 Javadoc
Original Pull Request #2516
Closes #2515
2023-04-03 20:20:13 +02:00
Peter-Josef Meisch a5fb7a3c76 Remove old methods from configuration.
Original Pull Request #2513
Closes #2512
2023-04-02 20:16:13 +02:00
Greg L. Turnquist 7f772703d3 Test against Java 20 on CI.
See #2507.
2023-03-30 11:40:56 -05:00
Greg L. Turnquist 43eb40e45e Upgrade to BlockHound 1.0.7.RELEASE.
Closes #2510.
2023-03-30 11:39:57 -05:00
Greg L. Turnquist 75bf578b31 Update CI properties.
See #2500
2023-03-28 13:58:23 -05:00
Peter-Josef Meisch 675b77982b Allow field names with dots.
Original Pull Request #2504
Closes #2502
2023-03-22 19:51:00 +01:00
Christoph Strobl d95af9fcfa After release cleanups.
See #2464
2023-03-20 15:05:35 +01:00
Christoph Strobl 96985fa14a Prepare next development iteration.
See #2464
2023-03-20 15:05:34 +01:00
Christoph Strobl ff1f25b0b2 Release version 5.1 M3 (2023.0.0).
See #2464
2023-03-20 15:01:48 +01:00
Christoph Strobl d3e95815d9 Prepare 5.1 M3 (2023.0.0).
See #2464
2023-03-20 15:01:20 +01:00
Peter-Josef Meisch 797dbb5a18 Improve save(Flux<T>) implementations.
Original Pull Request #2497
Closes #2496
Closes #2492
2023-03-19 15:01:24 +01:00
Mark Paluch a7d6b9df6d Adopt to newly introduced ParameterAccessor.getScrollPosition().
Closes #2494
2023-03-16 15:26:47 +01:00
Peter-Josef Meisch ade90328d3 ValueConverters can be derived from AbstractPropertyValueConverter.
Original Pull Request #2490
Closes #2489
2023-03-05 20:54:20 +01:00
Peter-Josef Meisch ec77b3a082 Add option to not write version to document source.
Original Pull Request #2487
Closes #2466
2023-03-05 09:53:41 +01:00
Peter-Josef Meisch 63cebd7038 Fix putMapping request creation .
Original Pull Request #2483
Closes #2487
2023-02-26 15:41:12 +01:00
Peter-Josef Meisch e7c9bf20f6 Add option to specify if empty property should not be sent to Elasticsearch.
Original Pull Request #2482
Closes #2290
2023-02-25 17:50:12 +01:00
Peter-Josef Meisch b4c3e25a60 Move scripted fields from native query up to base query.
Original Pull Request #2481
Closes #2477
2023-02-25 14:43:32 +01:00
Peter-Josef Meisch 43ab49b5fa refactor tests
Original Pull Request #2480
Closes #2479
2023-02-25 13:38:50 +01:00
Peter-Josef Meisch c9b8b1af19 Adapt custom routing.
Original Pull Request #2474
Closes #2087
2023-02-21 20:17:45 +01:00
Mark Paluch 9c80dc9ae8 Upgrade to Maven Wrapper 3.9.0.
See #2471
2023-02-20 11:58:19 +01:00
Peter-Josef Meisch 03ffb07827 Upgrade to Elasticsearch 8.6.2.
Original Pull Request #2470
Closes #2468
2023-02-19 12:57:18 +01:00
Christoph Strobl d29cf7788b After release cleanups.
See #2463
2023-02-17 14:25:49 +01:00
Christoph Strobl 71e46c8277 Prepare next development iteration.
See #2463
2023-02-17 14:25:47 +01:00
Christoph Strobl fc1e8d3cf6 Release version 5.1 M2 (2023.0.0).
See #2463
2023-02-17 14:22:31 +01:00
Christoph Strobl c84a8bbc12 Prepare 5.1 M2 (2023.0.0).
See #2463
2023-02-17 14:22:18 +01:00
Christoph Strobl 97a9176775 After release cleanups.
See #2367
2023-02-17 13:31:54 +01:00
Christoph Strobl 7eff8f5dd1 Prepare next development iteration.
See #2367
2023-02-17 13:31:53 +01:00
66 changed files with 1648 additions and 1091 deletions
+1 -2
View File
@@ -9,8 +9,7 @@ Make sure that:
-->
- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).
- [ ] **There is a ticket in the bug tracker for the project in our [issue tracker](https://github.
com/spring-projects/spring-data-elasticsearch/issues)**. Add the issue number to the _Closes #issue-number_ line below
- [ ] **There is a ticket in the bug tracker for the project in our [issue tracker](https://github.com/spring-projects/spring-data-elasticsearch/issues)**. Add the issue number to the _Closes #issue-number_ line below
- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Dont submit any formatting related changes.
- [ ] You submit test cases (unit or integration tests) that back your changes.
- [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).
+1
View File
@@ -24,4 +24,5 @@ target
/zap.env
/localdocker.env
.localdocker-env
+2 -2
View File
@@ -1,3 +1,3 @@
#Mon Jan 30 10:48:19 CET 2023
#Thu Apr 06 16:16:37 CEST 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.7/apache-maven-3.8.7-bin.zip
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip
+3
View File
@@ -1,6 +1,9 @@
= Spring Data contribution guidelines
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc[here].
**Please read these carefully!**
Do not submit a Pull Request before having created an issue and having discussed it. This prevents you from doing work that might be rejected.
== Running the test locally
Vendored
+43 -20
View File
@@ -1,7 +1,7 @@
def p = [:]
node {
checkout scm
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
checkout scm
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
}
pipeline {
@@ -18,7 +18,7 @@ pipeline {
}
stages {
stage("test: baseline (Java 17)") {
stage("test: baseline (main)") {
when {
beforeAgent(true)
anyOf {
@@ -32,17 +32,42 @@ pipeline {
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
stage("Test other configurations") {
when {
beforeAgent(true)
allOf {
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: baseline (next)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
@@ -68,17 +93,15 @@ pipeline {
steps {
script {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
+2
View File
@@ -1,8 +1,10 @@
# Java versions
java.main.tag=17.0.6_10-jdk-focal
java.next.tag=20-jdk-jammy
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
# Supported versions of MongoDB
docker.mongodb.4.4.version=4.4.18
+5 -38
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>5.1.0-M1</version>
<version>5.1.0-RC1</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.1.0-M1</version>
<version>3.1.0-RC1</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -18,17 +18,17 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<springdata.commons>3.1.0-M1</springdata.commons>
<springdata.commons>3.1.0-RC1</springdata.commons>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.9</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<elasticsearch-java>8.6.1</elasticsearch-java>
<elasticsearch-java>8.7.0</elasticsearch-java>
<log4j>2.18.0</log4j>
<netty>4.1.65.Final</netty>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<blockhound-junit>1.0.7.RELEASE</blockhound-junit>
<hoverfly>0.14.3</hoverfly>
<jsonassert>1.5.1</jsonassert>
<testcontainers>1.17.3</testcontainers>
@@ -43,7 +43,6 @@
-->
<mvn.unit-test.goal>test</mvn.unit-test.goal>
<mvn.integration-test-elasticsearch.goal>integration-test</mvn.integration-test-elasticsearch.goal>
<mvn.integration-test-opensearch.goal>none</mvn.integration-test-opensearch.goal>
</properties>
<developers>
@@ -279,24 +278,6 @@
<scope>test</scope>
</dependency>
<!--
we don't use lombok in Spring Data Elasticsearch anymore. But the dependency is set in the parent project, and so the
lombok compiler stuff is executed regardless of the fact that we don't need it.
On AdoptOpenJdk 16.0.0 this leads to an error, so the project does not build.
Therefore we replace lombok with a jar - that just contains an empty file - that lives in a local maven repository in
src/test/resources/local-maven-repo/
It was installed with
mvn deploy:deploy-file -DgroupId=org.projectlombok -DartifactId=lombok -Dversion=999999 -Durl=file:./src/test/resources/local-maven-repo/ -DrepositoryId=local-maven-repo -DupdateReleaseInfo=true -Dfile=path/to/empty.jar
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--suppress MavenPackageUpdate -->
<version>999999</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
@@ -419,20 +400,6 @@
</systemPropertyVariables>
</configuration>
</execution>
<!-- execution to run the integration tests against Opensearch -->
<execution>
<id>integration-test-opensearch</id>
<phase>${mvn.integration-test-opensearch.goal}</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<groups>integration-test</groups>
<systemPropertyVariables>
<sde.integration-test.environment>opensearch</sde.integration-test.environment>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
+1 -1
View File
@@ -37,7 +37,7 @@ built and tested.
[cols="^,^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
| 2022.1 | 5.1.x | 8.6.1 | 6.0.x | 3.0.x
| 2022.1 | 5.1.x | 8.7.0 | 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
@@ -4,7 +4,7 @@
[[new-features.5-1-0]]
== New in Spring Data Elasticsearch 5.1
* Upgrade to Elasticsearch 8.6.1
* Upgrade to Elasticsearch 8.7.0
[[new-features.5-0-0]]
== New in Spring Data Elasticsearch 5.0
@@ -85,6 +85,14 @@ public @interface Document {
*/
boolean storeIdInSource() default true;
/**
* Specifies if the version property should also be stored in the Elasticsearch document source. Default value is
* true.
*
* @since 5.1
*/
boolean storeVersionInSource() default true;
/**
* @since 4.3
*/
@@ -210,4 +210,12 @@ public @interface Field {
* @since 4.3
*/
boolean excludeFromSource() default false;
/**
* when this field is a {{@link String}}, a {{@link java.util.Collection}} or a {{@link java.util.Map}} that is empty
* this property controlls whether the empty value is sent to Elasticsearch.
*
* @since 5.1
*/
boolean storeEmptyValue() default true;
}
@@ -163,11 +163,6 @@ public interface ClientConfiguration {
*/
Optional<String> getProxy();
/**
* @return the function for configuring a WebClient.
*/
Function<WebClient, WebClient> getWebClientConfigurer();
/**
* @return the client configuration callbacks
* @since 4.3
@@ -59,7 +59,6 @@ class ClientConfigurationBuilder
private @Nullable String password;
private @Nullable String pathPrefix;
private @Nullable String proxy;
private final Function<WebClient, WebClient> webClientConfigurer = Function.identity();
private Supplier<HttpHeaders> headersSupplier = HttpHeaders::new;
@Deprecated private final HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
private final List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
@@ -230,7 +229,7 @@ class ClientConfigurationBuilder
}
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix,
hostnameVerifier, proxy, webClientConfigurer, httpClientConfigurer, clientConfigurers, headersSupplier);
hostnameVerifier, proxy, httpClientConfigurer, clientConfigurers, headersSupplier);
}
private static InetSocketAddress parse(String hostAndPort) {
@@ -19,7 +19,6 @@ import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.net.ssl.HostnameVerifier;
@@ -28,7 +27,6 @@ import javax.net.ssl.SSLContext;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Default {@link ClientConfiguration} implementation.
@@ -50,7 +48,6 @@ class DefaultClientConfiguration implements ClientConfiguration {
private final @Nullable String pathPrefix;
private final @Nullable HostnameVerifier hostnameVerifier;
private final @Nullable String proxy;
private final Function<WebClient, WebClient> webClientConfigurer;
private final HttpClientConfigCallback httpClientConfigurer;
private final Supplier<HttpHeaders> headersSupplier;
private final List<ClientConfigurationCallback<?>> clientConfigurers;
@@ -58,8 +55,8 @@ class DefaultClientConfiguration implements ClientConfiguration {
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix,
@Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
Function<WebClient, WebClient> webClientConfigurer, HttpClientConfigCallback httpClientConfigurer,
List<ClientConfigurationCallback<?>> clientConfigurers, Supplier<HttpHeaders> headersSupplier) {
HttpClientConfigCallback httpClientConfigurer, List<ClientConfigurationCallback<?>> clientConfigurers,
Supplier<HttpHeaders> headersSupplier) {
this.hosts = List.copyOf(hosts);
this.headers = headers;
@@ -70,7 +67,6 @@ class DefaultClientConfiguration implements ClientConfiguration {
this.pathPrefix = pathPrefix;
this.hostnameVerifier = hostnameVerifier;
this.proxy = proxy;
this.webClientConfigurer = webClientConfigurer;
this.httpClientConfigurer = httpClientConfigurer;
this.clientConfigurers = clientConfigurers;
this.headersSupplier = headersSupplier;
@@ -122,11 +118,6 @@ class DefaultClientConfiguration implements ClientConfiguration {
return Optional.ofNullable(proxy);
}
@Override
public Function<WebClient, WebClient> getWebClientConfigurer() {
return webClientConfigurer;
}
@Override
public <T> List<ClientConfigurationCallback<?>> getClientConfigurers() {
return clientConfigurers;
@@ -174,8 +174,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, clazz, index,
getRefreshPolicy());
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index, getRefreshPolicy());
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
@@ -309,7 +309,8 @@ 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);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
true);
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
@@ -331,7 +332,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
protected <T> SearchHits<T> doSearch(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false);
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
// noinspection DuplicatedCode
@@ -343,7 +345,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
}
protected <T> SearchHits<T> doSearch(SearchTemplateQuery query, Class<T> clazz, IndexCoordinates index) {
var searchTemplateRequest = requestConverter.searchTemplate(query, index);
var searchTemplateRequest = requestConverter.searchTemplate(query, routingResolver.getRouting(), index);
var searchTemplateResponse = execute(client -> client.searchTemplate(searchTemplateRequest, EntityAsMap.class));
// noinspection DuplicatedCode
@@ -374,7 +376,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
Assert.notNull(query.getPageable(), "pageable of query must not be null.");
SearchRequest request = requestConverter.searchRequest(query, clazz, index, false, scrollTimeInMillis);
SearchRequest request = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index, false,
scrollTimeInMillis);
SearchResponse<EntityAsMap> response = execute(client -> client.search(request, EntityAsMap.class));
return getSearchScrollHits(clazz, index, response);
@@ -492,7 +495,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<SearchHits<?>> doMultiSearch(List<MultiSearchQueryParameter> multiSearchQueryParameters) {
MsearchRequest request = requestConverter.searchMsearchRequest(multiSearchQueryParameters);
MsearchRequest request = requestConverter.searchMsearchRequest(multiSearchQueryParameters,
routingResolver.getRouting());
MsearchResponse<EntityAsMap> msearchResponse = execute(client -> client.msearch(request, EntityAsMap.class));
List<MultiSearchResponseItem<EntityAsMap>> responseItems = msearchResponse.responses();
@@ -50,7 +50,6 @@ public class NativeQuery extends BaseQuery {
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse;
private List<ScriptedField> scriptedFields = Collections.emptyList();
private List<SortOptions> sortOptions = Collections.emptyList();
private Map<String, JsonData> searchExtensions = Collections.emptyMap();
@@ -63,7 +62,6 @@ public class NativeQuery extends BaseQuery {
this.aggregations.putAll(builder.getAggregations());
this.suggester = builder.getSuggester();
this.fieldCollapse = builder.getFieldCollapse();
this.scriptedFields = builder.getScriptedFields();
this.sortOptions = builder.getSortOptions();
this.searchExtensions = builder.getSearchExtensions();
@@ -107,10 +105,6 @@ public class NativeQuery extends BaseQuery {
return fieldCollapse;
}
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
public List<SortOptions> getSortOptions() {
return sortOptions;
}
@@ -32,7 +32,6 @@ import java.util.Map;
import java.util.function.Function;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -48,7 +47,6 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
@Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse;
private final List<ScriptedField> scriptedFields = new ArrayList<>();
private List<SortOptions> sortOptions = new ArrayList<>();
private Map<String, JsonData> searchExtensions = new LinkedHashMap<>();
@@ -81,10 +79,6 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return fieldCollapse;
}
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
public List<SortOptions> getSortOptions() {
return sortOptions;
}
@@ -149,17 +143,10 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return this;
}
public NativeQueryBuilder withScriptedField(ScriptedField scriptedField) {
Assert.notNull(scriptedField, "scriptedField must not be null");
this.scriptedFields.add(scriptedField);
return this;
}
public NativeQueryBuilder withSort(List<SortOptions> values) {
Assert.notEmpty(values, "values must not be empty");
sortOptions.clear();
sortOptions.addAll(values);
@@ -111,8 +111,7 @@ 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(), //
@@ -171,8 +170,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(query, "query must not be null");
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, entityType, index,
getRefreshPolicy());
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index, getRefreshPolicy());
return Mono
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
.map(responseConverter::byQueryResponse);
@@ -391,7 +390,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive));
baseQuery.addSort(Sort.by("_shard_doc"));
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, clazz, index, false, true);
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(),
clazz, index, false, true);
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
.search(firstSearchRequest, EntityAsMap.class))).expand(entityAsMapSearchResponse -> {
@@ -404,7 +404,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
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);
SearchRequest followSearchRequest = requestConverter.searchRequest(baseQuery,
routingResolver.getRouting(), clazz, index, false, true);
return Mono.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client
.search(followSearchRequest, EntityAsMap.class)));
});
@@ -460,7 +461,8 @@ 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);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), entityType, index,
true);
return Mono
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
@@ -470,7 +472,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private Flux<SearchDocument> doFindBounded(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false, false);
return Mono
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
@@ -481,7 +484,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
private Flux<SearchDocument> doSearch(SearchTemplateQuery query, Class<?> clazz, IndexCoordinates index) {
var request = requestConverter.searchTemplate(query, index);
var request = requestConverter.searchTemplate(query, routingResolver.getRouting(), index);
return Mono
.from(execute((ClientCallback<Publisher<SearchTemplateResponse<EntityAsMap>>>) client -> client
@@ -496,7 +499,8 @@ 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, clazz, index, false);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false);
// noinspection unchecked
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>((Class<T>) clazz, index);
@@ -16,21 +16,10 @@
package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import static org.springframework.util.CollectionUtils.*;
import static org.springframework.util.CollectionUtils.isEmpty;
import co.elastic.clients.elasticsearch._types.Conflicts;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.InlineScript;
import co.elastic.clients.elasticsearch._types.OpType;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.VersionType;
import co.elastic.clients.elasticsearch._types.WaitForActiveShardOptions;
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.mapping.*;
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;
@@ -55,15 +44,10 @@ import jakarta.json.stream.JsonParser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -73,9 +57,7 @@ import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.ScriptType;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.index.DeleteTemplateRequest;
import org.springframework.data.elasticsearch.core.index.ExistsTemplateRequest;
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
@@ -144,14 +126,12 @@ class RequestConverter {
createRequestBuilder.index(indexCoordinates.getIndexName());
// note: the new client does not support the index.storeType anymore
String settingsJson = Document.from(settings).toJson();
IndexSettings indexSettings = fromJson(settingsJson, IndexSettings._DESERIALIZER);
createRequestBuilder.settings(indexSettings);
createRequestBuilder.settings(IndexSettings.of(b -> b //
.withJson(new StringReader(Document.from(settings).toJson()))));
if (mapping != null) {
String mappingJson = mapping.toJson();
TypeMapping typeMapping = fromJson(mappingJson, TypeMapping._DESERIALIZER);
createRequestBuilder.mappings(typeMapping);
createRequestBuilder.mappings(TypeMapping.of(b -> b //
.withJson(new StringReader(mapping.toJson()))));
}
return createRequestBuilder.build();
@@ -243,11 +223,12 @@ class RequestConverter {
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
Assert.notNull(mapping, "mapping must not be null");
PutMappingRequest.Builder builder = new PutMappingRequest.Builder();
builder.index(Arrays.asList(indexCoordinates.getIndexNames()));
addPropertiesToMapping(builder, mapping);
PutMappingRequest request = new PutMappingRequest.Builder() //
.withJson(new StringReader(mapping.toJson())) //
.index(Arrays.asList(indexCoordinates.getIndexNames())) //
.build();
return builder.build();
return request;
}
public GetMappingRequest indicesGetMappingRequest(IndexCoordinates indexCoordinates) {
@@ -257,23 +238,6 @@ class RequestConverter {
return new GetMappingRequest.Builder().index(Arrays.asList(indexCoordinates.getIndexNames())).build();
}
private void addPropertiesToMapping(PutMappingRequest.Builder builder, Document mapping) {
Object properties = mapping.get("properties");
if (properties != null) {
if (properties instanceof Map) {
Map<String, Property> propertiesMap = new HashMap<>();
// noinspection unchecked
((Map<String, Object>) properties).forEach((key, value) -> {
Property property = getProperty(value);
propertiesMap.put(key, property);
});
builder.properties(propertiesMap);
}
}
}
private Property getProperty(Object value) {
// noinspection SpellCheckingInspection
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -837,8 +801,8 @@ class RequestConverter {
});
}
public DeleteByQueryRequest documentDeleteByQueryRequest(Query query, Class<?> clazz, IndexCoordinates index,
@Nullable RefreshPolicy refreshPolicy) {
public DeleteByQueryRequest documentDeleteByQueryRequest(Query query, @Nullable String routing, Class<?> clazz,
IndexCoordinates index, @Nullable RefreshPolicy refreshPolicy) {
Assert.notNull(query, "query must not be null");
Assert.notNull(index, "index must not be null");
@@ -857,6 +821,8 @@ class RequestConverter {
if (query.getRoute() != null) {
b.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
b.routing(routing);
}
return b;
@@ -998,23 +964,24 @@ class RequestConverter {
// region search
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
boolean forCount) {
return searchRequest(query, clazz, indexCoordinates, forCount, false, null);
public <T> SearchRequest searchRequest(Query query, @Nullable String routing, @Nullable Class<T> clazz,
IndexCoordinates indexCoordinates, boolean forCount) {
return searchRequest(query, routing, 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 String routing, @Nullable Class<T> clazz,
IndexCoordinates indexCoordinates, boolean forCount, long scrollTimeInMillis) {
return searchRequest(query, routing, clazz, indexCoordinates, forCount, true, scrollTimeInMillis);
}
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
boolean forCount, boolean forBatchedSearch) {
return searchRequest(query, clazz, indexCoordinates, forCount, forBatchedSearch, null);
public <T> SearchRequest searchRequest(Query query, @Nullable String routing, @Nullable Class<T> clazz,
IndexCoordinates indexCoordinates, boolean forCount, boolean forBatchedSearch) {
return searchRequest(query, routing, clazz, indexCoordinates, forCount, forBatchedSearch, null);
}
public <T> SearchRequest searchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
boolean forCount, boolean forBatchedSearch, @Nullable Long scrollTimeInMillis) {
public <T> SearchRequest searchRequest(Query query, @Nullable String routing, @Nullable Class<T> clazz,
IndexCoordinates indexCoordinates, boolean forCount, boolean forBatchedSearch,
@Nullable Long scrollTimeInMillis) {
String[] indexNames = indexCoordinates.getIndexNames();
Assert.notNull(query, "query must not be null");
@@ -1022,7 +989,7 @@ class RequestConverter {
elasticsearchConverter.updateQuery(query, clazz);
SearchRequest.Builder builder = new SearchRequest.Builder();
prepareSearchRequest(query, clazz, indexCoordinates, builder, forCount, forBatchedSearch);
prepareSearchRequest(query, routing, clazz, indexCoordinates, builder, forCount, forBatchedSearch);
if (scrollTimeInMillis != null) {
builder.scroll(t -> t.time(scrollTimeInMillis + "ms"));
@@ -1030,13 +997,20 @@ class RequestConverter {
builder.query(getQuery(query, clazz));
if (StringUtils.hasText(query.getRoute())) {
builder.routing(query.getRoute());
}
if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
addFilter(query, builder);
return builder.build();
}
public MsearchRequest searchMsearchRequest(
List<ElasticsearchTemplate.MultiSearchQueryParameter> multiSearchQueryParameters) {
List<ElasticsearchTemplate.MultiSearchQueryParameter> multiSearchQueryParameters, @Nullable String routing) {
// basically the same stuff as in prepareSearchRequest, but the new Elasticsearch has different builders for a
// normal search and msearch
@@ -1049,11 +1023,16 @@ class RequestConverter {
.header(h -> {
h //
.index(Arrays.asList(param.index().getIndexNames())) //
.routing(query.getRoute()) //
.searchType(searchType(query.getSearchType())) //
.requestCache(query.getRequestCache()) //
;
if (StringUtils.hasText(query.getRoute())) {
h.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
h.routing(routing);
}
if (query.getPreference() != null) {
h.preference(query.getPreference());
}
@@ -1142,6 +1121,9 @@ class RequestConverter {
bb.indicesBoost(boosts);
}
query.getScriptedFields().forEach(scriptedField -> bb.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
if (query instanceof NativeQuery) {
prepareNativeSearch((NativeQuery) query, bb);
}
@@ -1156,8 +1138,8 @@ class RequestConverter {
});
}
private <T> void prepareSearchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
SearchRequest.Builder builder, boolean forCount, boolean forBatchedSearch) {
private <T> void prepareSearchRequest(Query query, @Nullable String routing, @Nullable Class<T> clazz,
IndexCoordinates indexCoordinates, SearchRequest.Builder builder, boolean forCount, boolean forBatchedSearch) {
String[] indexNames = indexCoordinates.getIndexNames();
@@ -1190,6 +1172,8 @@ class RequestConverter {
if (query.getRoute() != null) {
builder.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
if (query.getPreference() != null) {
@@ -1242,6 +1226,9 @@ class RequestConverter {
addHighlight(query, builder);
query.getScriptedFields().forEach(scriptedField -> builder.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
if (query instanceof NativeQuery) {
prepareNativeSearch((NativeQuery) query, builder);
}
@@ -1407,14 +1394,15 @@ class RequestConverter {
@SuppressWarnings("DuplicatedCode")
private void prepareNativeSearch(NativeQuery query, SearchRequest.Builder builder) {
query.getScriptedFields().forEach(scriptedField -> builder.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
builder //
.suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) //
.sort(query.getSortOptions()) //
.knn(query.getKnnQuery());
;
if (query.getKnnQuery() != null) {
builder.knn(query.getKnnQuery());
}
if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations());
@@ -1428,9 +1416,6 @@ class RequestConverter {
@SuppressWarnings("DuplicatedCode")
private void prepareNativeSearch(NativeQuery query, MultisearchBody.Builder builder) {
query.getScriptedFields().forEach(scriptedField -> builder.scriptFields(scriptedField.getFieldName(),
sf -> sf.script(getScript(scriptedField.getScriptData()))));
builder //
.suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) //
@@ -1559,7 +1544,8 @@ class RequestConverter {
return ClosePointInTimeRequest.of(cpit -> cpit.id(pit));
}
public SearchTemplateRequest searchTemplate(SearchTemplateQuery query, IndexCoordinates index) {
public SearchTemplateRequest searchTemplate(SearchTemplateQuery query, @Nullable String routing,
IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
@@ -1570,10 +1556,15 @@ class RequestConverter {
.id(query.getId()) //
.index(Arrays.asList(index.getIndexNames())) //
.preference(query.getPreference()) //
.routing(query.getRoute()) //
.searchType(searchType(query.getSearchType())).source(query.getSource()) //
;
if (query.getRoute() != null) {
builder.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
var expandWildcards = query.getExpandWildcards();
if (!expandWildcards.isEmpty()) {
builder.expandWildcards(expandWildcards(expandWildcards));
@@ -261,7 +261,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(query, clazz, index);
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index);
return ResponseConverter
.byQueryResponseOf(execute(client -> client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT)));
}
@@ -398,7 +399,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
final Boolean trackTotalHits = query.getTrackTotalHits();
query.setTrackTotalHits(true);
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
query.setTrackTotalHits(trackTotalHits);
searchRequest.source().size(0);
@@ -409,7 +410,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
@Override
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
@@ -431,7 +432,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query.getPageable(), "pageable of query must not be null.");
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
SearchRequest searchRequest = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
searchRequest.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
@@ -477,7 +478,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
MultiSearchRequest request = new MultiSearchRequest();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, clazz, index));
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
@@ -504,7 +505,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
Class<?> clazz = it.next();
request.add(requestFactory.searchRequest(query, clazz, getIndexCoordinatesFor(clazz)));
request
.add(requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, getIndexCoordinatesFor(clazz)));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
@@ -538,7 +540,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
MultiSearchRequest request = new MultiSearchRequest();
Iterator<Class<?>> it = classes.iterator();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, it.next(), index));
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), it.next(), index));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
@@ -572,7 +574,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
Iterator<Class<?>> it = classes.iterator();
Iterator<IndexCoordinates> indexesIt = indexes.iterator();
for (Query query : queries) {
request.add(requestFactory.searchRequest(query, it.next(), indexesIt.next()));
request.add(requestFactory.searchRequest(query, routingResolver.getRouting(), it.next(), indexesIt.next()));
}
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
@@ -374,7 +374,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
protected Mono<BulkByScrollResponse> doDeleteBy(Query query, Class<?> entityType, IndexCoordinates index) {
return Mono.defer(() -> {
DeleteByQueryRequest request = requestFactory.deleteByQueryRequest(query, entityType, index);
DeleteByQueryRequest request = requestFactory.deleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index);
return doDeleteBy(prepareDeleteByRequest(request));
});
}
@@ -505,7 +506,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return Flux.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
boolean useScroll = !(query.getPageable().isPaged() || query.isLimiting());
request = prepareSearchRequest(request, useScroll);
@@ -520,7 +521,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
protected <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
return Mono.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), clazz, index);
request = prepareSearchRequest(request, false);
SearchDocumentCallback<?> documentCallback = new ReadSearchDocumentCallback<>(clazz, index);
@@ -540,7 +541,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(index, "index must not be null");
return Flux.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, entityType, index);
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), entityType, index);
request = prepareSearchRequest(request, false);
return doAggregate(request);
});
@@ -565,7 +566,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
protected Mono<Long> doCount(Query query, Class<?> entityType, IndexCoordinates index) {
return Mono.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, entityType, index);
SearchRequest request = requestFactory.searchRequest(query, routingResolver.getRouting(), entityType, index);
request = prepareSearchRequest(request, false);
return doCount(request);
});
@@ -525,8 +525,8 @@ class RequestFactory {
// endregion
// region delete
public DeleteByQueryRequest deleteByQueryRequest(Query query, Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = searchRequest(query, clazz, index);
public DeleteByQueryRequest deleteByQueryRequest(Query query, @Nullable String routing, Class<?> clazz, IndexCoordinates index) {
SearchRequest searchRequest = searchRequest(query, routing, clazz, index);
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index.getIndexNames()) //
.setQuery(searchRequest.source().query()) //
.setAbortOnVersionConflict(false) //
@@ -544,6 +544,8 @@ class RequestFactory {
if (query.getRoute() != null) {
deleteByQueryRequest.setRouting(query.getRoute());
} else if (StringUtils.hasText(routing)) {
deleteByQueryRequest.setRouting(routing);
}
return deleteByQueryRequest;
@@ -751,10 +753,10 @@ class RequestFactory {
return searchRequest;
}
public SearchRequest searchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
public SearchRequest searchRequest(Query query, @Nullable String routing, @Nullable Class<?> clazz, IndexCoordinates index) {
elasticsearchConverter.updateQuery(query, clazz);
SearchRequest searchRequest = prepareSearchRequest(query, clazz, index);
SearchRequest searchRequest = prepareSearchRequest(query, routing,clazz, index);
QueryBuilder elasticsearchQuery = getQuery(query);
QueryBuilder elasticsearchFilter = getFilter(query);
@@ -768,7 +770,7 @@ class RequestFactory {
}
private SearchRequest prepareSearchRequest(Query query, @Nullable Class<?> clazz, IndexCoordinates indexCoordinates) {
private SearchRequest prepareSearchRequest(Query query, @Nullable String routing, @Nullable Class<?> clazz, IndexCoordinates indexCoordinates) {
String[] indexNames = indexCoordinates.getIndexNames();
Assert.notNull(indexNames, "No index defined for Query");
@@ -843,6 +845,8 @@ class RequestFactory {
if (StringUtils.hasLength(query.getRoute())) {
request.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
request.routing(routing);
}
Duration timeout = query.getTimeout();
@@ -66,7 +66,7 @@ import org.springframework.util.StringUtils;
/**
* This class contains methods that are common to different implementations of the {@link ElasticsearchOperations}
* interface that use different clients, like RestHighLevelClient and the next Java client from Elasticsearch or some
* future implementation that might use an Opensearch client. This class must not contain imports or use classes that
* external implementation that might use a different client. This class must not contain imports or use classes that
* are specific to one of these implementations.
* <p>
* <strong>Note:</strong> Although this class is public, it is not considered to be part of the official Spring Data
@@ -17,13 +17,17 @@ package org.springframework.data.elasticsearch.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.util.function.Tuple2;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -207,6 +211,61 @@ abstract public class AbstractReactiveElasticsearchTemplate
return save(entity, getIndexCoordinatesFor(entity.getClass()));
}
@Override
public <T> Flux<T> save(Flux<T> entities, Class<?> clazz, int bulkSize) {
return save(entities, getIndexCoordinatesFor(clazz), bulkSize);
}
@Override
public <T> Flux<T> save(Flux<T> entities, IndexCoordinates index, int bulkSize) {
Assert.notNull(entities, "entities must not be null");
Assert.notNull(index, "index must not be null");
Assert.isTrue(bulkSize > 0, "bulkSize must be greater than 0");
return Flux.defer(() -> {
Sinks.Many<T> sink = Sinks.many().unicast().onBackpressureBuffer();
entities //
.bufferTimeout(bulkSize, Duration.ofMillis(200)) //
.subscribe(new Subscriber<List<T>>() {
private Subscription subscription;
private AtomicBoolean upstreamComplete = new AtomicBoolean(false);
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(List<T> entityList) {
saveAll(entityList, index) //
.map(sink::tryEmitNext) //
.doOnComplete(() -> {
if (!upstreamComplete.get()) {
subscription.request(1);
} else {
sink.tryEmitComplete();
}
}).subscribe();
}
@Override
public void onError(Throwable throwable) {
subscription.cancel();
sink.tryEmitError(throwable);
}
@Override
public void onComplete() {
upstreamComplete.set(true);
}
});
return sink.asFlux();
});
}
@Override
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entities, Class<T> clazz) {
return saveAll(entities, getIndexCoordinatesFor(clazz));
@@ -44,6 +44,9 @@ import org.springframework.util.Assert;
* @since 4.0
*/
public interface ReactiveDocumentOperations {
int FLUX_SAVE_BULK_SIZE = 500;
/**
* Index the given entity, once available, extracting index from entity metadata.
*
@@ -93,6 +96,61 @@ public interface ReactiveDocumentOperations {
*/
<T> Mono<T> save(T entity, IndexCoordinates index);
/**
* Indexes the entities into the index extracted from entity metadata.
*
* @param entities
* @param clazz the class to get the index name from
* @param <T> entity type
* @return a Flux emitting the saved entities
* @since 5.1
*/
default <T> Flux<T> save(Flux<T> entities, Class<?> clazz) {
return save(entities, clazz, FLUX_SAVE_BULK_SIZE);
}
/**
* Indexes the entities into the index extracted from entity metadata. The entities are collected into batches of
* {bulkSize} with a maximal timeout of 200 ms, see
* {@link reactor.core.publisher.Flux#bufferTimeout(int, java.time .Duration)} and then sent in a bulk operation to
* Elasticsearch.
*
* @param entities
* @param clazz the class to get the index name from
* @param bulkSize number of entities to put in a bulk request
* @param <T> entity type
* @return a Flux emitting the saved entities
* @since 5.1
*/
<T> Flux<T> save(Flux<T> entities, Class<?> clazz, int bulkSize);
/**
* Indexes the entities into the given index.
*
* @param entities the entities to save
* @param index the index to save to
* @param <T> entity type
* @return a Flux emitting the saved entities
* @since 5.1
*/
default <T> Flux<T> save(Flux<T> entities, IndexCoordinates index) {
return save(entities, index, FLUX_SAVE_BULK_SIZE);
}
/**
* Indexes the entities into the given index. The entities are collected into batches of {bulkSize} with a maximal
* timeout of 200 ms, see {@link reactor.core.publisher.Flux#bufferTimeout(int, java.time * .Duration)} and then sent
* in a bulk operation to Elasticsearch.
*
* @param entities the entities to save
* @param index the index to save to
* @param bulkSize number of entities to put in a bulk request
* @param <T> entity type
* @return a Flux emitting the saved entities
* @since 5.1
*/
<T> Flux<T> save(Flux<T> entities, IndexCoordinates index, int bulkSize);
/**
* Index entities the index extracted from entity metadata.
*
@@ -45,6 +45,7 @@ import org.springframework.util.Assert;
* @author Roman Puchkovskiy
* @author Matt Gilene
* @author Sascha Woo
* @author Jakob Hoeper
* @since 4.0
*/
public class SearchHitMapping<T> {
@@ -199,9 +200,10 @@ public class SearchHitMapping<T> {
}
try {
ElasticsearchPersistentEntity<?> persistentEntityForType = mappingContext.getPersistentEntity(type);
NestedMetaData nestedMetaData = searchHits.getSearchHit(0).getContent().getNestedMetaData();
ElasticsearchPersistentEntityWithNestedMetaData persistentEntityWithNestedMetaData = getPersistentEntity(
mappingContext.getPersistentEntity(type), nestedMetaData);
persistentEntityForType, nestedMetaData);
if (persistentEntityWithNestedMetaData.entity != null) {
List<SearchHit<Object>> convertedSearchHits = new ArrayList<>();
@@ -219,7 +221,8 @@ public class SearchHitMapping<T> {
searchDocument.getSortValues(), //
searchDocument.getHighlightFields(), //
searchHit.getInnerHits(), //
persistentEntityWithNestedMetaData.nestedMetaData, //
getPersistentEntity(persistentEntityForType, //
searchHit.getContent().getNestedMetaData()).nestedMetaData, //
searchHit.getExplanation(), //
searchHit.getMatchedQueries(), //
targetObject));
@@ -941,7 +941,8 @@ public class MappingElasticsearchConverter
if (!property.isWritable() //
|| property.isIndexedIndexNameProperty() //
|| (property.isIdProperty() && !entity.storeIdInSource())) {
|| (property.isIdProperty() && !entity.storeIdInSource()) //
|| (property.isVersionProperty() && !entity.storeVersionInSource())) {
continue;
}
@@ -960,6 +961,10 @@ public class MappingElasticsearchConverter
continue;
}
if (!property.storeEmptyValue() && hasEmptyValue(value)) {
continue;
}
if (property.hasPropertyValueConverter()) {
value = propertyConverterWrite(property, value);
sink.set(property, value);
@@ -988,6 +993,16 @@ public class MappingElasticsearchConverter
}
}
private static boolean hasEmptyValue(Object value) {
if (value instanceof String s && s.isEmpty() || value instanceof Collection<?> c && c.isEmpty()
|| value instanceof Map<?, ?> m && m.isEmpty()) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
protected void writeProperty(ElasticsearchPersistentProperty property, Object value, MapValueAccessor sink) {
@@ -1404,7 +1419,7 @@ public class MappingElasticsearchConverter
}
if (!fieldName.contains(".")) {
if (property.hasExplicitFieldName() || !fieldName.contains(".")) {
return target.get(fieldName);
}
@@ -181,4 +181,10 @@ public interface ElasticsearchPersistentEntity<T> extends PersistentEntity<T, El
* @since 5.1
*/
boolean storeIdInSource();
/**
* @return the storeVersionInSource value from the document annotation.
* @since 5.1
*/
boolean storeVersionInSource();
}
@@ -39,6 +39,12 @@ public interface ElasticsearchPersistentProperty extends PersistentProperty<Elas
*/
String getFieldName();
/**
* @return {@literal true} if the field name comes from an explicit value in the field annotation
* @since 5.1
*/
boolean hasExplicitFieldName();
/**
* Returns whether the current property is a {@link SeqNoPrimaryTerm} property.
*
@@ -74,6 +80,13 @@ public interface ElasticsearchPersistentProperty extends PersistentProperty<Elas
*/
boolean storeNullValue();
/**
* @return true if empty values ({{@link String}}, or {{@link java.util.Collection}} or {{@link java.util.Map}})
* should be store in Elasticsearch.
* @since 5.1
*/
boolean storeEmptyValue();
/**
* @return {@literal true} if this is a GeoPoint property
* @since 4.1
@@ -100,7 +113,7 @@ public interface ElasticsearchPersistentProperty extends PersistentProperty<Elas
/**
* @return {@literal true} if this is a property annotated with
* {@link org.springframework.data.elasticsearch.annotations.IndexedIndexName}.
* {@link org.springframework.data.elasticsearch.annotations.IndexedIndexName}.
* @since 5.1
*/
boolean isIndexedIndexNameProperty();
@@ -84,6 +84,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private final Lazy<EvaluationContext> indexNameEvaluationContext = Lazy.of(this::getIndexNameEvaluationContext);
private final boolean storeIdInSource;
private final boolean storeVersionInSource;
public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation,
ContextConfiguration contextConfiguration) {
@@ -108,9 +109,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
this.createIndexAndMapping = document.createIndex();
this.dynamic = document.dynamic();
this.storeIdInSource = document.storeIdInSource();
this.storeVersionInSource = document.storeVersionInSource();
} else {
this.dynamic = Dynamic.INHERIT;
this.storeIdInSource = true;
this.storeVersionInSource = true;
}
Routing routingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Routing.class);
@@ -200,6 +203,11 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return storeIdInSource;
}
@Override
public boolean storeVersionInSource() {
return storeVersionInSource;
}
@Override
public void addPersistentProperty(ElasticsearchPersistentProperty property) {
super.addPersistentProperty(property);
@@ -35,6 +35,7 @@ import org.springframework.data.elasticsearch.annotations.IndexedIndexName;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.annotations.WriteOnlyProperty;
import org.springframework.data.elasticsearch.core.convert.AbstractPropertyValueConverter;
import org.springframework.data.elasticsearch.core.convert.DatePropertyValueConverter;
import org.springframework.data.elasticsearch.core.convert.DateRangePropertyValueConverter;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchDateConverter;
@@ -82,6 +83,7 @@ public class SimpleElasticsearchPersistentProperty extends
private final @Nullable String annotatedFieldName;
@Nullable private PropertyValueConverter propertyValueConverter;
private final boolean storeNullValue;
private final boolean storeEmptyValue;
public SimpleElasticsearchPersistentProperty(Property property,
PersistentEntity<?, ElasticsearchPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
@@ -106,6 +108,7 @@ public class SimpleElasticsearchPersistentProperty extends
initPropertyValueConverter();
storeNullValue = isField && getRequiredAnnotation(Field.class).storeNullValue();
storeEmptyValue = isField ? getRequiredAnnotation(Field.class).storeEmptyValue() : true;
}
@Override
@@ -134,7 +137,13 @@ public class SimpleElasticsearchPersistentProperty extends
return storeNullValue;
}
protected boolean hasExplicitFieldName() {
@Override
public boolean storeEmptyValue() {
return storeEmptyValue;
}
@Override
public boolean hasExplicitFieldName() {
return StringUtils.hasText(getAnnotatedFieldName());
}
@@ -243,7 +252,11 @@ public class SimpleElasticsearchPersistentProperty extends
}
propertyValueConverter = enumConstants[0];
} else {
propertyValueConverter = BeanUtils.instantiateClass(clazz);
if (AbstractPropertyValueConverter.class.isAssignableFrom(clazz)) {
propertyValueConverter = BeanUtils.instantiateClass(BeanUtils.getResolvableConstructor(clazz), this);
} else {
propertyValueConverter = BeanUtils.instantiateClass(clazz);
}
}
}
}
@@ -81,6 +81,7 @@ public class BaseQuery implements Query {
@Nullable private Boolean allowNoIndices = null;
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
private List<DocValueField> docValueFields = new ArrayList<>();
private List<ScriptedField> scriptedFields = new ArrayList<>();
public BaseQuery() {}
@@ -115,6 +116,7 @@ public class BaseQuery implements Query {
this.allowNoIndices = builder.getAllowNoIndices();
this.expandWildcards = builder.getExpandWildcards();
this.docValueFields = builder.getDocValueFields();
this.scriptedFields = builder.getScriptedFields();
}
/**
@@ -224,7 +226,7 @@ public class BaseQuery implements Query {
}
/**
* Set Ids for a multi-get request with on this query.
* Set Ids for a multi-get request run with this query. Not used in any other searches.
*
* @param ids list of id values
*/
@@ -253,7 +255,7 @@ public class BaseQuery implements Query {
}
/**
* Set Ids with routing values for a multi-get request set on this query.
* Set Ids with routing values for a multi-get request run with this query. Not used in any other searches.
*
* @param idsWithRouting list of id values, must not be {@literal null}
* @since 4.3
@@ -543,4 +545,9 @@ public class BaseQuery implements Query {
this.docValueFields = docValueFields;
}
@Override
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
}
@@ -71,6 +71,7 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
@Nullable Integer reactiveBatchSize;
private final List<DocValueField> docValueFields = new ArrayList<>();
private final List<ScriptedField> scriptedFields = new ArrayList<>();
@Nullable
public Sort getSort() {
@@ -226,6 +227,10 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return docValueFields;
}
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
public SELF withPageable(Pageable pageable) {
this.pageable = pageable;
return self();
@@ -245,6 +250,11 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
/**
* Set Ids for a multi-get request run with this query. Not used in any other searches.
*
* @param ids list of id values
*/
public SELF withIds(String... ids) {
this.ids.clear();
@@ -252,6 +262,11 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
/**
* Set Ids for a multi-get request run with this query. Not used in any other searches.
*
* @param ids list of id values
*/
public SELF withIds(Collection<String> ids) {
Assert.notNull(ids, "ids must not be null");
@@ -367,6 +382,12 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
/**
* Set Ids with routing values for a multi-get request run with this query. Not used in any other searches.
*
* @param idsWithRouting list of id values, must not be {@literal null}
* @since 4.3
*/
public SELF withIdsWithRouting(List<Query.IdWithRouting> idsWithRouting) {
Assert.notNull(idsWithRouting, "idsWithRouting must not be null");
@@ -443,6 +464,14 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
public SELF withScriptedField(ScriptedField scriptedField) {
Assert.notNull(scriptedField, "scriptedField must not be null");
this.scriptedFields.add(scriptedField);
return self();
}
public abstract Q build();
private SELF self() {
@@ -477,6 +477,12 @@ public interface Query {
*/
List<DocValueField> getDocValueFields();
/**
* @return the list of scripted fields for the query
* @since 5.1
*/
List<ScriptedField> getScriptedFields();
/**
* @since 4.3
*/
@@ -15,7 +15,6 @@
*/
package org.springframework.data.elasticsearch.repository.support;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -30,6 +29,7 @@ import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.util.Assert;
@@ -97,7 +97,7 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
Assert.notNull(entityStream, "EntityStream must not be null!");
return operations.saveAll(Flux.from(entityStream).collectList(), entityInformation.getIndexCoordinates())
return operations.save(Flux.from(entityStream), entityInformation.getIndexCoordinates())
.concatWith(doRefresh().then(Mono.empty()));
}
+4 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 5.1 M1 (2023.0.0)
Spring Data Elasticsearch 5.1 RC1 (2023.0.0)
Copyright (c) [2013-2022] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -9,3 +9,6 @@ separate copyright notices and license terms. Your use of the source
code for the these subcomponents is subject to the terms and
conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -153,15 +153,6 @@ public class ClientConfigurationUnitTests {
assertThat(clientConfiguration.getHostNameVerifier()).contains(NoopHostnameVerifier.INSTANCE);
}
@Test // DATAES-719
void shouldHaveDefaultWebClientConfigurer() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder() //
.connectedTo("foo", "bar") //
.build();
assertThat(clientConfiguration.getWebClientConfigurer()).isEqualTo(Function.identity());
}
@Test // #1885
@DisplayName("should use configured httpClientConfigurer as client configurer")
void shouldUseConfiguredHttpClientConfigurerAsClientConfigurer() {
@@ -40,7 +40,7 @@ public class ElasticsearchPartQueryELCIntegrationTests extends ElasticsearchPart
JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper();
RequestConverter requestConverter = new RequestConverter(operations.getElasticsearchConverter(), jsonpMapper);
SearchRequest request = requestConverter.searchRequest(query, clazz, IndexCoordinates.of("dummy"), false);
SearchRequest request = requestConverter.searchRequest(query, null, clazz, IndexCoordinates.of("dummy"), false);
return JsonUtils.toJson(request, jsonpMapper);
// return "{\"query\":" + JsonUtils.toJson(request.query(), jsonpMapper) + "}";
@@ -62,7 +62,7 @@ class RequestConverterTest {
.withDocValueFields(docValueFields) //
.build();
var searchRequest = requestConverter.searchRequest(query, SampleEntity.class, IndexCoordinates.of("foo"), true);
var searchRequest = requestConverter.searchRequest(query,null, SampleEntity.class, IndexCoordinates.of("foo"), true);
var fieldAndFormats = searchRequest.docvalueFields();
assertThat(fieldAndFormats).hasSize(2);
@@ -40,7 +40,7 @@ public class ElasticsearchPartQueryERHLCIntegrationTests extends ElasticsearchPa
protected String buildQueryString(Query query, Class<?> clazz) {
SearchSourceBuilder source = new RequestFactory(operations.getElasticsearchConverter())
.searchRequest(query, clazz, IndexCoordinates.of("dummy")).source();
.searchRequest(query, null, clazz, IndexCoordinates.of("dummy")).source();
// remove defaultboost values
return source.toString().replaceAll("(\\^\\d+\\.\\d+)", "");
}
@@ -110,7 +110,7 @@ class RequestFactoryTests {
.build();
// when
SearchRequest searchRequest = requestFactory.searchRequest(query, null, IndexCoordinates.of("test"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, null, IndexCoordinates.of("test"));
// then
assertThat(searchRequest.source().from()).isEqualTo(30);
@@ -218,21 +218,35 @@ class RequestFactoryTests {
" ]" + //
'}';
String searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons")).source()
.toString();
String searchRequest = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"))
.source().toString();
assertEquals(expected, searchRequest, false);
}
@Test
// DATAES-449
void shouldAddRouting() {
void shouldAddRoutingFromQuery() {
String route = "route66";
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
query.setRoute(route);
converter.updateQuery(query, Person.class);
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.routing()).isEqualTo(route);
}
@Test
// #2087
void shouldAddRoutingFromRouting() {
String route = "route66";
CriteriaQuery query = new CriteriaQuery(new Criteria("lastName").is("Smith"));
converter.updateQuery(query, Person.class);
SearchRequest searchRequest = requestFactory.searchRequest(query, route, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.routing()).isEqualTo(route);
}
@@ -242,7 +256,8 @@ class RequestFactoryTests {
void shouldAddMaxQueryWindowForUnpagedToRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withPageable(Pageable.unpaged()).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().from()).isEqualTo(0);
assertThat(searchRequest.source().size()).isEqualTo(RequestFactory.INDEX_MAX_RESULT_WINDOW);
@@ -267,7 +282,7 @@ class RequestFactoryTests {
void shouldNotRequestSeqNoAndPrimaryTermWhenEntityClassDoesNotContainSeqNoPrimaryTermProperty() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest request = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"));
assertThat(request.source().seqNoAndPrimaryTerm()).isNull();
}
@@ -277,7 +292,7 @@ class RequestFactoryTests {
void shouldRequestSeqNoAndPrimaryTermWhenEntityClassContainsSeqNoPrimaryTermProperty() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, EntityWithSeqNoPrimaryTerm.class,
SearchRequest request = requestFactory.searchRequest(query, null, EntityWithSeqNoPrimaryTerm.class,
IndexCoordinates.of("seqNoPrimaryTerm"));
assertThat(request.source().seqNoAndPrimaryTerm()).isTrue();
@@ -288,7 +303,7 @@ class RequestFactoryTests {
void shouldNotRequestSeqNoAndPrimaryTermWhenEntityClassIsNull() {
Query query = new NativeSearchQueryBuilder().build();
SearchRequest request = requestFactory.searchRequest(query, null, IndexCoordinates.of("persons"));
SearchRequest request = requestFactory.searchRequest(query, null, null, IndexCoordinates.of("persons"));
assertThat(request.source().seqNoAndPrimaryTerm()).isNull();
}
@@ -516,7 +531,8 @@ class RequestFactoryTests {
void shouldSetTimeoutToRequest() {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withTimeout(Duration.ofSeconds(1)).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().timeout().getMillis()).isEqualTo(Duration.ofSeconds(1).toMillis());
}
@@ -622,8 +638,8 @@ class RequestFactoryTests {
]
}""";
String searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons")).source()
.toString();
String searchRequest = requestFactory.searchRequest(query, null, Person.class, IndexCoordinates.of("persons"))
.source().toString();
assertEquals(expected, searchRequest, false);
}
@@ -634,7 +650,8 @@ class RequestFactoryTests {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isNull();
}
@@ -646,7 +663,8 @@ class RequestFactoryTests {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
query.setRequestCache(true);
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isTrue();
}
@@ -658,7 +676,8 @@ class RequestFactoryTests {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
query.setRequestCache(false);
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.requestCache()).isFalse();
}
@@ -670,7 +689,8 @@ class RequestFactoryTests {
Query query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withStoredFields("lastName", "location")
.build();
SearchRequest searchRequest = requestFactory.searchRequest(query, Person.class, IndexCoordinates.of("persons"));
SearchRequest searchRequest = requestFactory.searchRequest(query, null, Person.class,
IndexCoordinates.of("persons"));
assertThat(searchRequest.source().storedFields()).isNotNull();
assertThat(searchRequest.source().storedFields().fieldNames())
@@ -42,6 +42,7 @@ import org.springframework.lang.Nullable;
* Testing the querying and parsing of inner_hits.
*
* @author Peter-Josef Meisch
* @author Jakob Hoeper
*/
@SpringIntegrationTest
public abstract class InnerHitsIntegrationTests {
@@ -58,8 +59,9 @@ public abstract class InnerHitsIntegrationTests {
indexOps.createWithMapping();
Inhabitant john = new Inhabitant("John", "Smith");
Inhabitant carla = new Inhabitant("Carla", "Miller");
House cornerHouse = new House("Round the corner", "7", Arrays.asList(john, carla));
Inhabitant carla1 = new Inhabitant("Carla", "Miller");
Inhabitant carla2 = new Inhabitant("Carla", "Nguyen");
House cornerHouse = new House("Round the corner", "7", Arrays.asList(john, carla1, carla2));
City metropole = new City("Metropole", Arrays.asList(cornerHouse));
Inhabitant jack = new Inhabitant("Jack", "Wayne");
@@ -76,7 +78,7 @@ public abstract class InnerHitsIntegrationTests {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
}
@Test
@Test // #2521
void shouldReturnInnerHits() {
Query query = buildQueryForInnerHits("inner_hit_name", "hou-ses.in-habi-tants", "hou-ses.in-habi-tants.first-name",
@@ -91,7 +93,7 @@ public abstract class InnerHitsIntegrationTests {
softly.assertThat(searchHit.getInnerHits()).hasSize(1);
SearchHits<?> innerHits = searchHit.getInnerHits("inner_hit_name");
softly.assertThat(innerHits).hasSize(1);
softly.assertThat(innerHits).hasSize(2);
SearchHit<?> innerHit = innerHits.getSearchHit(0);
Object content = innerHit.getContent();
@@ -106,6 +108,10 @@ public abstract class InnerHitsIntegrationTests {
softly.assertThat(nestedMetaData.getChild().getField()).isEqualTo("inhabitants");
softly.assertThat(nestedMetaData.getChild().getOffset()).isEqualTo(1);
innerHit = innerHits.getSearchHit(1);
softly.assertThat(((Inhabitant) innerHit.getContent()).getLastName()).isEqualTo("Nguyen");
softly.assertThat(innerHit.getNestedMetaData().getChild().getOffset()).isEqualTo(2);
softly.assertAll();
}
@@ -20,7 +20,7 @@ import static org.assertj.core.api.Assertions.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import org.springframework.data.elasticsearch.annotations.IndexedIndexName;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -61,6 +61,7 @@ import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.IndexedIndexName;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.annotations.WriteOnlyProperty;
@@ -170,7 +171,6 @@ public abstract class ReactiveElasticsearchIntegrationTests {
assertThat(saved.getIndexedIndexName()).isEqualTo(indexNameProvider.indexName() + "-indexedindexname");
}
private Mono<Boolean> documentWithIdExistsInIndex(String id, String index) {
return operations.exists(id, IndexCoordinates.of(index));
}
@@ -1180,6 +1180,25 @@ public abstract class ReactiveElasticsearchIntegrationTests {
assertThat(readEntity.getPart2()).isEqualTo(entity.getPart2()); //
}).verifyComplete();
}
@Test // #2496
@DisplayName("should save data from Flux and return saved data in a flux")
void shouldSaveDataFromFluxAndReturnSavedDataInAFlux() {
var count = 12_345;
var entityList = IntStream.rangeClosed(1, count)//
.mapToObj(SampleEntity::of) //
.collect(Collectors.toList());
var entityFlux = Flux.fromIterable(entityList);
operations.save(entityFlux, SampleEntity.class).collectList() //
.as(StepVerifier::create) //
.consumeNextWith(savedEntities -> {
assertThat(savedEntities).isEqualTo(entityList);
}) //
.verifyComplete();
}
// endregion
// region Helper functions
@@ -1262,6 +1281,13 @@ public abstract class ReactiveElasticsearchIntegrationTests {
@Nullable
@Version private Long version;
static SampleEntity of(int id) {
var entity = new SampleEntity();
entity.setId("" + id);
entity.setMessage(" message " + id);
return entity;
}
@Nullable
public String getId() {
return id;
@@ -1543,6 +1569,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
this.part2 = part2;
}
}
@Document(indexName = "#{@indexNameProvider.indexName()}-indexedindexname")
private static class IndexedIndexNameEntity {
@Nullable
@@ -1550,8 +1577,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
@Nullable
@Field(type = Text) private String someText;
@Nullable
@IndexedIndexName
private String indexedIndexName;
@IndexedIndexName private String indexedIndexName;
@Nullable
public String getId() {
@@ -1579,5 +1605,6 @@ public abstract class ReactiveElasticsearchIntegrationTests {
public void setIndexedIndexName(@Nullable String indexedIndexName) {
this.indexedIndexName = indexedIndexName;
}
} // endregion
}
// endregion
}
@@ -50,6 +50,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.annotation.Version;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.domain.Range;
@@ -1893,6 +1894,101 @@ public class MappingElasticsearchConverterUnitTests {
assertEquals(expected, json, true);
}
@Test // #2364
@DisplayName("should not write version property to document source if configured so")
void shouldNotWriteVersionPropertyToDocumentSourceIfConfiguredSo() throws JSONException {
@Language("JSON")
var expected = """
{
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$DontWriteVersionToSourceEntity",
"id": "42",
"text": "some text"
}
""";
var entity = new DontWriteVersionToSourceEntity();
entity.setId("42");
entity.setVersion(7L);
entity.setText("some text");
Document document = Document.create();
mappingElasticsearchConverter.write(entity, document);
String json = document.toJson();
assertEquals(expected, json, true);
}
@Test // #2290
@DisplayName("should respect field setting for empty properties")
void shouldRespectFieldSettingForEmptyProperties() throws JSONException {
@Language("JSON")
var expected = """
{
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$EntityWithPropertiesThatMightBeEmpty",
"id": "42",
"stringToWriteWhenEmpty": "",
"listToWriteWhenEmpty": [],
"mapToWriteWhenEmpty": {}
}
""";
var entity = new EntityWithPropertiesThatMightBeEmpty();
entity.setId("42");
entity.setStringToWriteWhenEmpty("");
entity.setStringToNotWriteWhenEmpty("");
entity.setListToWriteWhenEmpty(emptyList());
entity.setListToNotWriteWhenEmpty(emptyList());
entity.setMapToWriteWhenEmpty(emptyMap());
entity.setMapToNotWriteWhenEmpty(emptyMap());
Document document = Document.create();
mappingElasticsearchConverter.write(entity, document);
String json = document.toJson();
assertEquals(expected, json, true);
}
@Test // #2502
@DisplayName("should write entity with dotted field name")
void shouldWriteEntityWithDottedFieldName() throws JSONException {
@Language("JSON")
var expected = """
{
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$FieldNameDotsEntity",
"id": "42",
"dotted.field": "dotted field"
}
""";
var entity = new FieldNameDotsEntity();
entity.setId("42");
entity.setDottedField("dotted field");
Document document = Document.create();
mappingElasticsearchConverter.write(entity, document);
String json = document.toJson();
assertEquals(expected, json, true);
}
@Test // #2502
@DisplayName("should read entity with dotted field name")
void shouldReadEntityWithDottedFieldName() {
@Language("JSON")
String json = """
{
"id": "42",
"dotted.field": "dotted field"
}""";
Document document = Document.parse(json);
FieldNameDotsEntity entity = mappingElasticsearchConverter.read(FieldNameDotsEntity.class, document);
assertThat(entity.id).isEqualTo("42");
assertThat(entity.getDottedField()).isEqualTo("dotted field");
}
// region entities
public static class Sample {
@Nullable public @ReadOnlyProperty String readOnly;
@@ -2974,6 +3070,153 @@ public class MappingElasticsearchConverterUnitTests {
this.text = text;
}
}
@org.springframework.data.elasticsearch.annotations.Document(indexName = "doesnt-matter",
storeVersionInSource = false)
static class DontWriteVersionToSourceEntity {
@Nullable private String id;
@Version
@Nullable private Long version;
@Nullable
@Field(type = FieldType.Text) private String text;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public Long getVersion() {
return version;
}
public void setVersion(@Nullable Long version) {
this.version = version;
}
@Nullable
public String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
static class EntityWithPropertiesThatMightBeEmpty {
@Nullable private String id;
@Field(type = FieldType.Text)
@Nullable private String stringToWriteWhenEmpty;
@Field(type = FieldType.Text, storeEmptyValue = false)
@Nullable private String stringToNotWriteWhenEmpty;
@Field(type = FieldType.Nested)
@Nullable private List<String> listToWriteWhenEmpty;
@Field(type = FieldType.Nested, storeEmptyValue = false)
@Nullable private List<String> listToNotWriteWhenEmpty;
@Field(type = FieldType.Nested)
@Nullable private Map<String, String> mapToWriteWhenEmpty;
@Field(type = FieldType.Nested, storeEmptyValue = false)
@Nullable private Map<String, String> mapToNotWriteWhenEmpty;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getStringToWriteWhenEmpty() {
return stringToWriteWhenEmpty;
}
public void setStringToWriteWhenEmpty(@Nullable String stringToWriteWhenEmpty) {
this.stringToWriteWhenEmpty = stringToWriteWhenEmpty;
}
@Nullable
public String getStringToNotWriteWhenEmpty() {
return stringToNotWriteWhenEmpty;
}
public void setStringToNotWriteWhenEmpty(@Nullable String stringToNotWriteWhenEmpty) {
this.stringToNotWriteWhenEmpty = stringToNotWriteWhenEmpty;
}
@Nullable
public List<String> getListToWriteWhenEmpty() {
return listToWriteWhenEmpty;
}
public void setListToWriteWhenEmpty(@Nullable List<String> listToWriteWhenEmpty) {
this.listToWriteWhenEmpty = listToWriteWhenEmpty;
}
@Nullable
public List<String> getListToNotWriteWhenEmpty() {
return listToNotWriteWhenEmpty;
}
public void setListToNotWriteWhenEmpty(@Nullable List<String> listToNotWriteWhenEmpty) {
this.listToNotWriteWhenEmpty = listToNotWriteWhenEmpty;
}
@Nullable
public Map<String, String> getMapToWriteWhenEmpty() {
return mapToWriteWhenEmpty;
}
public void setMapToWriteWhenEmpty(@Nullable Map<String, String> mapToWriteWhenEmpty) {
this.mapToWriteWhenEmpty = mapToWriteWhenEmpty;
}
@Nullable
public Map<String, String> getMapToNotWriteWhenEmpty() {
return mapToNotWriteWhenEmpty;
}
public void setMapToNotWriteWhenEmpty(@Nullable Map<String, String> mapToNotWriteWhenEmpty) {
this.mapToNotWriteWhenEmpty = mapToNotWriteWhenEmpty;
}
}
static class FieldNameDotsEntity {
@Id
@Nullable private String id;
@Nullable
@Field(name = "dotted.field", type = FieldType.Text) private String dottedField;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getDottedField() {
return dottedField;
}
public void setDottedField(@Nullable String dottedField) {
this.dottedField = dottedField;
}
}
// endregion
private static String reverse(Object o) {
@@ -43,7 +43,16 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.NewElasticsearchClientDevelopment;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Dynamic;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.Mapping;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.annotations.TermVector;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.MappingContextBaseTests;
@@ -205,11 +214,37 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
}
@Test // #1767
@DisplayName("should write dynamic mapping annotations")
void shouldWriteDynamicMappingAnnotations() {
@DisplayName("should write dynamic mapping annotations on create")
void shouldWriteDynamicMappingAnnotationsOnCreate() {
IndexOperations indexOps = operations.indexOps(DynamicMappingAnnotationEntity.class);
indexOps.createWithMapping();
var mapping = indexOps.getMapping();
var dynamic = mapping.get("dynamic");
if (dynamic instanceof String s) {
assertThat(dynamic).isEqualTo("false");
} else {
assertThat(mapping.get("dynamic")).isEqualTo(false);
}
}
@Test // #2478
@DisplayName("should write dynamic mapping annotations on put")
void shouldWriteDynamicMappingAnnotationsOnPut() {
IndexOperations indexOps = operations.indexOps(DynamicMappingAnnotationEntity.class);
indexOps.create();
indexOps.putMapping();
var mapping = indexOps.getMapping();
var dynamic = mapping.get("dynamic");
if (dynamic instanceof String s) {
assertThat(dynamic).isEqualTo("false");
} else {
assertThat(mapping.get("dynamic")).isEqualTo(false);
}
}
@Test // #1871
@@ -244,6 +279,14 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
indexOps.createWithMapping();
}
@Test // #2502
@DisplayName(" should write mapping with field name with dots")
void shouldWriteMappingWithFieldNameWithDots() {
IndexOperations indexOps = operations.indexOps(FieldNameDotsEntity.class);
indexOps.createWithMapping();
}
// region Entities
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Book {
@@ -866,5 +909,12 @@ public abstract class MappingBuilderIntegrationTests extends MappingContextBaseT
@Nullable
@Field(type = FieldType.Dense_Vector, dims = 1) String denseVectorField;
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
private static class FieldNameDotsEntity {
@Id
@Nullable private String id;
@Nullable
@Field(name = "dotted.field", type = Text) private String dottedField;
}
// endregion
}
@@ -1065,6 +1065,30 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
assertEquals(expected, mapping, true);
}
@Test // #2502
@DisplayName("should use custom name with dots")
void shouldUseCustomNameWithDots() throws JSONException {
var expected = """
{
"properties": {
"_class": {
"type": "keyword",
"index": false,
"doc_values": false
},
"dotted.field": {
"type": "text"
}
}
}
""";
String mapping = getMappingBuilder().buildPropertyMapping(FieldNameDotsEntity.class);
assertEquals(expected, mapping, true);
}
// region entities
@Document(indexName = "ignore-above-index")
@@ -2220,8 +2244,14 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
@Nullable
@Field(type = Text) private String someText;
@Nullable
@IndexedIndexName
private String storedIndexName;
@IndexedIndexName private String storedIndexName;
}
private static class FieldNameDotsEntity {
@Id
@Nullable private String id;
@Nullable
@Field(name = "dotted.field", type = Text) private String dottedField;
}
// endregion
}
@@ -34,8 +34,10 @@ import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.InnerField;
import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.core.convert.AbstractPropertyValueConverter;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -258,6 +260,8 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
assertThat(
persistentEntity.getRequiredPersistentProperty("fieldWithClassBasedConverter").getPropertyValueConverter())
.isInstanceOf(ClassBasedValueConverter.class);
assertThat(persistentEntity.getRequiredPersistentProperty("fieldWithClassBasedDerivedFromAbstractValueConverter")
.getPropertyValueConverter()).isInstanceOf(ClassBasedDerivedFromAbstractValueConverter.class);
assertThat(
persistentEntity.getRequiredPersistentProperty("fieldWithEnumBasedConverter").getPropertyValueConverter())
.isInstanceOf(EnumBasedValueConverter.class);
@@ -354,6 +358,8 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
@Nullable
@ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
@Nullable
@ValueConverter(ClassBasedDerivedFromAbstractValueConverter.class) private String fieldWithClassBasedDerivedFromAbstractValueConverter;
@Nullable
@ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
}
@@ -370,6 +376,23 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
}
}
private static class ClassBasedDerivedFromAbstractValueConverter extends AbstractPropertyValueConverter {
public ClassBasedDerivedFromAbstractValueConverter(PersistentProperty<?> property) {
super(property);
}
@Override
public Object write(Object value) {
return value;
}
@Override
public Object read(Object value) {
return value;
}
}
private enum EnumBasedValueConverter implements PropertyValueConverter {
INSTANCE;
@@ -112,7 +112,6 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
operations.bulkIndex(indexQueries, AnnotatedCompletionEntity.class);
}
// @DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionEntity() {
@@ -144,7 +143,6 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
operations.get("1", CompletionEntity.class);
}
// @DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test
public void shouldFindSuggestionsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
@@ -168,7 +166,6 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
assertThat(options.get(1).getText()).isIn("Marchand", "Mohsin");
}
// @DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES 1issue 150")
@Test
public void shouldFindSuggestionsWithWeightsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
@@ -66,7 +66,6 @@ public abstract class ReactiveSuggestIntegrationTests implements NewElasticsearc
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
}
// @DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
@Test // #1302
@DisplayName("should find suggestions for given prefix completion")
void shouldFindSuggestionsForGivenPrefixCompletion() {
@@ -20,7 +20,7 @@ package org.springframework.data.elasticsearch.junit.jupiter;
*/
public enum IntegrationtestEnvironment {
ELASTICSEARCH, OPENSEARCH, UNDEFINED;
ELASTICSEARCH, UNDEFINED;
public static final String SYSTEM_PROPERTY = "sde.integration-test.environment";
@@ -29,7 +29,6 @@ public enum IntegrationtestEnvironment {
String property = System.getProperty(SYSTEM_PROPERTY, "elasticsearch");
return switch (property.toUpperCase()) {
case "ELASTICSEARCH" -> ELASTICSEARCH;
case "OPENSEARCH" -> OPENSEARCH;
default -> UNDEFINED;
};
}
@@ -20,6 +20,7 @@ import java.util.Iterator;
import java.util.Optional;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.ParameterAccessor;
@@ -37,6 +38,11 @@ class StubParameterAccessor implements ElasticsearchParameterAccessor {
this.values = values;
}
@Override
public ScrollPosition getScrollPosition() {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
@@ -39,7 +39,7 @@ import org.springframework.lang.Nullable;
/**
* Integration tests to check that {@link org.springframework.data.elasticsearch.annotations.ValueConverter} annotated
* properties are handle correctly (method name derived queries, for
* properties are handled correctly (method name derived queries, for
*
* @{@link org.springframework.data.elasticsearch.core.query.Query} methods we don't know which parameters map to which
* property.
@@ -739,6 +739,24 @@ abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests {
.verifyComplete();
}
@Test // #2496
@DisplayName("should save data from Flux and return saved data in a flux")
void shouldSaveDataFromFluxAndReturnSavedDataInAFlux() {
var count = 12_345;
var entityList = IntStream.rangeClosed(1, count)//
.mapToObj(SampleEntity::of) //
.collect(Collectors.toList());
var entityFlux = Flux.fromIterable(entityList);
repository.saveAll(entityFlux).collectList() //
.as(StepVerifier::create) //
.consumeNextWith(savedEntities -> {
assertThat(savedEntities).isEqualTo(entityList);
}) //
.verifyComplete();
}
Mono<Void> bulkIndex(SampleEntity... entities) {
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(indexNameProvider.indexName())).then();
}
@@ -829,6 +847,13 @@ abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests {
@Field(name = "custom_field_name", type = FieldType.Text)
@Nullable private String customFieldNameMessage;
static SampleEntity of(int id) {
var entity = new SampleEntity();
entity.setId("" + id);
entity.setMessage(" message " + id);
return entity;
}
public SampleEntity() {}
public SampleEntity(@Nullable String id) {
@@ -15,10 +15,16 @@
*/
package org.springframework.data.elasticsearch.repository.support.querybyexample;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.assertj.core.api.AbstractThrowableAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -39,14 +45,6 @@ import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.lang.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.data.elasticsearch.utils.IdGenerator.nextIdAsString;
/**
* @author Ezequiel Antúnez Camacho
* @since 5.1
@@ -65,410 +63,398 @@ abstract class QueryByExampleElasticsearchExecutorIntegrationTests {
}
@Test // #2418
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
}
@Nested
@DisplayName("All QueryByExampleExecutor operations should work")
class QueryByExampleExecutorOperations {
@Test // #2418
void shouldFindOne() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void shouldFindOne() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
Optional<SampleEntity> entityFromElasticSearch = repository.findOne(Example.of(probe));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
Optional<SampleEntity> entityFromElasticSearch = repository.findOne(Example.of(probe));
// then
assertThat(entityFromElasticSearch).contains(sampleEntity2);
}
@Test // #2418
void shouldThrowExceptionIfMoreThanOneResultInFindOne() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("some message");
final Example<SampleEntity> example = Example.of(probe);
AbstractThrowableAssert<?, ? extends Throwable> assertThatThrownBy = assertThatThrownBy(
() -> repository.findOne(example));
// then
assertThatThrownBy.isInstanceOf(IncorrectResultSizeDataAccessException.class);
}
@Test // #2418
void shouldFindOneWithNestedField() {
// given
SampleEntity.SampleNestedEntity sampleNestedEntity = new SampleEntity.SampleNestedEntity();
sampleNestedEntity.setNestedData("sampleNestedData");
sampleNestedEntity.setAnotherNestedData("sampleAnotherNestedData");
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setSampleNestedEntity(sampleNestedEntity);
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity.SampleNestedEntity sampleNestedEntity2 = new SampleEntity.SampleNestedEntity();
sampleNestedEntity2.setNestedData("sampleNestedData2");
sampleNestedEntity2.setAnotherNestedData("sampleAnotherNestedData2");
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setSampleNestedEntity(sampleNestedEntity2);
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity.SampleNestedEntity sampleNestedEntityProbe = new SampleEntity.SampleNestedEntity();
sampleNestedEntityProbe.setNestedData("sampleNestedData");
SampleEntity probe = new SampleEntity();
probe.setSampleNestedEntity(sampleNestedEntityProbe);
Optional<SampleEntity> entityFromElasticSearch = repository.findOne(Example.of(probe));
// then
assertThat(entityFromElasticSearch).contains(sampleEntity);
}
@Test // #2418
void shouldFindAll() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("bye world");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
@Test // #2418
void shouldFindAllWithSort() {
// given
SampleEntity sampleEntityWithRate11 = new SampleEntity();
sampleEntityWithRate11.setDocumentId(nextIdAsString());
sampleEntityWithRate11.setMessage("hello world");
sampleEntityWithRate11.setRate(11);
sampleEntityWithRate11.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate13 = new SampleEntity();
sampleEntityWithRate13.setDocumentId(nextIdAsString());
sampleEntityWithRate13.setMessage("hello world");
sampleEntityWithRate13.setRate(13);
sampleEntityWithRate13.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate22 = new SampleEntity();
sampleEntityWithRate22.setDocumentId(nextIdAsString());
sampleEntityWithRate22.setMessage("hello world");
sampleEntityWithRate22.setRate(22);
sampleEntityWithRate22.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntityWithRate11, sampleEntityWithRate13, sampleEntityWithRate22));
// when
SampleEntity probe = new SampleEntity();
final Iterable<SampleEntity> all = repository.findAll();
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe),
Sort.by(Sort.Direction.DESC, "rate"));
// then
assertThat(sampleEntities).isNotNull().hasSize(3).containsExactly(sampleEntityWithRate22, sampleEntityWithRate13,
sampleEntityWithRate11);
}
@Test // #2418
void shouldFindAllWithPageable() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setRate(1);
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setRate(3);
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hello world");
sampleEntity3.setRate(2);
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
Iterable<SampleEntity> page1 = repository.findAll(Example.of(probe),
PageRequest.of(0, 2, Sort.Direction.DESC, "rate"));
Iterable<SampleEntity> page2 = repository.findAll(Example.of(probe),
PageRequest.of(1, 2, Sort.Direction.DESC, "rate"));
// then
assertThat(page1).isNotNull().hasSize(2).containsExactly(sampleEntity2, sampleEntity3);
assertThat(page2).isNotNull().hasSize(1).containsExactly(sampleEntity);
}
@Test // #2418
void shouldCount() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
final long count = repository.count(Example.of(probe));
// then
assertThat(count).isPositive();
}
@Test // #2418
void shouldExists() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
boolean exists = repository.exists(Example.of(probe));
// then
assertThat(exists).isTrue();
}
// then
assertThat(entityFromElasticSearch).contains(sampleEntity2);
}
@Nested
@DisplayName("All ExampleMatchers should work")
class AllExampleMatchersShouldWork {
@Test // #2418
void shouldThrowExceptionIfMoreThanOneResultInFindOne() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void defaultStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("some message");
final Example<SampleEntity> example = Example.of(probe);
AbstractThrowableAssert<?, ? extends Throwable> assertThatThrownBy = assertThatThrownBy(
() -> repository.findOne(example));
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// then
assertThatThrownBy.isInstanceOf(IncorrectResultSizeDataAccessException.class);
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT))));
}
// then
assertThat(sampleEntities).isNotNull().hasSize(1);
}
@Test // #2418
void shouldFindOneWithNestedField() {
// given
SampleEntity.SampleNestedEntity sampleNestedEntity = new SampleEntity.SampleNestedEntity();
sampleNestedEntity.setNestedData("sampleNestedData");
sampleNestedEntity.setAnotherNestedData("sampleAnotherNestedData");
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setSampleNestedEntity(sampleNestedEntity);
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void exactStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity.SampleNestedEntity sampleNestedEntity2 = new SampleEntity.SampleNestedEntity();
sampleNestedEntity2.setNestedData("sampleNestedData2");
sampleNestedEntity2.setAnotherNestedData("sampleAnotherNestedData2");
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setSampleNestedEntity(sampleNestedEntity2);
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
// when
SampleEntity.SampleNestedEntity sampleNestedEntityProbe = new SampleEntity.SampleNestedEntity();
sampleNestedEntityProbe.setNestedData("sampleNestedData");
SampleEntity probe = new SampleEntity();
probe.setSampleNestedEntity(sampleNestedEntityProbe);
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
Optional<SampleEntity> entityFromElasticSearch = repository.findOne(Example.of(probe));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("bye world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT))));
// then
assertThat(entityFromElasticSearch).contains(sampleEntity);
// then
assertThat(sampleEntities).isNotNull().hasSize(1);
}
}
@Test // #2418
void startingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void shouldFindAll() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("bye world");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("h");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.STARTING))));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
@Test // #2418
void endingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void shouldFindAllWithSort() {
// given
SampleEntity sampleEntityWithRate11 = new SampleEntity();
sampleEntityWithRate11.setDocumentId(nextIdAsString());
sampleEntityWithRate11.setMessage("hello world");
sampleEntityWithRate11.setRate(11);
sampleEntityWithRate11.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate13 = new SampleEntity();
sampleEntityWithRate13.setDocumentId(nextIdAsString());
sampleEntityWithRate13.setMessage("hello world");
sampleEntityWithRate13.setRate(13);
sampleEntityWithRate13.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate22 = new SampleEntity();
sampleEntityWithRate22.setDocumentId(nextIdAsString());
sampleEntityWithRate22.setMessage("hello world");
sampleEntityWithRate22.setRate(22);
sampleEntityWithRate22.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
repository.saveAll(List.of(sampleEntityWithRate11, sampleEntityWithRate13, sampleEntityWithRate22));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING))));
// when
SampleEntity probe = new SampleEntity();
final Iterable<SampleEntity> all = repository.findAll();
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe), Sort.by(Sort.Direction.DESC, "rate"));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
// then
assertThat(sampleEntities).isNotNull().hasSize(3).containsExactly(sampleEntityWithRate22, sampleEntityWithRate13,
sampleEntityWithRate11);
}
@Test // #2418
void regexStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void shouldFindAllWithPageable() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setRate(1);
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setRate(3);
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hello world");
sampleEntity3.setRate(2);
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("[(hello)(hola)].*");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.REGEX))));
// when
SampleEntity probe = new SampleEntity();
Iterable<SampleEntity> page1 = repository.findAll(Example.of(probe),
PageRequest.of(0, 2, Sort.Direction.DESC, "rate"));
Iterable<SampleEntity> page2 = repository.findAll(Example.of(probe),
PageRequest.of(1, 2, Sort.Direction.DESC, "rate"));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
// then
assertThat(page1).isNotNull().hasSize(2).containsExactly(sampleEntity2, sampleEntity3);
assertThat(page2).isNotNull().hasSize(1).containsExactly(sampleEntity);
}
@Test // #2418
void shouldCount() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
final long count = repository.count(Example.of(probe));
// then
assertThat(count).isPositive();
}
@Test // #2418
void shouldExists() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2));
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
boolean exists = repository.exists(Example.of(probe));
// then
assertThat(exists).isTrue();
}
@Test // #2418
void defaultStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT))));
// then
assertThat(sampleEntities).isNotNull().hasSize(1);
}
@Test // #2418
void exactStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("bye world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT))));
// then
assertThat(sampleEntities).isNotNull().hasSize(1);
}
@Test // #2418
void startingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("h");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.STARTING))));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
@Test // #2418
void endingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("world");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING))));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
@Test // #2418
void regexStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3));
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("[(hello)(hola)].*");
Iterable<SampleEntity> sampleEntities = repository.findAll(Example.of(probe, ExampleMatcher.matching()
.withMatcher("message", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.REGEX))));
// then
assertThat(sampleEntities).isNotNull().hasSize(2);
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
@@ -15,9 +15,16 @@
*/
package org.springframework.data.elasticsearch.repository.support.querybyexample;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
import reactor.test.StepVerifier;
import java.util.List;
import java.util.Objects;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -36,13 +43,6 @@ import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRe
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.lang.Nullable;
import reactor.test.StepVerifier;
import java.util.List;
import java.util.Objects;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.elasticsearch.utils.IdGenerator.nextIdAsString;
/**
* @author Ezequiel Antúnez Camacho
@@ -58,430 +58,418 @@ abstract class ReactiveQueryByExampleElasticsearchExecutorIntegrationTests {
@BeforeEach
void before() {
indexNameProvider.increment();
operations.indexOps(SampleEntity.class).createWithMapping()
operations.indexOps(SampleEntity.class).createWithMapping().as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
@Test // #2418
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
}
@Test
void shouldFindOne() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.consumeNextWith(entityFromElasticSearch -> assertThat(entityFromElasticSearch).isEqualTo(sampleEntity2)) //
.verifyComplete();
}
@Test
void shouldThrowExceptionIfMoreThanOneResultInFindOne() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectError(IncorrectResultSizeDataAccessException.class) //
.verify();
}
@Test
void shouldFindOneWithNestedField() {
// given
SampleEntity.SampleNestedEntity sampleNestedEntity = new SampleEntity.SampleNestedEntity();
sampleNestedEntity.setNestedData("sampleNestedData");
sampleNestedEntity.setAnotherNestedData("sampleAnotherNestedData");
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setSampleNestedEntity(sampleNestedEntity);
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity.SampleNestedEntity sampleNestedEntity2 = new SampleEntity.SampleNestedEntity();
sampleNestedEntity2.setNestedData("sampleNestedData2");
sampleNestedEntity2.setAnotherNestedData("sampleAnotherNestedData2");
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setSampleNestedEntity(sampleNestedEntity2);
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity.SampleNestedEntity sampleNestedEntityProbe = new SampleEntity.SampleNestedEntity();
sampleNestedEntityProbe.setNestedData("sampleNestedData");
SampleEntity probe = new SampleEntity();
probe.setSampleNestedEntity(sampleNestedEntityProbe);
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity) //
.verifyComplete();
}
@Test
void shouldFindAll() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("bye world");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
repository.findAll(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity2)) //
.verifyComplete();
}
@Test
void shouldFindAllWithSort() {
// given
SampleEntity sampleEntityWithRate11 = new SampleEntity();
sampleEntityWithRate11.setDocumentId(nextIdAsString());
sampleEntityWithRate11.setMessage("hello world");
sampleEntityWithRate11.setRate(11);
sampleEntityWithRate11.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate13 = new SampleEntity();
sampleEntityWithRate13.setDocumentId(nextIdAsString());
sampleEntityWithRate13.setMessage("hello world");
sampleEntityWithRate13.setRate(13);
sampleEntityWithRate13.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate22 = new SampleEntity();
sampleEntityWithRate22.setDocumentId(nextIdAsString());
sampleEntityWithRate22.setMessage("hello world");
sampleEntityWithRate22.setRate(22);
sampleEntityWithRate22.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntityWithRate11, sampleEntityWithRate13, sampleEntityWithRate22)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
repository.findAll(Example.of(probe), Sort.by(Sort.Direction.DESC, "rate"))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntityWithRate22, sampleEntityWithRate13, sampleEntityWithRate11)) //
.verifyComplete();
}
@Test
void shouldCount() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.count(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(1L) //
.verifyComplete();
}
@Test
void shouldExists() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.exists(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
@Test // #2418
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
void defaultStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT))))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity) //
.verifyComplete();
}
@Nested
@DisplayName("All QueryByExampleExecutor operations should work")
class ReactiveQueryByExampleExecutorOperations {
@Test
void shouldFindOne() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void exactStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.consumeNextWith(entityFromElasticSearch -> assertThat(entityFromElasticSearch).isEqualTo(sampleEntity2)) //
.verifyComplete();
}
@Test
void shouldThrowExceptionIfMoreThanOneResultInFindOne() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectError(IncorrectResultSizeDataAccessException.class) //
.verify();
}
@Test
void shouldFindOneWithNestedField() {
// given
SampleEntity.SampleNestedEntity sampleNestedEntity = new SampleEntity.SampleNestedEntity();
sampleNestedEntity.setNestedData("sampleNestedData");
sampleNestedEntity.setAnotherNestedData("sampleAnotherNestedData");
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("some message");
sampleEntity.setSampleNestedEntity(sampleNestedEntity);
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity.SampleNestedEntity sampleNestedEntity2 = new SampleEntity.SampleNestedEntity();
sampleNestedEntity2.setNestedData("sampleNestedData2");
sampleNestedEntity2.setAnotherNestedData("sampleAnotherNestedData2");
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("some message");
sampleEntity2.setSampleNestedEntity(sampleNestedEntity2);
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity.SampleNestedEntity sampleNestedEntityProbe = new SampleEntity.SampleNestedEntity();
sampleNestedEntityProbe.setNestedData("sampleNestedData");
SampleEntity probe = new SampleEntity();
probe.setSampleNestedEntity(sampleNestedEntityProbe);
repository.findOne(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity) //
.verifyComplete();
}
@Test
void shouldFindAll() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("hello world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("bye world");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
repository.findAll(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity2)) //
.verifyComplete();
}
@Test
void shouldFindAllWithSort() {
// given
SampleEntity sampleEntityWithRate11 = new SampleEntity();
sampleEntityWithRate11.setDocumentId(nextIdAsString());
sampleEntityWithRate11.setMessage("hello world");
sampleEntityWithRate11.setRate(11);
sampleEntityWithRate11.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate13 = new SampleEntity();
sampleEntityWithRate13.setDocumentId(nextIdAsString());
sampleEntityWithRate13.setMessage("hello world");
sampleEntityWithRate13.setRate(13);
sampleEntityWithRate13.setVersion(System.currentTimeMillis());
SampleEntity sampleEntityWithRate22 = new SampleEntity();
sampleEntityWithRate22.setDocumentId(nextIdAsString());
sampleEntityWithRate22.setMessage("hello world");
sampleEntityWithRate22.setRate(22);
sampleEntityWithRate22.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntityWithRate11, sampleEntityWithRate13, sampleEntityWithRate22)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
repository.findAll(Example.of(probe), Sort.by(Sort.Direction.DESC, "rate"))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntityWithRate22, sampleEntityWithRate13, sampleEntityWithRate11)) //
.verifyComplete();
}
@Test
void shouldCount() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.count(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(1L) //
.verifyComplete();
}
@Test
void shouldExists() {
// given
String documentId = nextIdAsString();
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(documentId);
sampleEntity.setMessage("some message");
sampleEntity.setVersion(System.currentTimeMillis());
String documentId2 = nextIdAsString();
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(documentId2);
sampleEntity2.setMessage("some message");
sampleEntity2.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2)) //
.as(StepVerifier::create) //
.expectNextCount(2L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setDocumentId(documentId2);
repository.exists(Example.of(probe))
// then
.as(StepVerifier::create) //
.expectNext(true) //
.verifyComplete();
}
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("bye world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT))))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity2) //
.verifyComplete();
}
@Nested
@DisplayName("All ExampleMatchers should work")
class AllExampleMatchersShouldWork {
@Test // #2418
void startingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void defaultStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("h");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.STARTING))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity3)) //
.verifyComplete();
}
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("hello world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT))))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity) //
.verifyComplete();
}
@Test // #2418
void endingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void exactStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity2)) //
.verifyComplete();
}
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("bye world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT))))
// then
.as(StepVerifier::create) //
.expectNext(sampleEntity2) //
.verifyComplete();
}
@Test // #2418
void regexStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
@Test // #2418
void startingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("h");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.STARTING))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity3)) //
.verifyComplete();
}
@Test // #2418
void endingStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("world");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity2)) //
.verifyComplete();
}
@Test // #2418
void regexStringMatcherShouldWork() {
// given
SampleEntity sampleEntity = new SampleEntity();
sampleEntity.setDocumentId(nextIdAsString());
sampleEntity.setMessage("hello world");
sampleEntity.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity2 = new SampleEntity();
sampleEntity2.setDocumentId(nextIdAsString());
sampleEntity2.setMessage("bye world");
sampleEntity2.setVersion(System.currentTimeMillis());
SampleEntity sampleEntity3 = new SampleEntity();
sampleEntity3.setDocumentId(nextIdAsString());
sampleEntity3.setMessage("hola mundo");
sampleEntity3.setVersion(System.currentTimeMillis());
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("[(hello)(hola)].*");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.REGEX))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity3)) //
.verifyComplete();
}
repository.saveAll(List.of(sampleEntity, sampleEntity2, sampleEntity3)) //
.as(StepVerifier::create) //
.expectNextCount(3L) //
.verifyComplete();
// when
SampleEntity probe = new SampleEntity();
probe.setMessage("[(hello)(hola)].*");
repository
.findAll(Example.of(probe,
ExampleMatcher.matching().withMatcher("message",
ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.REGEX))))
// then
.as(StepVerifier::create) //
.expectNextSequence(List.of(sampleEntity, sampleEntity3)) //
.verifyComplete();
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
@@ -1 +0,0 @@
f6bbf833798e7af0055b94865a46440e
@@ -1 +0,0 @@
59ddfc2b714be7918808eacecc5739b8d277e60e
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>999999</version>
</project>
@@ -1 +0,0 @@
63317ccd46b6663ff35cb142e05dce10
@@ -1 +0,0 @@
db353983c68ade94ae7e08acfacc0fc21ed8b64b
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<versioning>
<release>999999</release>
<versions>
<version>999999</version>
</versions>
<lastUpdated>20210321155422</lastUpdated>
</versioning>
</metadata>
@@ -1 +0,0 @@
998d3b8876980a3ef5a90adc982cc727
@@ -1 +0,0 @@
f4090a49c6eec680c075130b68bf8ee48aac4e94
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=8.6.1
sde.testcontainers.image-version=8.7.0
#
#
# needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13
@@ -1,10 +0,0 @@
#
# properties defining the image, these are not passed to the container on startup
#
sde.testcontainers.image-name=opensearchproject/opensearch
sde.testcontainers.image-version=1.0.0
#
#
# Opensearch as default has TLS and Basic auth enabled, we do not want this here, Testcontainers cannot ignore self signed certificates
#
plugins.security.disabled=true