mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-08-14 05:23:27 +00:00
Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c36b878cee | ||
|
2efa79d469 | ||
|
20fde914df | ||
|
988736dd41 | ||
|
346c5cce58 | ||
|
a3ebd8be78 | ||
|
3c6d96e49f | ||
|
be70a398be | ||
|
e3e666fd2e | ||
|
cb3d1e11d3 | ||
|
9e17bf3df8 | ||
|
f70a7d6414 | ||
|
13a4aa31f6 | ||
|
aa4aecacdf | ||
|
85ed8a4891 | ||
|
ae66cbd619 | ||
|
98d1f5bf63 | ||
|
d9f71027b6 | ||
|
2729fa95a3 | ||
|
1126c65766 | ||
|
db21ab06f9 | ||
|
1af298d95a | ||
|
980c6b350d | ||
|
7efd4b3be7 | ||
|
d2cc58ccad | ||
|
109dc05d9b | ||
|
edde0214a0 | ||
|
c8699d93d0 | ||
|
c0b26a51f1 | ||
|
3dbb1e73d6 | ||
|
e5efd31973 | ||
|
0637927ed4 | ||
|
16dacbb63c | ||
|
12acddb86d | ||
|
8cef50347e | ||
|
ea4d3f9f30 | ||
|
b9e2b13f21 | ||
|
5549216db0 | ||
|
a2cee9defd | ||
|
ea0ac3f7bc | ||
|
49cb56ed0c | ||
|
e8f9f9f1e3 | ||
|
f91c9c443b |
4
.mvn/wrapper/maven-wrapper.properties
vendored
4
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,3 +1,3 @@
|
||||
#Tue Feb 22 13:59:12 CET 2022
|
||||
#Fri Jun 03 09:39:36 CEST 2022
|
||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
|
||||
|
11
Jenkinsfile
vendored
11
Jenkinsfile
vendored
@ -9,7 +9,7 @@ pipeline {
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
|
||||
upstream(upstreamProjects: "spring-data-commons/2.7.x", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
@ -20,8 +20,9 @@ pipeline {
|
||||
stages {
|
||||
stage("test: baseline (main)") {
|
||||
when {
|
||||
beforeAgent(true)
|
||||
anyOf {
|
||||
branch 'main'
|
||||
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
@ -50,8 +51,9 @@ pipeline {
|
||||
|
||||
stage("Test other configurations") {
|
||||
when {
|
||||
beforeAgent(true)
|
||||
allOf {
|
||||
branch 'main'
|
||||
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
@ -108,8 +110,9 @@ pipeline {
|
||||
|
||||
stage('Release to artifactory') {
|
||||
when {
|
||||
beforeAgent(true)
|
||||
anyOf {
|
||||
branch 'main'
|
||||
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
|
25
README.adoc
25
README.adoc
@ -1,3 +1,4 @@
|
||||
|
||||
image:https://spring.io/badges/spring-data-elasticsearch/ga.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start] image:https://spring.io/badges/spring-data-elasticsearch/snapshot.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start]
|
||||
|
||||
= Spring Data for Elasticsearch image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]]
|
||||
@ -12,13 +13,35 @@ This project is lead and maintained by the community.
|
||||
== Features
|
||||
|
||||
* Spring configuration support using Java based `@Configuration` classes or an XML namespace for a ES clients instances.
|
||||
* `ElasticsearchRestTemplate` helper class that increases productivity performing common ES operations.
|
||||
* `ElasticsearchOperations` class and implementations that increases productivity performing common ES operations.
|
||||
Includes integrated object mapping between documents and POJOs.
|
||||
* Feature Rich Object Mapping integrated with Spring’s Conversion Service
|
||||
* Annotation based mapping metadata
|
||||
* Automatic implementation of `Repository` interfaces including support for custom search methods.
|
||||
* CDI support for repositories
|
||||
|
||||
== About Elasticsearch versions and clients
|
||||
|
||||
=== Elasticsearch 7.17 client libraries
|
||||
|
||||
At the end of 2021 Elasticsearch with version 7.17 released the new version of their Java client and deprecated the `RestHighLevelCLient` which was the default way to access Elasticsearch up to then.
|
||||
|
||||
Spring Data Elasticsearch will in version 4.4 offer the possibility to optionally use the new client as an alternative to the existing setup using the `RestHighLevelCLient`.
|
||||
The default client that is used still is the `RestHighLevelCLient`, first because the integration of the new client is not yet complete, the new client still has features missing and bugs which will hopefully be resolved soon.
|
||||
Second, and more important, the new Elasticsearch client forces users to switch from using `javax.json.spi.JsonProvider` to `jakarta.json.spi.JsonProvider`.
|
||||
Spring Data Elasticsearch cannot enforce this switch; Spring Boot will switch to `jakarta` with version 3 and then it's safe for Spring Data Elasticsearch to switch to the new client.
|
||||
|
||||
So for version 4.4 Spring Data Elasticsearch will keep using the `RestHighLevelCLient` in version 7.17.x (as long as this will be available).
|
||||
|
||||
=== Elasticsearch 8 client libraries
|
||||
|
||||
In Elasticsearch 8, the `RestHighLevelCLient` has been removed.
|
||||
This means that a switch to this client version can only be done with the next major upgrade which will be Spring Data Elasticsearch 5, based on Spring Data 3, used by Spring Boot 3, based on Spring 6 and Java 17.
|
||||
|
||||
=== Elasticsearch 8 cluster
|
||||
|
||||
It should be possible to use the Elasticsearch 7 client to access a cluster running version 8 by setting the appropriate aompatibility headers (see the documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration). but I encountered and heard of cases where the response from the server is not parseable by the client although the headers are set, so use with care.
|
||||
|
||||
== Code of Conduct
|
||||
|
||||
This project is governed by the https://github.com/spring-projects/.github/blob/e3cc2ff230d8f1dca06535aa6b5a4a23815861d4/CODE_OF_CONDUCT.md[Spring Code of Conduct].
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Java versions
|
||||
java.main.tag=8u322-b06-jdk
|
||||
java.next.tag=11.0.14.1_1-jdk
|
||||
java.lts.tag=17.0.2_8-jdk
|
||||
java.main.tag=8u332-b09-jdk
|
||||
java.next.tag=11.0.15_10-jdk
|
||||
java.lts.tag=17.0.3_7-jdk
|
||||
|
||||
# Docker container images - standard
|
||||
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
|
||||
|
22
pom.xml
22
pom.xml
@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.4.0-SNAPSHOT</version>
|
||||
<version>4.4.3</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.7.0-SNAPSHOT</version>
|
||||
<version>2.7.3</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@ -18,11 +18,13 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<elasticsearch>7.17.1</elasticsearch>
|
||||
<elasticsearch-java>7.17.1</elasticsearch-java>
|
||||
<!-- version of the RestHighLevelClient -->
|
||||
<elasticsearch-rhlc>7.17.6</elasticsearch-rhlc>
|
||||
<!-- version of the new ElasticsearchClient -->
|
||||
<elasticsearch-java>7.17.6</elasticsearch-java>
|
||||
<log4j>2.17.1</log4j>
|
||||
<netty>4.1.65.Final</netty>
|
||||
<springdata.commons>2.7.0-SNAPSHOT</springdata.commons>
|
||||
<springdata.commons>2.7.3</springdata.commons>
|
||||
<testcontainers>1.16.2</testcontainers>
|
||||
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
@ -139,7 +141,7 @@
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<version>${elasticsearch-rhlc}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
@ -163,7 +165,7 @@
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
|
||||
<version>${elasticsearch}</version>
|
||||
<version>${elasticsearch-java}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
@ -497,15 +499,15 @@
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
<id>spring-libs-release</id>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>local-maven-repo</id>
|
||||
<url>file:///${project.basedir}/src/test/resources/local-maven-repo</url>
|
||||
</repository>
|
||||
|
||||
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
|
@ -29,15 +29,18 @@ Requires an installation of https://www.elastic.co/products/elasticsearch[Elasti
|
||||
[[preface.versions]]
|
||||
=== Versions
|
||||
|
||||
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring Data release train:
|
||||
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of
|
||||
Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring
|
||||
Data release train. The Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
|
||||
built and tested.
|
||||
|
||||
[cols="^,^,^,^,^",options="header"]
|
||||
|===
|
||||
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
|
||||
| 2022.0 (Raj) | 4.4.x | 7.17.1 | 5.3.x | 2.7.x
|
||||
| 2021.2 (Raj) | 4.4.x | 7.17.6 | 5.3.x | 2.7.x
|
||||
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
|
||||
| 2021.0 (Pascal) | 4.2.x | 7.12.0 | 5.3.x | 2.5.x
|
||||
| 2020.0 (Ockham)footnote:oom[Out of maintenance] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
|
||||
| 2021.0 (Pascal) | 4.2.xfootnote:oom[Out of maintenance] | 7.12.0 | 5.3.x | 2.5.x
|
||||
| 2020.0 (Ockham)footnote:oom[] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
|
||||
| Neumannfootnote:oom[] | 4.0.xfootnote:oom[] | 7.6.2 | 5.2.12 |2.3.x
|
||||
| Moorefootnote:oom[] | 3.2.xfootnote:oom[] |6.8.12 | 5.2.12| 2.2.x
|
||||
| Lovelacefootnote:oom[] | 3.1.xfootnote:oom[] | 6.2.2 | 5.1.19 |2.1.x
|
||||
|
@ -28,6 +28,14 @@ Connections to Elasticsearch must be made using either the imperative `Elasticse
|
||||
In 4.3 two classes (`ElasticsearchAggregations` and `ElasticsearchAggregation`) had been moved to the `org.springframework.data.elasticsearch.core.clients.elasticsearch7` package in preparation for the integration of the new Elasticsearch client.
|
||||
The were moved back to the `org.springframework.data.elasticsearch.core` package as we keep the classes use the old Elasticsearch client where they were.
|
||||
|
||||
=== Behaviour change
|
||||
|
||||
The `ReactiveElasticsearchTemplate`, when created directly or by Spring Boot configuration had a default refresh policy of IMMEDIATE.
|
||||
This could cause performance issues on heavy indexing and was different than the default behaviour of Elasticsearch.
|
||||
This has been changed to that now the default refresh policy is NONE.
|
||||
When the
|
||||
`ReactiveElasticsearchTemplate` was provided by using the configuration like described in <<elasticsearch.clients.reactive>> the default refresh policy already was set to NONE.
|
||||
|
||||
[[elasticsearch-migration-guide-4.3-4.4.new-clients]]
|
||||
== New Elasticsearch client
|
||||
|
||||
@ -74,7 +82,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
|
||||
<dependency>
|
||||
<groupId>co.elastic.clients</groupId>
|
||||
<artifactId>elasticsearch-java</artifactId>
|
||||
<version>7.17.1</version>
|
||||
<version>7.17.6</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
@ -85,7 +93,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
|
||||
<version>7.17.1</version>
|
||||
<version>7.17.6</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
|
@ -5,7 +5,7 @@
|
||||
== New in Spring Data Elasticsearch 4.4
|
||||
|
||||
* Introduction of new imperative and reactive clients using the classes from the new Elasticsearch Java client
|
||||
* Upgrade to Elasticsearch 7.17.1.
|
||||
* Upgrade to Elasticsearch 7.17.6.
|
||||
|
||||
[[new-features.4-3-0]]
|
||||
== New in Spring Data Elasticsearch 4.3
|
||||
|
@ -25,6 +25,14 @@ public class NoSuchIndexException extends NonTransientDataAccessResourceExceptio
|
||||
|
||||
private final String index;
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public NoSuchIndexException(String index) {
|
||||
super(String.format("Index %s not found.", index));
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public NoSuchIndexException(String index, Throwable cause) {
|
||||
super(String.format("Index %s not found.", index), cause);
|
||||
this.index = index;
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
|
||||
/**
|
||||
* Class to combine an Elasticsearch {@link co.elastic.clients.elasticsearch._types.aggregations.Aggregate} with its
|
||||
* name. Necessary as the Elasticsearch Aggregate does not know i"s name.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public class Aggregation {
|
||||
|
||||
private final String name;
|
||||
private final Aggregate aggregate;
|
||||
|
||||
public Aggregation(String name, Aggregate aggregate) {
|
||||
this.name = name;
|
||||
this.aggregate = aggregate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Aggregate getAggregate() {
|
||||
return aggregate;
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.convert.GeoConverters;
|
||||
@ -67,9 +68,13 @@ class CriteriaFilterProcessor {
|
||||
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
|
||||
|
||||
if (chainedCriteria.isOr()) {
|
||||
// todo #1973
|
||||
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
|
||||
queriesForEntries(chainedCriteria).forEach(boolQueryBuilder::should);
|
||||
filterQueries.add(new Query(boolQueryBuilder.build()));
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
// todo #1973
|
||||
Collection<? extends Query> negatingFilters = buildNegatingFilter(criteria.getField().getName(),
|
||||
criteria.getFilterCriteriaEntries());
|
||||
filterQueries.addAll(negatingFilters);
|
||||
} else {
|
||||
filterQueries.addAll(queriesForEntries(chainedCriteria));
|
||||
}
|
||||
@ -85,11 +90,28 @@ class CriteriaFilterProcessor {
|
||||
BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool();
|
||||
filterQueries.forEach(boolQueryBuilder::must);
|
||||
BoolQuery boolQuery = boolQueryBuilder.build();
|
||||
return Optional.of(boolQuery._toQuery());
|
||||
return Optional.of(new Query(boolQuery));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<? extends Query> buildNegatingFilter(String fieldName,
|
||||
Set<Criteria.CriteriaEntry> filterCriteriaEntries) {
|
||||
|
||||
List<Query> negationFilters = new ArrayList<>();
|
||||
|
||||
filterCriteriaEntries.forEach(criteriaEntry -> {
|
||||
Optional<Query> query = queryFor(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName);
|
||||
|
||||
if (query.isPresent()) {
|
||||
BoolQuery negatingFilter = QueryBuilders.bool().mustNot(query.get()).build();
|
||||
negationFilters.add(new Query(negatingFilter));
|
||||
}
|
||||
});
|
||||
|
||||
return negationFilters;
|
||||
}
|
||||
|
||||
private static Collection<? extends Query> queriesForEntries(Criteria criteria) {
|
||||
|
||||
Assert.notNull(criteria.getField(), "criteria must have a field");
|
||||
|
@ -28,6 +28,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@ -77,18 +78,11 @@ final class DocumentAdapters {
|
||||
|
||||
NestedMetaData nestedMetaData = from(hit.nested());
|
||||
|
||||
// todo #1973 explanation
|
||||
Explanation explanation = from(hit.explanation());
|
||||
|
||||
// todo #1973 matchedQueries
|
||||
List<String> matchedQueries = null;
|
||||
// todo #1973 documentFields
|
||||
Map<String, List<Object>> documentFields = Collections.emptyMap();
|
||||
List<String> matchedQueries = hit.matchedQueries();
|
||||
|
||||
Document document;
|
||||
Object source = hit.source();
|
||||
if (source == null) {
|
||||
// Elasticsearch provides raw JsonData, so we build the fields into a JSON string
|
||||
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
final boolean[] firstField = { true };
|
||||
hit.fields().forEach((key, jsonData) -> {
|
||||
@ -100,7 +94,25 @@ final class DocumentAdapters {
|
||||
firstField[0] = false;
|
||||
});
|
||||
sb.append('}');
|
||||
document = Document.parse(sb.toString());
|
||||
return new EntityAsMap().fromJson(sb.toString());
|
||||
};
|
||||
|
||||
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
|
||||
|
||||
Map<String, List<Object>> documentFields = new LinkedHashMap<>();
|
||||
hitFieldsAsMap.entrySet().forEach(entry -> {
|
||||
if (entry.getValue() instanceof List) {
|
||||
// noinspection unchecked
|
||||
documentFields.put(entry.getKey(), (List<Object>) entry.getValue());
|
||||
} else {
|
||||
documentFields.put(entry.getKey(), Collections.singletonList(entry.getValue()));
|
||||
}
|
||||
});
|
||||
|
||||
Document document;
|
||||
Object source = hit.source();
|
||||
if (source == null) {
|
||||
document = Document.from(hitFieldsAsMap);
|
||||
} else {
|
||||
if (source instanceof EntityAsMap) {
|
||||
document = Document.from((EntityAsMap) source);
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.AggregationContainer;
|
||||
|
||||
/**
|
||||
* {@link AggregationContainer} for a {@link Aggregation} that holds Elasticsearch data.
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
|
||||
|
||||
private final Aggregation aggregation;
|
||||
|
||||
public ElasticsearchAggregation(Aggregation aggregation) {
|
||||
this.aggregation = aggregation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregation aggregation() {
|
||||
return aggregation;
|
||||
}
|
||||
}
|
@ -17,9 +17,12 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.AggregationsContainer;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AggregationsContainer implementation for the Elasticsearch aggregations.
|
||||
@ -27,16 +30,33 @@ import org.springframework.data.elasticsearch.core.AggregationsContainer;
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public class ElasticsearchAggregations implements AggregationsContainer<Map<String, Aggregate>> {
|
||||
public class ElasticsearchAggregations implements AggregationsContainer<List<ElasticsearchAggregation>> {
|
||||
|
||||
private final Map<String, Aggregate> aggregations;
|
||||
private final List<ElasticsearchAggregation> aggregations;
|
||||
|
||||
public ElasticsearchAggregations(List<ElasticsearchAggregation> aggregations) {
|
||||
|
||||
Assert.notNull(aggregations, "aggregations must not be null");
|
||||
|
||||
public ElasticsearchAggregations(Map<String, Aggregate> aggregations) {
|
||||
this.aggregations = aggregations;
|
||||
}
|
||||
|
||||
/**
|
||||
* convenience constructor taking a map as it is returned from the new Elasticsearch client.
|
||||
*
|
||||
* @param aggregationsMap aggregate map
|
||||
*/
|
||||
public ElasticsearchAggregations(Map<String, Aggregate> aggregationsMap) {
|
||||
|
||||
Assert.notNull(aggregationsMap, "aggregationsMap must not be null");
|
||||
|
||||
aggregations = new ArrayList<>(aggregationsMap.size());
|
||||
aggregationsMap
|
||||
.forEach((name, aggregate) -> aggregations.add(new ElasticsearchAggregation(new Aggregation(name, aggregate))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Aggregate> aggregations() {
|
||||
public List<ElasticsearchAggregation> aggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,18 @@ import co.elastic.clients.elasticsearch._types.ErrorResponse;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.elasticsearch.client.ResponseException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.elasticsearch.RestStatusException;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
|
||||
@ -67,14 +72,28 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
return new OptimisticLockingFailureException("Cannot index a document due to seq_no+primary_term conflict", ex);
|
||||
}
|
||||
|
||||
// todo #1973 index unavailable?
|
||||
|
||||
if (ex instanceof ElasticsearchException) {
|
||||
ElasticsearchException elasticsearchException = (ElasticsearchException) ex;
|
||||
|
||||
ErrorResponse response = elasticsearchException.response();
|
||||
|
||||
if (response.status() == HttpStatus.NOT_FOUND.value()
|
||||
&& "index_not_found_exception".equals(response.error().type())) {
|
||||
|
||||
Pattern pattern = Pattern.compile(".*no such index \\[(.*)\\]");
|
||||
String index = "";
|
||||
Matcher matcher = pattern.matcher(response.error().reason());
|
||||
if (matcher.matches()) {
|
||||
index = matcher.group(1);
|
||||
}
|
||||
return new NoSuchIndexException(index);
|
||||
}
|
||||
String body = JsonUtils.toJson(response, jsonpMapper);
|
||||
|
||||
if (response.error().type().contains("validation_exception")) {
|
||||
return new DataIntegrityViolationException(response.error().reason());
|
||||
}
|
||||
|
||||
return new UncategorizedElasticsearchException(ex.getMessage(), response.status(), body, ex);
|
||||
}
|
||||
|
||||
@ -86,20 +105,21 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isSeqNoConflict(Exception exception) {
|
||||
// todo #1973 check if this works
|
||||
private boolean isSeqNoConflict(Throwable exception) {
|
||||
Integer status = null;
|
||||
String message = null;
|
||||
|
||||
if (exception instanceof RestStatusException) {
|
||||
|
||||
RestStatusException statusException = (RestStatusException) exception;
|
||||
status = statusException.getStatus();
|
||||
message = statusException.getMessage();
|
||||
if (exception instanceof ResponseException) {
|
||||
ResponseException responseException = (ResponseException) exception;
|
||||
status = responseException.getResponse().getStatusLine().getStatusCode();
|
||||
message = responseException.getMessage();
|
||||
} else if (exception.getCause() != null) {
|
||||
return isSeqNoConflict(exception.getCause());
|
||||
}
|
||||
|
||||
if (status != null && message != null) {
|
||||
return status == 409 && message.contains("type=version_conflict_engine_exception")
|
||||
return status == 409 && message.contains("type\":\"version_conflict_engine_exception")
|
||||
&& message.contains("version conflict, required seqNo");
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
|
||||
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
|
||||
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.transport.Version;
|
||||
|
||||
@ -30,6 +34,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.elasticsearch.BulkFailureException;
|
||||
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
|
||||
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
|
||||
@ -40,6 +46,7 @@ import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.SearchScrollHits;
|
||||
import org.springframework.data.elasticsearch.core.cluster.ClusterOperations;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
@ -63,6 +70,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
|
||||
|
||||
private final ElasticsearchClient client;
|
||||
private final RequestConverter requestConverter;
|
||||
private final ResponseConverter responseConverter;
|
||||
@ -137,7 +146,12 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
@Override
|
||||
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(bulkOptions, "bulkOptions must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
doBulkOperation(queries, bulkOptions, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,12 +169,25 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
@Override
|
||||
public UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
|
||||
routingResolver.getRouting());
|
||||
co.elastic.clients.elasticsearch.core.UpdateResponse<Document> response = execute(
|
||||
client -> client.update(request, Document.class));
|
||||
return UpdateResponse.of(result(response.result()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse updateByQuery(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
Assert.notNull(updateQuery, "updateQuery must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
UpdateByQueryRequest request = requestConverter.documentUpdateByQueryRequest(updateQuery, index,
|
||||
getRefreshPolicy());
|
||||
|
||||
UpdateByQueryResponse byQueryResponse = execute(client -> client.updateByQuery(request));
|
||||
return responseConverter.byQueryResponse(byQueryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -226,7 +253,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
client -> client.reindex(reindexRequestES));
|
||||
|
||||
if (reindexResponse.task() == null) {
|
||||
// todo #1973 check behaviour and create issue in ES if necessary
|
||||
throw new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request");
|
||||
}
|
||||
|
||||
@ -331,8 +357,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return getSearchScrollHits(clazz, index, response);
|
||||
}
|
||||
|
||||
private <T, R extends SearchResponse<EntityAsMap>> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz,
|
||||
IndexCoordinates index, R response) {
|
||||
private <T> SearchScrollHits<T> getSearchScrollHits(Class<T> clazz, IndexCoordinates index,
|
||||
ResponseBody<EntityAsMap> response) {
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
@ -404,14 +430,53 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return doMultiSearch(multiSearchQueryParameters);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private List<SearchHits<?>> doMultiSearch(List<MultiSearchQueryParameter> multiSearchQueryParameters) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
MsearchRequest request = requestConverter.searchMsearchRequest(multiSearchQueryParameters);
|
||||
|
||||
MsearchResponse<EntityAsMap> msearchResponse = execute(client -> client.msearch(request, EntityAsMap.class));
|
||||
List<MultiSearchResponseItem<EntityAsMap>> responseItems = msearchResponse.responses();
|
||||
|
||||
Assert.isTrue(multiSearchQueryParameters.size() == responseItems.size(),
|
||||
"number of response items does not match number of requests");
|
||||
|
||||
List<SearchHits<?>> searchHitsList = new ArrayList<>(multiSearchQueryParameters.size());
|
||||
|
||||
Iterator<MultiSearchQueryParameter> queryIterator = multiSearchQueryParameters.iterator();
|
||||
Iterator<MultiSearchResponseItem<EntityAsMap>> responseIterator = responseItems.iterator();
|
||||
|
||||
while (queryIterator.hasNext()) {
|
||||
MultiSearchQueryParameter queryParameter = queryIterator.next();
|
||||
MultiSearchResponseItem<EntityAsMap> responseItem = responseIterator.next();
|
||||
|
||||
if (responseItem.isResult()) {
|
||||
|
||||
Class clazz = queryParameter.clazz;
|
||||
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, clazz,
|
||||
queryParameter.index);
|
||||
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(clazz,
|
||||
queryParameter.index);
|
||||
|
||||
SearchHits<?> searchHits = callback.doWith(
|
||||
SearchDocumentResponseBuilder.from(responseItem.result(), getEntityCreator(documentCallback), jsonpMapper));
|
||||
|
||||
searchHitsList.add(searchHits);
|
||||
} else {
|
||||
if (LOGGER.isWarnEnabled()) {
|
||||
LOGGER
|
||||
.warn(String.format("multisearch responsecontains failure: {}", responseItem.failure().error().reason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return searchHitsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* value class combining the information needed for a single query in a multisearch request.
|
||||
*/
|
||||
private static class MultiSearchQueryParameter {
|
||||
static class MultiSearchQueryParameter {
|
||||
final Query query;
|
||||
final Class<?> clazz;
|
||||
final IndexCoordinates index;
|
||||
|
@ -87,10 +87,6 @@ class HighlightQueryBuilder {
|
||||
builder.boundaryScannerLocale(parameters.getBoundaryScannerLocale());
|
||||
}
|
||||
|
||||
if (parameters.getForceSource()) { // default is false
|
||||
// todo #1973 parameter missing in new client
|
||||
}
|
||||
|
||||
if (StringUtils.hasLength(parameters.getFragmenter())) {
|
||||
builder.fragmenter(highlighterFragmenter(parameters.getFragmenter()));
|
||||
}
|
||||
@ -111,10 +107,6 @@ class HighlightQueryBuilder {
|
||||
builder.order(highlighterOrder(parameters.getOrder()));
|
||||
}
|
||||
|
||||
if (parameters.getPhraseLimit() > -1) {
|
||||
// todo #1973 parameter missing in new client
|
||||
}
|
||||
|
||||
if (parameters.getPreTags().length > 0) {
|
||||
builder.preTags(Arrays.asList(parameters.getPreTags()));
|
||||
}
|
||||
|
@ -17,14 +17,18 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||
import co.elastic.clients.elasticsearch.core.search.Suggester;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.data.elasticsearch.core.query.Query} implementation using query builders from the new
|
||||
@ -36,13 +40,23 @@ import org.springframework.util.Assert;
|
||||
public class NativeQuery extends BaseQuery {
|
||||
|
||||
@Nullable private final Query query;
|
||||
@Nullable private Query filter;
|
||||
// note: the new client does not have pipeline aggs, these are just set up as normal aggs
|
||||
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
|
||||
@Nullable private Suggester suggester;
|
||||
@Nullable private FieldCollapse fieldCollapse;
|
||||
private List<ScriptedField> scriptedFields = Collections.emptyList();
|
||||
private List<RescorerQuery> rescorerQueries = Collections.emptyList();
|
||||
|
||||
public NativeQuery(NativeQueryBuilder builder) {
|
||||
super(builder);
|
||||
this.query = builder.getQuery();
|
||||
this.filter = builder.getFilter();
|
||||
this.aggregations.putAll(builder.getAggregations());
|
||||
this.suggester = builder.getSuggester();
|
||||
this.fieldCollapse = builder.getFieldCollapse();
|
||||
this.scriptedFields = builder.getScriptedFields();
|
||||
this.rescorerQueries = builder.getRescorerQueries();
|
||||
}
|
||||
|
||||
public NativeQuery(@Nullable Query query) {
|
||||
@ -58,20 +72,9 @@ public class NativeQuery extends BaseQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void addAggregation(String name, Aggregation aggregation) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
Assert.notNull(aggregation, "aggregation must not be null");
|
||||
|
||||
aggregations.put(name, aggregation);
|
||||
}
|
||||
|
||||
public void setAggregations(Map<String, Aggregation> aggregations) {
|
||||
|
||||
Assert.notNull(aggregations, "aggregations must not be null");
|
||||
|
||||
this.aggregations.clear();
|
||||
this.aggregations.putAll(aggregations);
|
||||
@Nullable
|
||||
public Query getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public Map<String, Aggregation> getAggregations() {
|
||||
@ -83,8 +86,17 @@ public class NativeQuery extends BaseQuery {
|
||||
return suggester;
|
||||
}
|
||||
|
||||
public void setSuggester(@Nullable Suggester suggester) {
|
||||
this.suggester = suggester;
|
||||
@Nullable
|
||||
public FieldCollapse getFieldCollapse() {
|
||||
return fieldCollapse;
|
||||
}
|
||||
|
||||
public List<ScriptedField> getScriptedFields() {
|
||||
return scriptedFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RescorerQuery> getRescorerQueries() {
|
||||
return rescorerQueries;
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,19 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||
import co.elastic.clients.elasticsearch.core.search.Suggester;
|
||||
import co.elastic.clients.util.ObjectBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@ -35,17 +40,47 @@ import org.springframework.util.Assert;
|
||||
public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> {
|
||||
|
||||
@Nullable private Query query;
|
||||
@Nullable private Query filter;
|
||||
private final Map<String, Aggregation> aggregations = new LinkedHashMap<>();
|
||||
@Nullable private Suggester suggester;
|
||||
@Nullable private FieldCollapse fieldCollapse;
|
||||
private final List<ScriptedField> scriptedFields = new ArrayList<>();
|
||||
private List<RescorerQuery> rescorerQueries = new ArrayList<>();
|
||||
|
||||
public NativeQueryBuilder() {
|
||||
}
|
||||
public NativeQueryBuilder() {}
|
||||
|
||||
@Nullable
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Query getFilter() {
|
||||
return this.filter;
|
||||
}
|
||||
|
||||
public Map<String, Aggregation> getAggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Suggester getSuggester() {
|
||||
return suggester;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldCollapse getFieldCollapse() {
|
||||
return fieldCollapse;
|
||||
}
|
||||
|
||||
public List<ScriptedField> getScriptedFields() {
|
||||
return scriptedFields;
|
||||
}
|
||||
|
||||
public List<RescorerQuery> getRescorerQueries() {
|
||||
return rescorerQueries;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withQuery(Query query) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
@ -54,6 +89,11 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withFilter(@Nullable Query filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withQuery(Function<Query.Builder, ObjectBuilder<Query>> fn) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
@ -75,11 +115,28 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQuery build() {
|
||||
NativeQuery nativeQuery = new NativeQuery(this);
|
||||
nativeQuery.setAggregations(aggregations);
|
||||
nativeQuery.setSuggester(suggester);
|
||||
public NativeQueryBuilder withFieldCollapse(@Nullable FieldCollapse fieldCollapse) {
|
||||
this.fieldCollapse = fieldCollapse;
|
||||
return this;
|
||||
}
|
||||
|
||||
return nativeQuery;
|
||||
public NativeQueryBuilder withScriptedField(ScriptedField scriptedField) {
|
||||
|
||||
Assert.notNull(scriptedField, "scriptedField must not be null");
|
||||
|
||||
this.scriptedFields.add(scriptedField);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQueryBuilder withResorerQuery(RescorerQuery resorerQuery) {
|
||||
|
||||
Assert.notNull(resorerQuery, "resorerQuery must not be null");
|
||||
|
||||
this.rescorerQueries.add(resorerQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeQuery build() {
|
||||
return new NativeQuery(this);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.LatLonGeoLocation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.IdsQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
|
||||
@ -29,6 +30,7 @@ import co.elastic.clients.util.ObjectBuilder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
@ -45,6 +47,21 @@ public final class QueryBuilders {
|
||||
|
||||
private QueryBuilders() {}
|
||||
|
||||
public static IdsQuery idsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
return IdsQuery.of(i -> i.values(ids));
|
||||
}
|
||||
|
||||
public static Query idsQueryAsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
Function<Query.Builder, ObjectBuilder<Query>> builder = b -> b.ids(idsQuery(ids));
|
||||
|
||||
return builder.apply(new Query.Builder()).build();
|
||||
}
|
||||
public static MatchQuery matchQuery(String fieldName, String query, @Nullable Operator operator,
|
||||
@Nullable Float boost) {
|
||||
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
import co.elastic.clients.ApiClient;
|
||||
import co.elastic.clients.elasticsearch._types.ErrorResponse;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.JsonEndpoint;
|
||||
import co.elastic.clients.transport.TransportOptions;
|
||||
@ -127,12 +128,51 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
|
||||
}
|
||||
|
||||
public <T, P> Mono<UpdateResponse<T>> update(UpdateRequest<T, P> request, Class<T> clazz) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
|
||||
// noinspection unchecked
|
||||
JsonEndpoint<UpdateRequest<?, ?>, UpdateResponse<T>, ErrorResponse> endpoint = new EndpointWithResponseMapperAttr(
|
||||
UpdateRequest._ENDPOINT, "co.elastic.clients:Deserializer:_global.update.TDocument",
|
||||
this.getDeserializer(clazz));
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, this.transportOptions));
|
||||
}
|
||||
|
||||
public <T, P> Mono<UpdateResponse<T>> update(
|
||||
Function<UpdateRequest.Builder<T, P>, ObjectBuilder<UpdateRequest<T, P>>> fn, Class<T> clazz) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return update(fn.apply(new UpdateRequest.Builder<>()).build(), clazz);
|
||||
}
|
||||
|
||||
public <T> Mono<GetResponse<T>> get(Function<GetRequest.Builder, ObjectBuilder<GetRequest>> fn, Class<T> tClass) {
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return get(fn.apply(new GetRequest.Builder()).build(), tClass);
|
||||
}
|
||||
|
||||
public <T> Mono<MgetResponse<T>> mget(MgetRequest request, Class<T> clazz) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
|
||||
// noinspection unchecked
|
||||
JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse>) MgetRequest._ENDPOINT;
|
||||
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.mget.TDocument",
|
||||
this.getDeserializer(clazz));
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
|
||||
}
|
||||
|
||||
public <T> Mono<MgetResponse<T>> mget(Function<MgetRequest.Builder, ObjectBuilder<MgetRequest>> fn, Class<T> clazz) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return mget(fn.apply(new MgetRequest.Builder()).build(), clazz);
|
||||
}
|
||||
|
||||
public Mono<ReindexResponse> reindex(ReindexRequest request) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
@ -161,10 +201,25 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
return delete(fn.apply(new DeleteRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<DeleteByQueryResponse> deleteByQuery(DeleteByQueryRequest request) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, DeleteByQueryRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<DeleteByQueryResponse> deleteByQuery(
|
||||
Function<DeleteByQueryRequest.Builder, ObjectBuilder<DeleteByQueryRequest>> fn) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
||||
return deleteByQuery(fn.apply(new DeleteByQueryRequest.Builder()).build());
|
||||
}
|
||||
|
||||
// endregion
|
||||
// region search
|
||||
|
||||
public <T> Mono<SearchResponse<T>> search(SearchRequest request, Class<T> tDocumentClass) {
|
||||
public <T> Mono<ResponseBody<T>> search(SearchRequest request, Class<T> tDocumentClass) {
|
||||
|
||||
Assert.notNull(request, "request must not be null");
|
||||
Assert.notNull(tDocumentClass, "tDocumentClass must not be null");
|
||||
@ -173,7 +228,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
SearchRequest.createSearchEndpoint(this.getDeserializer(tDocumentClass)), transportOptions));
|
||||
}
|
||||
|
||||
public <T> Mono<SearchResponse<T>> search(Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> fn,
|
||||
public <T> Mono<ResponseBody<T>> search(Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> fn,
|
||||
Class<T> tDocumentClass) {
|
||||
|
||||
Assert.notNull(fn, "fn must not be null");
|
||||
|
@ -212,14 +212,6 @@ public class ReactiveElasticsearchIndicesClient
|
||||
return existsTemplate(fn.apply(new ExistsTemplateRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<BooleanResponse> existsType(ExistsTypeRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, ExistsTypeRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<BooleanResponse> existsType(Function<ExistsTypeRequest.Builder, ObjectBuilder<ExistsTypeRequest>> fn) {
|
||||
return existsType(fn.apply(new ExistsTypeRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<FlushResponse> flush(FlushRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, FlushRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
@ -232,19 +224,6 @@ public class ReactiveElasticsearchIndicesClient
|
||||
return flush(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<FlushSyncedResponse> flushSynced(FlushSyncedRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, FlushSyncedRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<FlushSyncedResponse> flushSynced(
|
||||
Function<FlushSyncedRequest.Builder, ObjectBuilder<FlushSyncedRequest>> fn) {
|
||||
return flushSynced(fn.apply(new FlushSyncedRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<FlushSyncedResponse> flushSynced() {
|
||||
return flushSynced(builder -> builder);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public Mono<ForcemergeResponse> forcemerge(ForcemergeRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, ForcemergeRequest._ENDPOINT, transportOptions));
|
||||
@ -260,14 +239,6 @@ public class ReactiveElasticsearchIndicesClient
|
||||
return forcemerge(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<FreezeResponse> freeze(FreezeRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, FreezeRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<FreezeResponse> freeze(Function<FreezeRequest.Builder, ObjectBuilder<FreezeRequest>> fn) {
|
||||
return freeze(fn.apply(new FreezeRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<GetIndexResponse> get(GetIndexRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, GetIndexRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
@ -363,18 +334,6 @@ public class ReactiveElasticsearchIndicesClient
|
||||
return getTemplate(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<GetUpgradeResponse> getUpgrade(GetUpgradeRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, GetUpgradeRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<GetUpgradeResponse> getUpgrade(Function<GetUpgradeRequest.Builder, ObjectBuilder<GetUpgradeRequest>> fn) {
|
||||
return getUpgrade(fn.apply(new GetUpgradeRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<GetUpgradeResponse> getUpgrade() {
|
||||
return getUpgrade(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<MigrateToDataStreamResponse> migrateToDataStream(MigrateToDataStreamRequest request) {
|
||||
return Mono
|
||||
.fromFuture(transport.performRequestAsync(request, MigrateToDataStreamRequest._ENDPOINT, transportOptions));
|
||||
@ -601,18 +560,6 @@ public class ReactiveElasticsearchIndicesClient
|
||||
return updateAliases(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<UpgradeResponse> upgrade(UpgradeRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, UpgradeRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
||||
public Mono<UpgradeResponse> upgrade(Function<UpgradeRequest.Builder, ObjectBuilder<UpgradeRequest>> fn) {
|
||||
return upgrade(fn.apply(new UpgradeRequest.Builder()).build());
|
||||
}
|
||||
|
||||
public Mono<UpgradeResponse> upgrade() {
|
||||
return upgrade(builder -> builder);
|
||||
}
|
||||
|
||||
public Mono<ValidateQueryResponse> validateQuery(ValidateQueryRequest request) {
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, ValidateQueryRequest._ENDPOINT, transportOptions));
|
||||
}
|
||||
|
@ -15,10 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Result;
|
||||
import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
|
||||
import co.elastic.clients.elasticsearch.core.get.GetResult;
|
||||
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.transport.Version;
|
||||
import reactor.core.publisher.Flux;
|
||||
@ -46,6 +50,7 @@ import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperatio
|
||||
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
|
||||
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
@ -103,6 +108,34 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
|
||||
|
||||
return entitiesPublisher //
|
||||
.flatMapMany(entities -> Flux.fromIterable(entities) //
|
||||
.concatMap(entity -> maybeCallBeforeConvert(entity, index)) //
|
||||
).collectList() //
|
||||
.map(Entities::new) //
|
||||
.flatMapMany(entities -> {
|
||||
|
||||
if (entities.isEmpty()) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
return doBulkOperation(entities.indexQueries(), BulkOptions.defaultOptions(), index)//
|
||||
.index() //
|
||||
.flatMap(indexAndResponse -> {
|
||||
T savedEntity = entities.entityAt(indexAndResponse.getT1());
|
||||
BulkResponseItem response = indexAndResponse.getT2();
|
||||
updateIndexedObject(savedEntity, IndexedObjectInformation.of(response.id(), response.seqNo(),
|
||||
response.primaryTerm(), response.version()));
|
||||
return maybeCallAfterSave(savedEntity, index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> get(String id, Class<T> entityType, IndexCoordinates index) {
|
||||
|
||||
@ -144,9 +177,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return Mono.from(execute( //
|
||||
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.ReindexResponse>>) client -> client
|
||||
.reindex(reindexRequestES)))
|
||||
.flatMap(response -> (response.task() == null)
|
||||
? Mono.error( // todo #1973 check behaviour and create issue in ES if necessary
|
||||
new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request"))
|
||||
.flatMap(response -> (response.task() == null) ? Mono.error(
|
||||
new UnsupportedBackendOperation("ElasticsearchClient did not return a task id on submit request"))
|
||||
: Mono.just(response.task()));
|
||||
}
|
||||
|
||||
@ -170,7 +202,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
}
|
||||
|
||||
private <R> Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
|
||||
private Mono<BulkResponse> checkForBulkOperationFailure(BulkResponse bulkResponse) {
|
||||
|
||||
if (bulkResponse.errors()) {
|
||||
Map<String, String> failedDocuments = new HashMap<>();
|
||||
@ -214,6 +246,31 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
}).onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
|
||||
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
|
||||
|
||||
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
|
||||
|
||||
Publisher<MgetResponse<EntityAsMap>> response = execute(
|
||||
(ClientCallback<Publisher<MgetResponse<EntityAsMap>>>) client -> client.mget(request, EntityAsMap.class));
|
||||
|
||||
return Mono.from(response)//
|
||||
.flatMapMany(it -> Flux.fromIterable(DocumentAdapters.from(it))) //
|
||||
.flatMap(multiGetItem -> {
|
||||
if (multiGetItem.isFailed()) {
|
||||
return Mono.just(MultiGetItem.of(null, multiGetItem.getFailure()));
|
||||
} else {
|
||||
return callback.toEntity(multiGetItem.getItem()) //
|
||||
.map(t -> MultiGetItem.of(t, multiGetItem.getFailure()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
@ -223,8 +280,30 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
@Override
|
||||
protected Mono<Boolean> doExists(String id, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index, true);
|
||||
|
||||
return Mono.from(execute(
|
||||
((ClientCallback<Publisher<GetResponse<EntityAsMap>>>) client -> client.get(getRequest, EntityAsMap.class))))
|
||||
.map(GetResult::found) //
|
||||
.onErrorReturn(NoSuchIndexException.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, entityType, index,
|
||||
getRefreshPolicy());
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<DeleteByQueryResponse>>) client -> client.deleteByQuery(request)))
|
||||
.map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
// region search operations
|
||||
|
||||
@Override
|
||||
@ -247,22 +326,27 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
Time scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll() : Time.of(t -> t.time("1m"));
|
||||
|
||||
Flux<SearchResponse<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
|
||||
state -> Mono
|
||||
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client
|
||||
.search(searchRequest, EntityAsMap.class))) //
|
||||
.expand(entityAsMapSearchResponse -> {
|
||||
Flux<ResponseBody<EntityAsMap>> searchResponses = Flux.usingWhen(Mono.fromSupplier(ScrollState::new), //
|
||||
state -> {
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client1 -> client1
|
||||
.search(searchRequest, EntityAsMap.class))) //
|
||||
.expand(entityAsMapSearchResponse -> {
|
||||
|
||||
state.updateScrollId(entityAsMapSearchResponse.scrollId());
|
||||
state.updateScrollId(entityAsMapSearchResponse.scrollId());
|
||||
|
||||
if (entityAsMapSearchResponse.hits() == null
|
||||
|| CollectionUtils.isEmpty(entityAsMapSearchResponse.hits().hits())) {
|
||||
return Mono.empty();
|
||||
}
|
||||
if (entityAsMapSearchResponse.hits() == null
|
||||
|| CollectionUtils.isEmpty(entityAsMapSearchResponse.hits().hits())) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return Mono.from(execute((ClientCallback<Publisher<ScrollResponse<EntityAsMap>>>) client -> client.scroll(
|
||||
ScrollRequest.of(sr -> sr.scrollId(state.getScrollId()).scroll(scrollTimeout)), EntityAsMap.class)));
|
||||
}),
|
||||
return Mono.from(execute((ClientCallback<Publisher<ScrollResponse<EntityAsMap>>>) client1 -> {
|
||||
ScrollRequest scrollRequest = ScrollRequest
|
||||
.of(sr -> sr.scrollId(state.getScrollId()).scroll(scrollTimeout));
|
||||
return client1.scroll(scrollRequest, EntityAsMap.class);
|
||||
}));
|
||||
});
|
||||
},
|
||||
this::cleanupScroll, (state, ex) -> cleanupScroll(state), this::cleanupScroll);
|
||||
|
||||
return searchResponses.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits())
|
||||
@ -284,7 +368,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, entityType, index, true, false);
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
EntityAsMap.class)))
|
||||
.map(searchResponse -> searchResponse.hits().total() != null ? searchResponse.hits().total().value() : 0L);
|
||||
}
|
||||
@ -292,7 +376,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
private Flux<SearchDocument> doFind(SearchRequest searchRequest) {
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
EntityAsMap.class))) //
|
||||
.flatMapIterable(entityAsMapSearchResponse -> entityAsMapSearchResponse.hits().hits()) //
|
||||
.map(entityAsMapHit -> DocumentAdapters.from(entityAsMapHit, jsonpMapper));
|
||||
@ -307,16 +391,25 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
SearchRequest searchRequest = requestConverter.searchRequest(query, clazz, index, false, false);
|
||||
|
||||
// noinspection unchecked
|
||||
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<T>((Class<T>) clazz, index);
|
||||
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>((Class<T>) clazz, index);
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> callback.toEntity(searchDocument)
|
||||
.toFuture();
|
||||
|
||||
return Mono
|
||||
.from(execute((ClientCallback<Publisher<SearchResponse<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
.from(execute((ClientCallback<Publisher<ResponseBody<EntityAsMap>>>) client -> client.search(searchRequest,
|
||||
EntityAsMap.class)))
|
||||
.map(searchResponse -> SearchDocumentResponseBuilder.from(searchResponse, entityCreator, jsonpMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<? extends AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
return doFindForResponse(query, entityType, index).flatMapMany(searchDocumentResponse -> {
|
||||
ElasticsearchAggregations aggregations = (ElasticsearchAggregations) searchDocumentResponse.getAggregations();
|
||||
return aggregations == null ? Flux.empty() : Flux.fromIterable(aggregations.aggregations());
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
@ -326,7 +419,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
@Override
|
||||
protected Mono<String> getRuntimeLibraryVersion() {
|
||||
return Mono.just(Version.VERSION.toString());
|
||||
return Mono.just(Version.VERSION != null ? Version.VERSION.toString() : "null");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -334,47 +427,22 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return Mono.from(execute(ReactiveElasticsearchClient::info)).map(infoResponse -> infoResponse.version().number());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> saveAll(Mono<? extends Collection<? extends T>> entitiesPublisher, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
|
||||
|
||||
return entitiesPublisher //
|
||||
.flatMapMany(entities -> Flux.fromIterable(entities) //
|
||||
.concatMap(entity -> maybeCallBeforeConvert(entity, index)) //
|
||||
).collectList() //
|
||||
.map(Entities::new) //
|
||||
.flatMapMany(entities -> {
|
||||
|
||||
if (entities.isEmpty()) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
return doBulkOperation(entities.indexQueries(), BulkOptions.defaultOptions(), index)//
|
||||
.index() //
|
||||
.flatMap(indexAndResponse -> {
|
||||
T savedEntity = entities.entityAt(indexAndResponse.getT1());
|
||||
BulkResponseItem response = indexAndResponse.getT2();
|
||||
updateIndexedObject(savedEntity, IndexedObjectInformation.of(response.id(), response.seqNo(),
|
||||
response.primaryTerm(), response.version()));
|
||||
return maybeCallAfterSave(savedEntity, index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<UpdateResponse> update(UpdateQuery updateQuery, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
Assert.notNull(updateQuery, "UpdateQuery must not be null");
|
||||
Assert.notNull(index, "Index must not be null");
|
||||
|
||||
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
|
||||
routingResolver.getRouting());
|
||||
|
||||
return Mono.from(execute(
|
||||
(ClientCallback<Publisher<co.elastic.clients.elasticsearch.core.UpdateResponse<Document>>>) client -> client
|
||||
.update(request, Document.class)))
|
||||
.flatMap(response -> {
|
||||
UpdateResponse.Result result = result(response.result());
|
||||
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -413,11 +481,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
return new ReactiveClusterTemplate(client.cluster(), converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
@ -435,7 +498,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
|
||||
@Override
|
||||
public Query idsQuery(List<String> ids) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
return NativeQuery.builder().withQuery(QueryBuilders.idsQueryAsQuery(ids)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,7 +297,7 @@ public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElast
|
||||
|
||||
co.elastic.clients.elasticsearch.indices.ExistsTemplateRequest existsTemplateRequestES = requestConverter
|
||||
.indicesExistsTemplateRequest(existsTemplateRequest);
|
||||
return Mono.from(execute(client1 -> client1.existsTemplate(existsTemplateRequestES))).map(BooleanResponse::value);
|
||||
return Mono.from(execute(client -> client.existsTemplate(existsTemplateRequestES))).map(BooleanResponse::value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -307,13 +307,18 @@ public class ReactiveIndicesTemplate extends ReactiveChildTemplate<ReactiveElast
|
||||
|
||||
co.elastic.clients.elasticsearch.indices.DeleteTemplateRequest deleteTemplateRequestES = requestConverter
|
||||
.indicesDeleteTemplateRequest(deleteTemplateRequest);
|
||||
return Mono.from(execute(client1 -> client1.deleteTemplate(deleteTemplateRequestES)))
|
||||
return Mono.from(execute(client -> client.deleteTemplate(deleteTemplateRequestES)))
|
||||
.map(DeleteTemplateResponse::acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IndexInformation> getInformation(IndexCoordinates index) {
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
|
||||
GetIndexRequest request = requestConverter.indicesGetIndexRequest(index);
|
||||
|
||||
return Mono.from(execute(client -> client.get(request))) //
|
||||
.map(responseConverter::indicesGetIndexInformations) //
|
||||
.flatMapMany(Flux::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,14 +16,15 @@
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
import static org.springframework.util.CollectionUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Conflicts;
|
||||
import co.elastic.clients.elasticsearch._types.InlineScript;
|
||||
import co.elastic.clients.elasticsearch._types.OpType;
|
||||
import co.elastic.clients.elasticsearch._types.SortOptions;
|
||||
import co.elastic.clients.elasticsearch._types.SortOrder;
|
||||
import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch._types.VersionType;
|
||||
import co.elastic.clients.elasticsearch._types.WaitForActiveShardOptions;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.Property;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
|
||||
@ -37,12 +38,17 @@ import co.elastic.clients.elasticsearch.core.DeleteRequest;
|
||||
import co.elastic.clients.elasticsearch.core.GetRequest;
|
||||
import co.elastic.clients.elasticsearch.core.IndexRequest;
|
||||
import co.elastic.clients.elasticsearch.core.MgetRequest;
|
||||
import co.elastic.clients.elasticsearch.core.MsearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateByQueryRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateRequest;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation;
|
||||
import co.elastic.clients.elasticsearch.core.mget.MultiGetOperation;
|
||||
import co.elastic.clients.elasticsearch.core.search.Highlight;
|
||||
import co.elastic.clients.elasticsearch.core.search.Rescore;
|
||||
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
|
||||
import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
|
||||
@ -58,7 +64,9 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@ -67,6 +75,7 @@ import java.util.stream.Collectors;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.RefreshPolicy;
|
||||
import org.springframework.data.elasticsearch.core.ScriptType;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasAction;
|
||||
@ -192,7 +201,7 @@ class RequestConverter {
|
||||
|
||||
if (filterQuery != null) {
|
||||
elasticsearchConverter.updateQuery(filterQuery, parameters.getFilterQueryClass());
|
||||
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
|
||||
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
|
||||
if (esQuery != null) {
|
||||
addActionBuilder.filter(esQuery);
|
||||
|
||||
@ -239,7 +248,8 @@ class RequestConverter {
|
||||
PutMappingRequest.Builder builder = new PutMappingRequest.Builder();
|
||||
builder.index(Arrays.asList(indexCoordinates.getIndexNames()));
|
||||
addPropertiesToMapping(builder, mapping);
|
||||
// TODO #1973 what else to add
|
||||
|
||||
// TODO #2155 what else to add
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@ -374,7 +384,7 @@ class RequestConverter {
|
||||
Query filterQuery = parameters.getFilterQuery();
|
||||
|
||||
if (filterQuery != null) {
|
||||
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getFilter(filterQuery);
|
||||
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = getQuery(filterQuery, null);
|
||||
|
||||
if (esQuery != null) {
|
||||
aliasBuilder.filter(esQuery);
|
||||
@ -475,7 +485,7 @@ class RequestConverter {
|
||||
}
|
||||
}
|
||||
|
||||
builder.refresh(refresh(refreshPolicy));
|
||||
builder.refresh(TypeUtils.refresh(refreshPolicy));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@ -562,6 +572,79 @@ class RequestConverter {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private UpdateOperation<?, ?> bulkUpdateOperation(UpdateQuery query, IndexCoordinates index,
|
||||
@Nullable RefreshPolicy refreshPolicy) {
|
||||
|
||||
UpdateOperation.Builder<Object, Object> uob = new UpdateOperation.Builder<>();
|
||||
String indexName = query.getIndexName() != null ? query.getIndexName() : index.getIndexName();
|
||||
|
||||
uob.index(indexName).id(query.getId());
|
||||
uob.action(a -> {
|
||||
a //
|
||||
.script(getScript(query.getScriptData())) //
|
||||
.doc(query.getDocument()) //
|
||||
.upsert(query.getUpsert()) //
|
||||
.scriptedUpsert(query.getScriptedUpsert()) //
|
||||
.docAsUpsert(query.getDocAsUpsert()) //
|
||||
;
|
||||
|
||||
if (query.getFetchSource() != null) {
|
||||
a.source(sc -> sc.fetch(query.getFetchSource()));
|
||||
}
|
||||
|
||||
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
|
||||
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
|
||||
: Collections.emptyList();
|
||||
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
|
||||
: Collections.emptyList();
|
||||
a.source(sc -> sc.filter(sf -> sf.includes(includes).excludes(excludes)));
|
||||
}
|
||||
|
||||
return a;
|
||||
});
|
||||
|
||||
uob //
|
||||
.routing(query.getRouting()) //
|
||||
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
|
||||
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
|
||||
.retryOnConflict(query.getRetryOnConflict()) //
|
||||
;
|
||||
|
||||
// no refresh, timeout, waitForActiveShards on UpdateOperation or UpdateAction
|
||||
|
||||
return uob.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private co.elastic.clients.elasticsearch._types.Script getScript(@Nullable ScriptData scriptData) {
|
||||
|
||||
if (scriptData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, JsonData> params = new HashMap<>();
|
||||
|
||||
if (scriptData.getParams() != null) {
|
||||
scriptData.getParams().forEach((key, value) -> {
|
||||
params.put(key, JsonData.of(value, jsonpMapper));
|
||||
});
|
||||
}
|
||||
return co.elastic.clients.elasticsearch._types.Script.of(sb -> {
|
||||
if (scriptData.getType() == ScriptType.INLINE) {
|
||||
sb.inline(is -> is //
|
||||
.lang(scriptData.getLanguage()) //
|
||||
.source(scriptData.getScript()) //
|
||||
.params(params)); //
|
||||
} else if (scriptData.getType() == ScriptType.STORED) {
|
||||
sb.stored(ss -> ss //
|
||||
.id(scriptData.getScript()) //
|
||||
.params(params) //
|
||||
);
|
||||
}
|
||||
return sb;
|
||||
});
|
||||
}
|
||||
|
||||
public BulkRequest documentBulkRequest(List<?> queries, BulkOptions bulkOptions, IndexCoordinates indexCoordinates,
|
||||
@Nullable RefreshPolicy refreshPolicy) {
|
||||
|
||||
@ -571,9 +654,9 @@ class RequestConverter {
|
||||
builder.timeout(tb -> tb.time(Long.valueOf(bulkOptions.getTimeout().toMillis()).toString() + "ms"));
|
||||
}
|
||||
|
||||
builder.refresh(refresh(refreshPolicy));
|
||||
builder.refresh(TypeUtils.refresh(refreshPolicy));
|
||||
if (bulkOptions.getRefreshPolicy() != null) {
|
||||
builder.refresh(refresh(bulkOptions.getRefreshPolicy()));
|
||||
builder.refresh(TypeUtils.refresh(bulkOptions.getRefreshPolicy()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
@ -599,7 +682,8 @@ class RequestConverter {
|
||||
ob.index(bulkIndexOperation(indexQuery, indexCoordinates, refreshPolicy));
|
||||
}
|
||||
} else if (query instanceof UpdateQuery) {
|
||||
// todo #1973
|
||||
UpdateQuery updateQuery = (UpdateQuery) query;
|
||||
ob.update(bulkUpdateOperation(updateQuery, indexCoordinates, refreshPolicy));
|
||||
}
|
||||
return ob.build();
|
||||
}).collect(Collectors.toList());
|
||||
@ -674,7 +758,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (source.getQuery() != null) {
|
||||
s.query(getQuery(source.getQuery()));
|
||||
s.query(getQuery(source.getQuery(), null));
|
||||
}
|
||||
|
||||
if (source.getRemote() != null) {
|
||||
@ -717,13 +801,13 @@ class RequestConverter {
|
||||
ReindexRequest.Dest dest = reindexRequest.getDest();
|
||||
return d //
|
||||
.index(dest.getIndex().getIndexName()) //
|
||||
.versionType(versionType(dest.getVersionType())) //
|
||||
.opType(opType(dest.getOpType()));
|
||||
.versionType(TypeUtils.versionType(dest.getVersionType())) //
|
||||
.opType(TypeUtils.opType(dest.getOpType()));
|
||||
} //
|
||||
);
|
||||
|
||||
if (reindexRequest.getConflicts() != null) {
|
||||
builder.conflicts(conflicts(reindexRequest.getConflicts()));
|
||||
builder.conflicts(TypeUtils.conflicts(reindexRequest.getConflicts()));
|
||||
}
|
||||
|
||||
ReindexRequest.Script script = reindexRequest.getScript();
|
||||
@ -731,17 +815,12 @@ class RequestConverter {
|
||||
builder.script(s -> s.inline(InlineScript.of(i -> i.lang(script.getLang()).source(script.getSource()))));
|
||||
}
|
||||
|
||||
if (reindexRequest.getTimeout() != null) {
|
||||
builder.timeout(tv -> tv.time(reindexRequest.getTimeout().toMillis() + "ms"));
|
||||
}
|
||||
|
||||
if (reindexRequest.getScroll() != null) {
|
||||
builder.scroll(tv -> tv.time(reindexRequest.getScroll().toMillis() + "ms"));
|
||||
}
|
||||
builder.timeout(time(reindexRequest.getTimeout())) //
|
||||
.scroll(time(reindexRequest.getScroll()));
|
||||
|
||||
if (reindexRequest.getWaitForActiveShards() != null) {
|
||||
builder.waitForActiveShards(wfas -> wfas //
|
||||
.count(waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
|
||||
.count(TypeUtils.waitForActiveShardsCount(reindexRequest.getWaitForActiveShards())));
|
||||
}
|
||||
|
||||
builder //
|
||||
@ -749,7 +828,11 @@ class RequestConverter {
|
||||
.refresh(reindexRequest.getRefresh()) //
|
||||
.requireAlias(reindexRequest.getRequireAlias()) //
|
||||
.requestsPerSecond(reindexRequest.getRequestsPerSecond()) //
|
||||
.slices(reindexRequest.getSlices());
|
||||
;
|
||||
|
||||
if (reindexRequest.getSlices() != null) {
|
||||
builder.slices(sb -> sb.value(reindexRequest.getSlices().intValue()));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@ -766,7 +849,7 @@ class RequestConverter {
|
||||
if (routing != null) {
|
||||
r.routing(routing);
|
||||
}
|
||||
r.refresh(refresh(refreshPolicy));
|
||||
r.refresh(TypeUtils.refresh(refreshPolicy));
|
||||
return r;
|
||||
});
|
||||
}
|
||||
@ -779,7 +862,7 @@ class RequestConverter {
|
||||
|
||||
return DeleteByQueryRequest.of(b -> {
|
||||
b.index(Arrays.asList(index.getIndexNames())) //
|
||||
.query(getQuery(query))//
|
||||
.query(getQuery(query, clazz))//
|
||||
.refresh(deleteByQueryRefresh(refreshPolicy));
|
||||
|
||||
if (query.isLimiting()) {
|
||||
@ -787,10 +870,7 @@ class RequestConverter {
|
||||
b.maxDocs(Long.valueOf(query.getMaxResults()));
|
||||
}
|
||||
|
||||
if (query.hasScrollTime()) {
|
||||
// noinspection ConstantConditions
|
||||
b.scroll(Time.of(t -> t.time(query.getScrollTime().toMillis() + "ms")));
|
||||
}
|
||||
b.scroll(time(query.getScrollTime()));
|
||||
|
||||
if (query.getRoute() != null) {
|
||||
b.routing(query.getRoute());
|
||||
@ -800,6 +880,143 @@ class RequestConverter {
|
||||
});
|
||||
}
|
||||
|
||||
public UpdateRequest<Document, ?> documentUpdateRequest(UpdateQuery query, IndexCoordinates index,
|
||||
@Nullable RefreshPolicy refreshPolicy, @Nullable String routing) {
|
||||
|
||||
String indexName = query.getIndexName() != null ? query.getIndexName() : index.getIndexName();
|
||||
return UpdateRequest.of(uqb -> {
|
||||
uqb.index(indexName).id(query.getId());
|
||||
|
||||
if (query.getScript() != null) {
|
||||
Map<String, JsonData> params = new HashMap<>();
|
||||
|
||||
if (query.getParams() != null) {
|
||||
query.getParams().forEach((key, value) -> {
|
||||
params.put(key, JsonData.of(value, jsonpMapper));
|
||||
});
|
||||
}
|
||||
|
||||
uqb.script(sb -> {
|
||||
if (query.getScriptType() == ScriptType.INLINE) {
|
||||
sb.inline(is -> is //
|
||||
.lang(query.getLang()) //
|
||||
.source(query.getScript()) //
|
||||
.params(params)); //
|
||||
} else if (query.getScriptType() == ScriptType.STORED) {
|
||||
sb.stored(ss -> ss //
|
||||
.id(query.getScript()) //
|
||||
.params(params) //
|
||||
);
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
uqb //
|
||||
.doc(query.getDocument()) //
|
||||
.upsert(query.getUpsert()) //
|
||||
.routing(query.getRouting() != null ? query.getRouting() : routing) //
|
||||
.scriptedUpsert(query.getScriptedUpsert()) //
|
||||
.docAsUpsert(query.getDocAsUpsert()) //
|
||||
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
|
||||
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
|
||||
.refresh(TypeUtils.refresh(refreshPolicy)) //
|
||||
.retryOnConflict(query.getRetryOnConflict()) //
|
||||
;
|
||||
|
||||
if (query.getFetchSource() != null) {
|
||||
uqb.source(sc -> sc.fetch(query.getFetchSource()));
|
||||
}
|
||||
|
||||
if (query.getFetchSourceIncludes() != null || query.getFetchSourceExcludes() != null) {
|
||||
List<String> includes = query.getFetchSourceIncludes() != null ? query.getFetchSourceIncludes()
|
||||
: Collections.emptyList();
|
||||
List<String> excludes = query.getFetchSourceExcludes() != null ? query.getFetchSourceExcludes()
|
||||
: Collections.emptyList();
|
||||
uqb.source(sc -> sc.filter(sf -> sf.includes(includes).excludes(excludes)));
|
||||
}
|
||||
|
||||
if (query.getTimeout() != null) {
|
||||
uqb.timeout(tv -> tv.time(query.getTimeout()));
|
||||
}
|
||||
|
||||
String waitForActiveShards = query.getWaitForActiveShards();
|
||||
if (waitForActiveShards != null) {
|
||||
if ("all".equalsIgnoreCase(waitForActiveShards)) {
|
||||
uqb.waitForActiveShards(wfa -> wfa.option(WaitForActiveShardOptions.All));
|
||||
} else {
|
||||
int val;
|
||||
try {
|
||||
val = Integer.parseInt(waitForActiveShards);
|
||||
} catch (NumberFormatException var3) {
|
||||
throw new IllegalArgumentException("cannot parse ActiveShardCount[" + waitForActiveShards + "]", var3);
|
||||
}
|
||||
uqb.waitForActiveShards(wfa -> wfa.count(val));
|
||||
}
|
||||
}
|
||||
|
||||
return uqb;
|
||||
} //
|
||||
);
|
||||
}
|
||||
|
||||
public UpdateByQueryRequest documentUpdateByQueryRequest(UpdateQuery updateQuery, IndexCoordinates index,
|
||||
@Nullable RefreshPolicy refreshPolicy) {
|
||||
|
||||
return UpdateByQueryRequest.of(ub -> {
|
||||
ub //
|
||||
.index(Arrays.asList(index.getIndexNames())) //
|
||||
.refresh(refreshPolicy == RefreshPolicy.IMMEDIATE) //
|
||||
.routing(updateQuery.getRouting()) //
|
||||
.script(getScript(updateQuery.getScriptData())) //
|
||||
.maxDocs(updateQuery.getMaxDocs() != null ? Long.valueOf(updateQuery.getMaxDocs()) : null) //
|
||||
.pipeline(updateQuery.getPipeline()) //
|
||||
.requestsPerSecond(
|
||||
updateQuery.getRequestsPerSecond() != null ? updateQuery.getRequestsPerSecond().longValue() : null) //
|
||||
;
|
||||
|
||||
if (updateQuery.getSlices() != null) {
|
||||
ub.slices(sb -> sb.value(updateQuery.getSlices() != null ? updateQuery.getSlices() : null));
|
||||
}
|
||||
|
||||
if (updateQuery.getAbortOnVersionConflict() != null) {
|
||||
ub.conflicts(updateQuery.getAbortOnVersionConflict() ? Conflicts.Abort : Conflicts.Proceed);
|
||||
}
|
||||
|
||||
if (updateQuery.getBatchSize() != null) {
|
||||
ub.size(Long.valueOf(updateQuery.getBatchSize()));
|
||||
}
|
||||
|
||||
if (updateQuery.getQuery() != null) {
|
||||
Query queryQuery = updateQuery.getQuery();
|
||||
ub.query(getQuery(queryQuery, null));
|
||||
|
||||
// no indicesOptions available like in old client
|
||||
|
||||
ub.scroll(time(queryQuery.getScrollTime()));
|
||||
}
|
||||
|
||||
// no maxRetries available like in old client
|
||||
// no shouldStoreResult
|
||||
|
||||
if (updateQuery.getRefreshPolicy() != null) {
|
||||
ub.refresh(updateQuery.getRefreshPolicy() == RefreshPolicy.IMMEDIATE);
|
||||
}
|
||||
|
||||
if (updateQuery.getTimeout() != null) {
|
||||
ub.timeout(tb -> tb.time(updateQuery.getTimeout()));
|
||||
}
|
||||
|
||||
if (updateQuery.getWaitForActiveShards() != null) {
|
||||
ub.waitForActiveShards(w -> w.count(TypeUtils.waitForActiveShardsCount(updateQuery.getWaitForActiveShards())));
|
||||
}
|
||||
|
||||
return ub;
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region search
|
||||
@ -831,13 +1048,36 @@ class RequestConverter {
|
||||
builder.scroll(t -> t.time(scrollTimeInMillis + "ms"));
|
||||
}
|
||||
|
||||
builder.query(getQuery(query));
|
||||
builder.query(getQuery(query, clazz));
|
||||
|
||||
addFilter(query, builder);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public MsearchRequest searchMsearchRequest(
|
||||
List<ElasticsearchTemplate.MultiSearchQueryParameter> multiSearchQueryParameters) {
|
||||
|
||||
return MsearchRequest.of(mrb -> {
|
||||
multiSearchQueryParameters.forEach(param -> {
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(param.clazz);
|
||||
|
||||
mrb.searches(sb -> sb //
|
||||
.header(h -> h //
|
||||
.index(param.index.getIndexName()) //
|
||||
// todo #2156 add remaining flags for header
|
||||
) //
|
||||
.body(bb -> bb //
|
||||
.query(getQuery(param.query, param.clazz))//
|
||||
// todo #2138 seq_no_primary_term and version not available in client ES issue 161
|
||||
// todo #2156 add remaining flags for body
|
||||
) //
|
||||
);
|
||||
});
|
||||
return mrb;
|
||||
});
|
||||
}
|
||||
|
||||
private <T> void prepareSearchRequest(Query query, @Nullable Class<T> clazz, IndexCoordinates indexCoordinates,
|
||||
SearchRequest.Builder builder, boolean forCount, boolean useScroll) {
|
||||
|
||||
@ -878,7 +1118,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
// todo #1973 indices options
|
||||
// new Elasticsearch client does not support the old Indices options, need to be adapted
|
||||
}
|
||||
|
||||
if (query.isLimiting()) {
|
||||
@ -893,7 +1133,7 @@ class RequestConverter {
|
||||
builder.preference(query.getPreference());
|
||||
}
|
||||
|
||||
// todo #1973 searchType
|
||||
builder.searchType(searchType(query.getSearchType()));
|
||||
|
||||
if (query.getSort() != null) {
|
||||
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
||||
@ -921,7 +1161,7 @@ class RequestConverter {
|
||||
builder.routing(query.getRoute());
|
||||
}
|
||||
|
||||
// todo #1973 timeout
|
||||
builder.timeout(timeStringMs(query.getTimeout()));
|
||||
|
||||
if (query.getExplain()) {
|
||||
builder.explain(true);
|
||||
@ -930,8 +1170,12 @@ class RequestConverter {
|
||||
if (!isEmpty(query.getSearchAfter())) {
|
||||
builder.searchAfter(query.getSearchAfter().stream().map(Object::toString).collect(Collectors.toList()));
|
||||
}
|
||||
// todo #1973 rescorer queries
|
||||
// todo #1973 request cache
|
||||
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> {
|
||||
builder.rescore(getRescore(rescorerQuery));
|
||||
});
|
||||
|
||||
builder.requestCache(query.getRequestCache());
|
||||
|
||||
if (!query.getRuntimeFields().isEmpty()) {
|
||||
|
||||
@ -952,10 +1196,34 @@ class RequestConverter {
|
||||
// request_cache is not allowed on scroll requests.
|
||||
builder.requestCache(null);
|
||||
Duration scrollTimeout = query.getScrollTime() != null ? query.getScrollTime() : Duration.ofMinutes(1);
|
||||
builder.scroll(tv -> tv.time(scrollTimeout.toMillis() + "ms"));
|
||||
builder.scroll(time(scrollTimeout));
|
||||
// limit the number of documents in a batch
|
||||
builder.size(500);
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Map<String, Double> boosts = new LinkedHashMap<>();
|
||||
query.getIndicesBoost().forEach(indexBoost -> {
|
||||
boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost());
|
||||
});
|
||||
// noinspection unchecked
|
||||
builder.indicesBoost(boosts);
|
||||
}
|
||||
}
|
||||
|
||||
private Rescore getRescore(RescorerQuery rescorerQuery) {
|
||||
|
||||
return Rescore.of(r -> r //
|
||||
.query(rq -> rq //
|
||||
.query(getQuery(rescorerQuery.getQuery(), null)) //
|
||||
.scoreMode(TypeUtils.scoreMode(rescorerQuery.getScoreMode())) //
|
||||
.queryWeight(rescorerQuery.getQueryWeight() != null ? Double.valueOf(rescorerQuery.getQueryWeight()) : 1.0) //
|
||||
.rescoreQueryWeight(
|
||||
rescorerQuery.getRescoreQueryWeight() != null ? Double.valueOf(rescorerQuery.getRescoreQueryWeight())
|
||||
: 1.0) //
|
||||
|
||||
) //
|
||||
.windowSize(rescorerQuery.getWindowSize()));
|
||||
}
|
||||
|
||||
private void addHighlight(Query query, SearchRequest.Builder builder) {
|
||||
@ -1000,8 +1268,9 @@ class RequestConverter {
|
||||
.geoDistance(gd -> gd //
|
||||
.field(fieldName) //
|
||||
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint())))//
|
||||
.distanceType(geoDistanceType(geoDistanceOrder.getDistanceType())).mode(sortMode(finalMode)) //
|
||||
.unit(distanceUnit(geoDistanceOrder.getUnit())) //
|
||||
.distanceType(TypeUtils.geoDistanceType(geoDistanceOrder.getDistanceType()))
|
||||
.mode(TypeUtils.sortMode(finalMode)) //
|
||||
.unit(TypeUtils.distanceUnit(geoDistanceOrder.getUnit())) //
|
||||
.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped())));
|
||||
} else {
|
||||
String missing = (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) ? "_first"
|
||||
@ -1011,10 +1280,10 @@ class RequestConverter {
|
||||
.field(f -> {
|
||||
f.field(fieldName) //
|
||||
.order(sortOrder) //
|
||||
.mode(sortMode(finalMode));
|
||||
.mode(TypeUtils.sortMode(finalMode));
|
||||
|
||||
if (finalUnmappedType != null) {
|
||||
FieldType fieldType = fieldType(finalUnmappedType);
|
||||
FieldType fieldType = TypeUtils.fieldType(finalUnmappedType);
|
||||
|
||||
if (fieldType != null) {
|
||||
f.unmappedType(fieldType);
|
||||
@ -1032,23 +1301,33 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
private void prepareNativeSearch(NativeQuery query, SearchRequest.Builder builder) {
|
||||
// todo #1973 script fields
|
||||
// todo #1973 collapse builder
|
||||
// todo #1973 indices boost
|
||||
|
||||
query.getScriptedFields().forEach(scriptedField -> {
|
||||
builder.scriptFields(scriptedField.getFieldName(), sf -> sf.script(getScript(scriptedField.getScriptData())));
|
||||
});
|
||||
|
||||
builder //
|
||||
.suggest(query.getSuggester()) //
|
||||
.collapse(query.getFieldCollapse()) //
|
||||
;
|
||||
|
||||
if (!isEmpty(query.getAggregations())) {
|
||||
builder.aggregations(query.getAggregations());
|
||||
}
|
||||
|
||||
builder.suggest(query.getSuggester());
|
||||
|
||||
// todo #1973 searchExt
|
||||
// todo #2150 searchExt, currently not supported by the new client
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private co.elastic.clients.elasticsearch._types.query_dsl.Query getQuery(Query query) {
|
||||
private co.elastic.clients.elasticsearch._types.query_dsl.Query getQuery(@Nullable Query query,
|
||||
@Nullable Class<?> clazz) {
|
||||
|
||||
if (query == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
elasticsearchConverter.updateQuery(query, clazz);
|
||||
|
||||
// todo #1973 some native stuff
|
||||
co.elastic.clients.elasticsearch._types.query_dsl.Query esQuery = null;
|
||||
|
||||
if (query instanceof CriteriaQuery) {
|
||||
@ -1074,7 +1353,7 @@ class RequestConverter {
|
||||
} else if (query instanceof StringQuery) {
|
||||
// no filter for StringQuery
|
||||
} else if (query instanceof NativeQuery) {
|
||||
// todo #1973 NativeQuery filter
|
||||
builder.postFilter(((NativeQuery) query).getFilter());
|
||||
} else {
|
||||
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
|
||||
}
|
||||
@ -1128,6 +1407,7 @@ class RequestConverter {
|
||||
|
||||
return moreLikeThisQuery;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region helper functions
|
||||
@ -1208,12 +1488,6 @@ class RequestConverter {
|
||||
return versionType != null ? versionType : VersionType.External;
|
||||
}
|
||||
|
||||
private co.elastic.clients.elasticsearch._types.query_dsl.Query getFilter(Query filterQuery) {
|
||||
// TODO #1973 add filter query
|
||||
|
||||
throw new UnsupportedOperationException("not implemented");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SourceConfig getSourceConfig(Query query) {
|
||||
|
||||
|
@ -23,6 +23,7 @@ import co.elastic.clients.elasticsearch._types.Time;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
|
||||
import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateByQueryResponse;
|
||||
import co.elastic.clients.elasticsearch.core.mget.MultiGetError;
|
||||
import co.elastic.clients.elasticsearch.core.mget.MultiGetResponseItem;
|
||||
import co.elastic.clients.elasticsearch.indices.*;
|
||||
@ -314,6 +315,56 @@ class ResponseConverter {
|
||||
}
|
||||
|
||||
public ByQueryResponse byQueryResponse(DeleteByQueryResponse response) {
|
||||
// the code for the methods taking a DeleteByQueryResponse or a UpdateByQueryResponse is duplicated because the
|
||||
// Elasticsearch responses do not share a common class
|
||||
// noinspection DuplicatedCode
|
||||
List<ByQueryResponse.Failure> failures = response.failures().stream().map(this::byQueryResponseFailureOf)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ByQueryResponse.ByQueryResponseBuilder builder = ByQueryResponse.builder();
|
||||
|
||||
if (response.took() != null) {
|
||||
builder.withTook(response.took());
|
||||
}
|
||||
|
||||
if (response.timedOut() != null) {
|
||||
builder.withTimedOut(response.timedOut());
|
||||
}
|
||||
|
||||
if (response.total() != null) {
|
||||
builder.withTotal(response.total());
|
||||
}
|
||||
|
||||
if (response.deleted() != null) {
|
||||
builder.withDeleted(response.deleted());
|
||||
}
|
||||
|
||||
if (response.batches() != null) {
|
||||
builder.withBatches(Math.toIntExact(response.batches()));
|
||||
}
|
||||
|
||||
if (response.versionConflicts() != null) {
|
||||
builder.withVersionConflicts(response.versionConflicts());
|
||||
}
|
||||
|
||||
if (response.noops() != null) {
|
||||
builder.withNoops(response.noops());
|
||||
}
|
||||
|
||||
if (response.retries() != null) {
|
||||
builder.withBulkRetries(response.retries().bulk());
|
||||
builder.withSearchRetries(response.retries().search());
|
||||
}
|
||||
|
||||
builder.withFailures(failures);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public ByQueryResponse byQueryResponse(UpdateByQueryResponse response) {
|
||||
// the code for the methods taking a DeleteByQueryResponse or a UpdateByQueryResponse is duplicated because the
|
||||
// Elasticsearch responses do not share a common class
|
||||
// noinspection DuplicatedCode
|
||||
List<ByQueryResponse.Failure> failures = response.failures().stream().map(this::byQueryResponseFailureOf)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@ -358,8 +409,8 @@ class ResponseConverter {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region helper functions
|
||||
|
||||
private long timeToLong(Time time) {
|
||||
|
||||
if (time.isTime()) {
|
||||
|
@ -19,6 +19,7 @@ import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
|
||||
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.elasticsearch.core.search.Suggestion;
|
||||
import co.elastic.clients.elasticsearch.core.search.TotalHits;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
@ -45,21 +46,22 @@ class SearchDocumentResponseBuilder {
|
||||
/**
|
||||
* creates a SearchDocumentResponse from the {@link SearchResponse}
|
||||
*
|
||||
* @param searchResponse the Elasticsearch search response
|
||||
* @param responseBody the Elasticsearch response body
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param jsonpMapper to map JsonData objects
|
||||
* @return the SearchDocumentResponse
|
||||
*/
|
||||
public static <T> SearchDocumentResponse from(SearchResponse<EntityAsMap> searchResponse,
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public static <T> SearchDocumentResponse from(ResponseBody<EntityAsMap> responseBody,
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {
|
||||
|
||||
Assert.notNull(searchResponse, "searchResponse must not be null");
|
||||
Assert.notNull(responseBody, "responseBody must not be null");
|
||||
Assert.notNull(entityCreator, "entityCreator must not be null");
|
||||
|
||||
HitsMetadata<EntityAsMap> hitsMetadata = searchResponse.hits();
|
||||
String scrollId = searchResponse.scrollId();
|
||||
Map<String, Aggregate> aggregations = searchResponse.aggregations();
|
||||
Map<String, List<Suggestion<EntityAsMap>>> suggest = searchResponse.suggest();
|
||||
HitsMetadata<EntityAsMap> hitsMetadata = responseBody.hits();
|
||||
String scrollId = responseBody.scrollId();
|
||||
Map<String, Aggregate> aggregations = responseBody.aggregations();
|
||||
Map<String, List<Suggestion<EntityAsMap>>> suggest = responseBody.suggest();
|
||||
|
||||
return from(hitsMetadata, scrollId, aggregations, suggest, entityCreator, jsonpMapper);
|
||||
}
|
||||
@ -114,7 +116,7 @@ class SearchDocumentResponseBuilder {
|
||||
ElasticsearchAggregations aggregationsContainer = aggregations != null ? new ElasticsearchAggregations(aggregations)
|
||||
: null;
|
||||
|
||||
// todo #1973
|
||||
// todo #2154
|
||||
Suggest suggest = null;
|
||||
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,
|
||||
|
@ -15,13 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Conflicts;
|
||||
import co.elastic.clients.elasticsearch._types.DistanceUnit;
|
||||
import co.elastic.clients.elasticsearch._types.GeoDistanceType;
|
||||
import co.elastic.clients.elasticsearch._types.OpType;
|
||||
import co.elastic.clients.elasticsearch._types.Refresh;
|
||||
import co.elastic.clients.elasticsearch._types.SortMode;
|
||||
import co.elastic.clients.elasticsearch._types.VersionType;
|
||||
import co.elastic.clients.elasticsearch._types.*;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
|
||||
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
|
||||
import co.elastic.clients.elasticsearch.core.search.BuiltinHighlighterType;
|
||||
@ -30,11 +24,17 @@ import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterOrder;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterTagsSchema;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterType;
|
||||
import co.elastic.clients.elasticsearch.core.search.ScoreMode;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.RefreshPolicy;
|
||||
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Order;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@ -243,6 +243,71 @@ final class TypeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static UpdateResponse.Result result(@Nullable Result result) {
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case Created:
|
||||
return UpdateResponse.Result.CREATED;
|
||||
case Updated:
|
||||
return UpdateResponse.Result.UPDATED;
|
||||
case Deleted:
|
||||
return UpdateResponse.Result.DELETED;
|
||||
case NotFound:
|
||||
return UpdateResponse.Result.NOT_FOUND;
|
||||
case NoOp:
|
||||
return UpdateResponse.Result.NOOP;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static ScoreMode scoreMode(@Nullable RescorerQuery.ScoreMode scoreMode) {
|
||||
|
||||
if (scoreMode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (scoreMode) {
|
||||
case Default:
|
||||
return null;
|
||||
case Avg:
|
||||
return ScoreMode.Avg;
|
||||
case Max:
|
||||
return ScoreMode.Max;
|
||||
case Min:
|
||||
return ScoreMode.Min;
|
||||
case Total:
|
||||
return ScoreMode.Total;
|
||||
case Multiply:
|
||||
return ScoreMode.Multiply;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static SearchType searchType(@Nullable Query.SearchType searchType) {
|
||||
|
||||
if (searchType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (searchType) {
|
||||
case QUERY_THEN_FETCH:
|
||||
return SearchType.QueryThenFetch;
|
||||
case DFS_QUERY_THEN_FETCH:
|
||||
return SearchType.DfsQueryThenFetch;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static SortMode sortMode(Order.Mode mode) {
|
||||
|
||||
@ -260,6 +325,26 @@ final class TypeUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Time time(@Nullable Duration duration) {
|
||||
|
||||
if (duration == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Time.of(t -> t.time(duration.toMillis() + "ms"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String timeStringMs(@Nullable Duration duration) {
|
||||
|
||||
if (duration == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return duration.toMillis() + "ms";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static VersionType versionType(
|
||||
@Nullable org.springframework.data.elasticsearch.annotations.Document.VersionType versionType) {
|
||||
|
@ -336,8 +336,13 @@ public class RequestConverters {
|
||||
public static Request index(IndexRequest indexRequest) {
|
||||
String method = Strings.hasLength(indexRequest.id()) ? HttpMethod.PUT.name() : HttpMethod.POST.name();
|
||||
boolean isCreate = (indexRequest.opType() == DocWriteRequest.OpType.CREATE);
|
||||
String endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(),
|
||||
isCreate ? "_create" : null);
|
||||
String endpoint;
|
||||
if (indexRequest.opType() == DocWriteRequest.OpType.CREATE) {
|
||||
endpoint = indexRequest.type().equals("_doc") ? endpoint(indexRequest.index(), "_create", indexRequest.id())
|
||||
: endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(), "_create");
|
||||
} else {
|
||||
endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id());
|
||||
}
|
||||
Request request = new Request(method, endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
@ -362,7 +367,9 @@ public class RequestConverters {
|
||||
}
|
||||
|
||||
public static Request update(UpdateRequest updateRequest) {
|
||||
String endpoint = endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
|
||||
String endpoint = updateRequest.type().equals("_doc")
|
||||
? endpoint(updateRequest.index(), "_update", updateRequest.id())
|
||||
: endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
|
||||
Request request = new Request(HttpMethod.POST.name(), endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
@ -500,8 +507,11 @@ public class RequestConverters {
|
||||
}
|
||||
|
||||
public static Request explain(ExplainRequest explainRequest) {
|
||||
String endpoint = explainRequest.type().equals("_doc")
|
||||
? endpoint(explainRequest.index(), "_explain", explainRequest.id())
|
||||
: endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain");
|
||||
Request request = new Request(HttpMethod.GET.name(),
|
||||
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain"));
|
||||
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), endpoint));
|
||||
|
||||
Params params = new Params(request);
|
||||
params.withStoredFields(explainRequest.storedFields());
|
||||
|
@ -478,7 +478,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null && idProperty.isWritable()
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
protected final SimpleElasticsearchMappingContext mappingContext;
|
||||
protected final EntityOperations entityOperations;
|
||||
|
||||
protected @Nullable RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE;
|
||||
protected @Nullable RefreshPolicy refreshPolicy = RefreshPolicy.NONE;
|
||||
protected RoutingResolver routingResolver;
|
||||
|
||||
protected @Nullable ReactiveEntityCallbacks entityCallbacks;
|
||||
@ -259,7 +259,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null && idProperty.isWritable()
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
@ -443,7 +443,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType) {
|
||||
public Flux<? extends AggregationContainer<?>> aggregate(Query query, Class<?> entityType) {
|
||||
return aggregate(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
|
@ -277,8 +277,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
*/
|
||||
protected Mono<GetResult> doGet(GetRequest request) {
|
||||
|
||||
return Mono.from(execute(client -> client.get(request))) //
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Mono.empty());
|
||||
return Mono.from(execute(client -> client.get(request)));
|
||||
}
|
||||
|
||||
protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index) {
|
||||
@ -633,8 +632,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
QUERY_LOGGER.debug(String.format("Executing doCount: %s", request));
|
||||
}
|
||||
|
||||
return Mono.from(execute(client -> client.count(request))) //
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Mono.just(0L));
|
||||
return Mono.from(execute(client -> client.count(request)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,8 +22,8 @@ import java.util.List;
|
||||
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
|
||||
@ -237,7 +237,7 @@ public interface ReactiveSearchOperations {
|
||||
* @return a {@link Flux} emitting matching aggregations one by one.
|
||||
* @since 4.0
|
||||
*/
|
||||
Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType);
|
||||
Flux<? extends AggregationContainer<?>> aggregate(Query query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Perform an aggregation specified by the given {@link Query query}. <br />
|
||||
@ -248,7 +248,7 @@ public interface ReactiveSearchOperations {
|
||||
* @return a {@link Flux} emitting matching aggregations one by one.
|
||||
* @since 4.0
|
||||
*/
|
||||
Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
Flux<? extends AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Does a suggest query
|
||||
|
@ -613,7 +613,7 @@ class RequestFactory {
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
|
||||
// If we have a query id and a document id, do not ask ES to generate one.
|
||||
if (id != null) {
|
||||
indexRequest = new IndexRequest(indexName).id(id);
|
||||
@ -1027,7 +1027,14 @@ class RequestFactory {
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
}
|
||||
Script script = new Script(getScriptType(query.getScriptType()), query.getLang(), query.getScript(), params);
|
||||
org.elasticsearch.script.ScriptType scriptType = getScriptType(query.getScriptType());
|
||||
String lang = query.getLang();
|
||||
|
||||
if (scriptType == org.elasticsearch.script.ScriptType.INLINE && lang == null) {
|
||||
lang = "painless";
|
||||
}
|
||||
|
||||
Script script = new Script(scriptType, lang, query.getScript(), params);
|
||||
updateRequest.script(script);
|
||||
}
|
||||
|
||||
@ -1092,8 +1099,7 @@ class RequestFactory {
|
||||
|
||||
public UpdateByQueryRequest updateByQueryRequest(UpdateQuery query, IndexCoordinates index) {
|
||||
|
||||
String indexName = index.getIndexName();
|
||||
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(indexName);
|
||||
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(index.getIndexNames());
|
||||
updateByQueryRequest.setScript(getScript(query));
|
||||
|
||||
if (query.getAbortOnVersionConflict() != null) {
|
||||
|
@ -66,7 +66,6 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public static ElasticsearchDateConverter of(String pattern) {
|
||||
|
||||
Assert.notNull(pattern, "pattern must not be null");
|
||||
Assert.hasText(pattern, "pattern must not be empty");
|
||||
|
||||
String[] subPatterns = pattern.split("\\|\\|");
|
||||
@ -86,7 +85,7 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public String format(TemporalAccessor accessor) {
|
||||
|
||||
Assert.notNull("accessor", "accessor must not be null");
|
||||
Assert.notNull(accessor, "accessor must not be null");
|
||||
|
||||
if (accessor instanceof Instant) {
|
||||
Instant instant = (Instant) accessor;
|
||||
|
@ -192,7 +192,7 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to do the actual writing. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* Class to do the actual reading. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* refactored to allow for keeping state during the conversion of an object.
|
||||
*/
|
||||
private static class Reader extends Base {
|
||||
@ -211,10 +211,17 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* Reads the given source into the given type.
|
||||
*
|
||||
* @param type they type to convert the given source to.
|
||||
* @param source the source to create an object of the given type from.
|
||||
* @return the object that was read
|
||||
*/
|
||||
<R> R read(Class<R> type, Document source) {
|
||||
|
||||
TypeInformation<R> typeHint = ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeHint, source);
|
||||
TypeInformation<R> typeInformation = ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeInformation, source);
|
||||
|
||||
if (r == null) {
|
||||
throw new ConversionException("could not convert into object of class " + type);
|
||||
@ -225,11 +232,11 @@ public class MappingElasticsearchConverter
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R read(TypeInformation<R> type, Map<String, Object> source) {
|
||||
private <R> R read(TypeInformation<R> typeInformation, Map<String, Object> source) {
|
||||
|
||||
Assert.notNull(source, "Source must not be null!");
|
||||
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type);
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, typeInformation);
|
||||
Class<? extends R> rawType = typeToUse.getType();
|
||||
|
||||
if (conversions.hasCustomReadTarget(source.getClass(), rawType)) {
|
||||
@ -247,8 +254,8 @@ public class MappingElasticsearchConverter
|
||||
if (typeToUse.equals(ClassTypeInformation.OBJECT)) {
|
||||
return (R) source;
|
||||
}
|
||||
// Retrieve persistent entity info
|
||||
|
||||
// Retrieve persistent entity info
|
||||
ElasticsearchPersistentEntity<?> entity = mappingContext.getPersistentEntity(typeToUse);
|
||||
|
||||
if (entity == null) {
|
||||
@ -333,7 +340,7 @@ public class MappingElasticsearchConverter
|
||||
PersistentPropertyAccessor<R> propertyAccessor = new ConvertingPropertyAccessor<>(
|
||||
targetEntity.getPropertyAccessor(result), conversionService);
|
||||
// Only deal with String because ES generated Ids are strings !
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
if (idProperty != null && idProperty.isWritable() && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, document.getId());
|
||||
}
|
||||
}
|
||||
@ -365,7 +372,6 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
|
||||
@ -397,7 +403,7 @@ public class MappingElasticsearchConverter
|
||||
|
||||
for (ElasticsearchPersistentProperty prop : entity) {
|
||||
|
||||
if (entity.isConstructorArgument(prop) || !prop.isReadable()) {
|
||||
if (entity.isConstructorArgument(prop) || !prop.isReadable() || !prop.isWritable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -455,12 +461,44 @@ public class MappingElasticsearchConverter
|
||||
} else if (value.getClass().isArray()) {
|
||||
return (T) readCollectionOrArray(type, Arrays.asList((Object[]) value));
|
||||
} else if (value instanceof Map) {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null) {
|
||||
Object o = read(collectionComponentType, (Map<String, Object>) value);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
return (T) read(type, (Map<String, Object>) value);
|
||||
} else {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null
|
||||
&& collectionComponentType.isAssignableFrom(ClassTypeInformation.from(value.getClass()))) {
|
||||
Object o = getPotentiallyConvertedSimpleRead(value, collectionComponentType);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
|
||||
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getCollectionWithSingleElement(TypeInformation<?> collectionType,
|
||||
TypeInformation<?> componentType, Object element) {
|
||||
Collection<Object> collection = CollectionFactory.createCollection(collectionType.getType(),
|
||||
componentType.getType(), 1);
|
||||
collection.add(element);
|
||||
return (T) collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the type to check
|
||||
* @return true if type is a collectoin, null otherwise,
|
||||
*/
|
||||
@Nullable
|
||||
TypeInformation<?> getCollectionComponentType(TypeInformation<?> type) {
|
||||
return type.isCollectionLike() ? type.getComponentType() : null;
|
||||
}
|
||||
|
||||
private Object propertyConverterRead(ElasticsearchPersistentProperty property, Object source) {
|
||||
PropertyValueConverter propertyValueConverter = Objects.requireNonNull(property.getPropertyValueConverter());
|
||||
|
||||
@ -649,7 +687,8 @@ public class MappingElasticsearchConverter
|
||||
* @see org.springframework.data.mapping.model.SpELExpressionParameterValueProvider#potentiallyConvertSpelValue(java.lang.Object, org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||
*/
|
||||
@Override
|
||||
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
protected <T> T potentiallyConvertSpelValue(Object object,
|
||||
Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
return readValue(object, parameter.getType());
|
||||
}
|
||||
}
|
||||
@ -1117,17 +1156,18 @@ public class MappingElasticsearchConverter
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
if (domainClass != null) {
|
||||
if (domainClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateFieldsAndSourceFilter(query, domainClass);
|
||||
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
|
||||
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updateCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updatePropertiesInCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||
|
||||
@ -1165,14 +1205,22 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> updateFieldNames(List<String> fields, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fields.stream().map(fieldName -> {
|
||||
/**
|
||||
* relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
|
||||
* fieldname. If no such property exists, the original fieldName is kept.
|
||||
*
|
||||
* @param fieldNames list of fieldnames
|
||||
* @param persistentEntity the persistent entity to check
|
||||
* @return an updated list of field names
|
||||
*/
|
||||
private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fieldNames.stream().map(fieldName -> {
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
|
||||
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
|
||||
Assert.notNull(criteriaQuery, "criteriaQuery must not be null");
|
||||
Assert.notNull(domainClass, "domainClass must not be null");
|
||||
@ -1181,17 +1229,17 @@ public class MappingElasticsearchConverter
|
||||
|
||||
if (persistentEntity != null) {
|
||||
for (Criteria chainedCriteria : criteriaQuery.getCriteria().getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
for (Criteria subCriteria : criteriaQuery.getCriteria().getSubCriteria()) {
|
||||
for (Criteria chainedCriteria : subCriteria.getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Field field = criteria.getField();
|
||||
|
||||
|
@ -22,7 +22,8 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* class that holds explanations returned from an Elasticsearch search.
|
||||
* class that holds explanations returned from an Elasticsearch search. Note: the new Elasticsearch client does not
|
||||
* return the match property in search hits anymore, probably because a returned hit always is a match.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
|
@ -62,15 +62,16 @@ public class BaseQuery implements Query {
|
||||
@Nullable protected Integer maxResults;
|
||||
@Nullable protected HighlightQuery highlightQuery;
|
||||
@Nullable private Boolean trackTotalHits;
|
||||
@Nullable private Integer trackTotalHitsUpTo;
|
||||
@Nullable private Duration scrollTime;
|
||||
@Nullable private Duration timeout;
|
||||
@Nullable protected Integer trackTotalHitsUpTo;
|
||||
@Nullable protected Duration scrollTime;
|
||||
@Nullable protected Duration timeout;
|
||||
private boolean explain = false;
|
||||
@Nullable private List<Object> searchAfter;
|
||||
@Nullable protected List<Object> searchAfter;
|
||||
protected List<RescorerQuery> rescorerQueries = new ArrayList<>();
|
||||
@Nullable protected Boolean requestCache;
|
||||
private List<IdWithRouting> idsWithRouting = Collections.emptyList();
|
||||
private final List<RuntimeField> runtimeFields = new ArrayList<>();
|
||||
protected List<IdWithRouting> idsWithRouting = Collections.emptyList();
|
||||
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
|
||||
@Nullable protected List<IndexBoost> indicesBoost;
|
||||
|
||||
public BaseQuery() {}
|
||||
|
||||
@ -86,7 +87,9 @@ public class BaseQuery implements Query {
|
||||
this.preference = builder.getPreference();
|
||||
this.sourceFilter = builder.getSourceFilter();
|
||||
this.fields = builder.getFields();
|
||||
// #1973 add the other fields to the builder
|
||||
this.highlightQuery = builder.highlightQuery;
|
||||
this.route = builder.getRoute();
|
||||
this.indicesBoost = builder.getIndicesBoost();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -431,4 +434,10 @@ public class BaseQuery implements Query {
|
||||
public List<RuntimeField> getRuntimeFields() {
|
||||
return runtimeFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<IndexBoost> getIndicesBoost() {
|
||||
return indicesBoost;
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,9 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
@Nullable private String preference;
|
||||
@Nullable private SourceFilter sourceFilter;
|
||||
private List<String> fields = new ArrayList<>();
|
||||
@Nullable protected HighlightQuery highlightQuery;
|
||||
@Nullable private String route;
|
||||
@Nullable private List<IndexBoost> indicesBoost;
|
||||
|
||||
@Nullable
|
||||
public Pageable getPageable() {
|
||||
@ -92,6 +95,21 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HighlightQuery getHighlightQuery() {
|
||||
return highlightQuery;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<IndexBoost> getIndicesBoost() {
|
||||
return indicesBoost;
|
||||
}
|
||||
|
||||
public SELF withPageable(Pageable pageable) {
|
||||
this.pageable = pageable;
|
||||
return self();
|
||||
@ -156,6 +174,26 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withHighlightQuery(HighlightQuery highlightQuery) {
|
||||
this.highlightQuery = highlightQuery;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withRoute(String route) {
|
||||
this.route = route;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIndicesBoost(List<IndexBoost> indicesBoost) {
|
||||
this.indicesBoost = indicesBoost;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF withIndicesBoost(IndexBoost... indicesBoost) {
|
||||
this.indicesBoost = Arrays.asList(indicesBoost);
|
||||
return self();
|
||||
}
|
||||
|
||||
public abstract Q build();
|
||||
|
||||
private SELF self() {
|
||||
|
@ -55,7 +55,6 @@ public class NativeSearchQuery extends BaseQuery {
|
||||
@Nullable private List<PipelineAggregationBuilder> pipelineAggregations;
|
||||
@Nullable private HighlightBuilder highlightBuilder;
|
||||
@Nullable private HighlightBuilder.Field[] highlightFields;
|
||||
@Nullable private List<IndexBoost> indicesBoost;
|
||||
@Nullable private SearchTemplateRequestBuilder searchTemplate;
|
||||
@Nullable private SuggestBuilder suggestBuilder;
|
||||
@Nullable private List<SearchExtBuilder> searchExtBuilders;
|
||||
@ -182,11 +181,6 @@ public class NativeSearchQuery extends BaseQuery {
|
||||
this.pipelineAggregations = pipelineAggregationBuilders;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<IndexBoost> getIndicesBoost() {
|
||||
return indicesBoost;
|
||||
}
|
||||
|
||||
public void setIndicesBoost(List<IndexBoost> indicesBoost) {
|
||||
this.indicesBoost = indicesBoost;
|
||||
}
|
||||
|
@ -63,9 +63,7 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
|
||||
@Nullable private List<HighlightBuilder.Field> highlightFields = new ArrayList<>();
|
||||
@Nullable protected List<String> storedFields;
|
||||
@Nullable private CollapseBuilder collapseBuilder;
|
||||
@Nullable private List<IndexBoost> indicesBoost = new ArrayList<>();
|
||||
@Nullable private SearchTemplateRequestBuilder searchTemplateBuilder;
|
||||
@Nullable private String route;
|
||||
@Nullable private SearchType searchType;
|
||||
@Nullable private Boolean trackTotalHits;
|
||||
@Nullable private Duration timeout;
|
||||
@ -195,19 +193,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withIndicesBoost(Collection<IndexBoost> indicesBoost) {
|
||||
this.indicesBoost.addAll(indicesBoost);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
public NativeSearchQueryBuilder withIndicesBoost(IndexBoost... indicesBoost) {
|
||||
Collections.addAll(this.indicesBoost, indicesBoost);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withSearchTemplate(SearchTemplateRequestBuilder searchTemplateBuilder) {
|
||||
this.searchTemplateBuilder = searchTemplateBuilder;
|
||||
return this;
|
||||
@ -232,11 +217,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withRoute(String route) {
|
||||
this.route = route;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NativeSearchQueryBuilder withSearchType(SearchType searchType) {
|
||||
this.searchType = searchType;
|
||||
return this;
|
||||
@ -296,10 +276,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
|
||||
nativeSearchQuery.setStoredFields(storedFields);
|
||||
}
|
||||
|
||||
if (indicesBoost != null) {
|
||||
nativeSearchQuery.setIndicesBoost(indicesBoost);
|
||||
}
|
||||
|
||||
if (searchTemplateBuilder != null) {
|
||||
nativeSearchQuery.setSearchTemplate(searchTemplateBuilder);
|
||||
}
|
||||
@ -320,10 +296,6 @@ public class NativeSearchQueryBuilder extends BaseQueryBuilder<NativeSearchQuery
|
||||
nativeSearchQuery.setPipelineAggregations(pipelineAggregationBuilders);
|
||||
}
|
||||
|
||||
if (route != null) {
|
||||
nativeSearchQuery.setRoute(route);
|
||||
}
|
||||
|
||||
if (searchType != null) {
|
||||
nativeSearchQuery.setSearchType(Query.SearchType.valueOf(searchType.name()));
|
||||
}
|
||||
|
@ -433,6 +433,12 @@ public interface Query {
|
||||
*/
|
||||
List<RuntimeField> getRuntimeFields();
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
@Nullable
|
||||
List<IndexBoost> getIndicesBoost();
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ScriptType;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* value class combining script information.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class ScriptData {
|
||||
@Nullable private final ScriptType type;
|
||||
@Nullable private final String language;
|
||||
@Nullable private final String script;
|
||||
@Nullable private final String scriptName;
|
||||
@Nullable private final Map<String, Object> params;
|
||||
|
||||
public ScriptData(@Nullable ScriptType type, @Nullable String language, @Nullable String script,
|
||||
@Nullable String scriptName, @Nullable Map<String, Object> params) {
|
||||
|
||||
this.type = type;
|
||||
this.language = language;
|
||||
this.script = script;
|
||||
this.scriptName = scriptName;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ScriptType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public class ScriptedField {
|
||||
|
||||
private final String fieldName;
|
||||
private final ScriptData scriptData;
|
||||
|
||||
public ScriptedField(String fieldName, ScriptData scriptData) {
|
||||
|
||||
Assert.notNull(fieldName, "fieldName must not be null");
|
||||
Assert.notNull(scriptData, "scriptData must not be null");
|
||||
|
||||
this.fieldName = fieldName;
|
||||
this.scriptData = scriptData;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public ScriptData getScriptData() {
|
||||
return scriptData;
|
||||
}
|
||||
}
|
@ -35,11 +35,8 @@ import org.springframework.lang.Nullable;
|
||||
public class UpdateQuery {
|
||||
|
||||
private final String id;
|
||||
@Nullable private final String script;
|
||||
@Nullable private final Map<String, Object> params;
|
||||
@Nullable private final Document document;
|
||||
@Nullable private final Document upsert;
|
||||
@Nullable private final String lang;
|
||||
@Nullable private final String routing;
|
||||
@Nullable private final Boolean scriptedUpsert;
|
||||
@Nullable private final Boolean docAsUpsert;
|
||||
@ -61,9 +58,8 @@ public class UpdateQuery {
|
||||
@Nullable private final Float requestsPerSecond;
|
||||
@Nullable private final Boolean shouldStoreResult;
|
||||
@Nullable private final Integer slices;
|
||||
@Nullable private final ScriptType scriptType;
|
||||
@Nullable private final String scriptName;
|
||||
@Nullable private final String indexName;
|
||||
@Nullable private final ScriptData scriptData;
|
||||
|
||||
public static Builder builder(String id) {
|
||||
return new Builder(id);
|
||||
@ -85,11 +81,8 @@ public class UpdateQuery {
|
||||
@Nullable String scriptName, @Nullable String indexName) {
|
||||
|
||||
this.id = id;
|
||||
this.script = script;
|
||||
this.params = params;
|
||||
this.document = document;
|
||||
this.upsert = upsert;
|
||||
this.lang = lang;
|
||||
this.routing = routing;
|
||||
this.scriptedUpsert = scriptedUpsert;
|
||||
this.docAsUpsert = docAsUpsert;
|
||||
@ -111,9 +104,13 @@ public class UpdateQuery {
|
||||
this.requestsPerSecond = requestsPerSecond;
|
||||
this.shouldStoreResult = shouldStoreResult;
|
||||
this.slices = slices;
|
||||
this.scriptType = scriptType;
|
||||
this.scriptName = scriptName;
|
||||
this.indexName = indexName;
|
||||
|
||||
if (scriptType != null || lang != null || script != null || scriptName != null || params != null) {
|
||||
this.scriptData = new ScriptData(scriptType, lang, script, scriptName, params);
|
||||
} else {
|
||||
this.scriptData = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@ -122,12 +119,12 @@ public class UpdateQuery {
|
||||
|
||||
@Nullable
|
||||
public String getScript() {
|
||||
return script;
|
||||
return scriptData != null ? scriptData.getScript() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
return scriptData != null ? scriptData.getParams() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -142,7 +139,7 @@ public class UpdateQuery {
|
||||
|
||||
@Nullable
|
||||
public String getLang() {
|
||||
return lang;
|
||||
return scriptData != null ? scriptData.getLanguage() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -252,12 +249,12 @@ public class UpdateQuery {
|
||||
|
||||
@Nullable
|
||||
public ScriptType getScriptType() {
|
||||
return scriptType;
|
||||
return scriptData != null ? scriptData.getType() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
return scriptData != null ? scriptData.getScriptName() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,13 +265,21 @@ public class UpdateQuery {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
@Nullable
|
||||
public ScriptData getScriptData() {
|
||||
return scriptData;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private String id;
|
||||
@Nullable private String script = null;
|
||||
@Nullable private Map<String, Object> params;
|
||||
@Nullable private Document document = null;
|
||||
@Nullable private Document upsert = null;
|
||||
@Nullable private String lang = "painless";
|
||||
@Nullable private String lang = null;
|
||||
@Nullable private String routing = null;
|
||||
@Nullable private Boolean scriptedUpsert;
|
||||
@Nullable private Boolean docAsUpsert;
|
||||
|
@ -35,6 +35,13 @@ public class UpdateResponse {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public static UpdateResponse of(Result result) {
|
||||
return new UpdateResponse(result);
|
||||
}
|
||||
|
||||
public Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
@ -30,6 +30,17 @@ public class Highlight {
|
||||
private final HighlightParameters parameters;
|
||||
private final List<HighlightField> fields;
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public Highlight(List<HighlightField> fields) {
|
||||
|
||||
Assert.notNull(fields, "fields must not be null");
|
||||
|
||||
this.parameters = HighlightParameters.builder().build();
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public Highlight(HighlightParameters parameters, List<HighlightField> fields) {
|
||||
|
||||
Assert.notNull(parameters, "parameters must not be null");
|
||||
|
@ -25,6 +25,17 @@ public class HighlightField {
|
||||
private final String name;
|
||||
private final HighlightFieldParameters parameters;
|
||||
|
||||
/**
|
||||
* @since 4.4
|
||||
*/
|
||||
public HighlightField(String name) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
||||
this.name = name;
|
||||
this.parameters = HighlightFieldParameters.builder().build();
|
||||
}
|
||||
|
||||
public HighlightField(String name, HighlightFieldParameters parameters) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
|
@ -82,8 +82,7 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor
|
||||
|
||||
ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor);
|
||||
|
||||
Query query = createQuery(
|
||||
new ConvertingParameterAccessor(elasticsearchOperations.getElasticsearchConverter(), parameterAccessor));
|
||||
Query query = createQuery(parameterAccessor);
|
||||
|
||||
if (queryMethod.hasAnnotatedHighlight()) {
|
||||
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
|
||||
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ConvertingParameterAccessor implements ElasticsearchParameterAccessor {
|
||||
|
||||
private final ElasticsearchConverter converter;
|
||||
private final ElasticsearchParameterAccessor delegate;
|
||||
|
||||
public ConvertingParameterAccessor(ElasticsearchConverter converter, ElasticsearchParameterAccessor delegate) {
|
||||
|
||||
this.converter = converter;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getValues() {
|
||||
return delegate.getValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable getPageable() {
|
||||
return delegate.getPageable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort getSort() {
|
||||
return delegate.getSort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class<?>> getDynamicProjection() {
|
||||
return delegate.getDynamicProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> findDynamicProjection() {
|
||||
return delegate.findDynamicProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBindableValue(int index) {
|
||||
return getConvertedValue(delegate.getBindableValue(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBindableNullValue() {
|
||||
return delegate.hasBindableNullValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getConvertedValue(Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (converter.getConversionService().canConvert(value.getClass(), String.class)) {
|
||||
return converter.getConversionService().convert(value, String.class);
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
}
|
@ -128,6 +128,9 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
||||
|
||||
} else if (tree.isCountProjection()) {
|
||||
result = elasticsearchOperations.count(query, clazz, index);
|
||||
} else if (tree.isExistsProjection()) {
|
||||
long count = elasticsearchOperations.count(query, clazz, index);
|
||||
result = count > 0;
|
||||
} else {
|
||||
result = elasticsearchOperations.searchOne(query, clazz, index);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 4.4 M3 (2021.2.0)
|
||||
Spring Data Elasticsearch 4.4.3 (2021.2.3)
|
||||
Copyright (c) [2013-2021] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@ -28,6 +28,12 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
version.spring-data-elasticsearch=${project.version}
|
||||
version.elasticsearch-client=${elasticsearch}
|
||||
version.elasticsearch-client=${elasticsearch-rhlc}
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
// * Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
import org.springframework.data.elasticsearch.client.elc.QueryBuilders;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
|
||||
/**
|
||||
* Class providing some queries for the new Elasticsearch client needed in different tests.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public final class ELCQueries {
|
||||
|
||||
private ELCQueries() {}
|
||||
|
||||
public static Query getTermsAggsQuery(String aggsName, String aggsField){
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(QueryBuilders.matchAllQueryAsQuery()) //
|
||||
.withAggregation(aggsName, Aggregation.of(a -> a //
|
||||
.terms(ta -> ta.field(aggsField)))) //
|
||||
.withMaxResults(0) //
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Query queryWithIds(String... ids) {
|
||||
return NativeQuery.builder().withIds(ids).build();
|
||||
}
|
||||
|
||||
public static BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
|
||||
return NativeQuery.builder().withQuery(matchAllQueryAsQuery());
|
||||
}
|
||||
|
||||
public static BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
|
||||
return NativeQuery.builder().withQuery(termQueryAsQuery(field, value));
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
package org.springframework.data.elasticsearch;
|
||||
|
||||
/**
|
||||
* TODO #1973 remove when the new Elasticsearch client is fully working
|
||||
* TODO remove when the new Elasticsearch client is fully working
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
|
@ -25,7 +25,7 @@ import co.elastic.clients.elasticsearch.cluster.HealthResponse;
|
||||
import co.elastic.clients.elasticsearch.core.IndexRequest;
|
||||
import co.elastic.clients.elasticsearch.core.IndexResponse;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
|
||||
import co.elastic.clients.elasticsearch.indices.ElasticsearchIndicesClient;
|
||||
import co.elastic.clients.elasticsearch.indices.IndexSettings;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
@ -300,7 +300,7 @@ public class DevTests {
|
||||
.query(query -> query.match(matchQuery -> matchQuery.field("content").query(FieldValue.of("content1"))))
|
||||
.build();
|
||||
|
||||
SearchResponse<EntityAsMap> searchResponse = null;
|
||||
ResponseBody<EntityAsMap> searchResponse = null;
|
||||
try {
|
||||
searchResponse = searchImperative(searchRequest);
|
||||
assertThat(searchResponse).isNotNull();
|
||||
@ -316,11 +316,11 @@ public class DevTests {
|
||||
}
|
||||
}
|
||||
|
||||
private SearchResponse<EntityAsMap> searchImperative(SearchRequest searchRequest) throws IOException {
|
||||
private ResponseBody<EntityAsMap> searchImperative(SearchRequest searchRequest) throws IOException {
|
||||
return imperativeElasticsearchClient.search(searchRequest, EntityAsMap.class);
|
||||
}
|
||||
|
||||
private SearchResponse<EntityAsMap> searchReactive(SearchRequest searchRequest) {
|
||||
private ResponseBody<EntityAsMap> searchReactive(SearchRequest searchRequest) {
|
||||
return Objects.requireNonNull(reactiveElasticsearchClient.search(searchRequest, EntityAsMap.class).block());
|
||||
}
|
||||
// endregion
|
||||
|
@ -20,12 +20,15 @@ import co.elastic.clients.json.JsonData;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.elasticsearch.core.document.Explanation;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
||||
|
||||
/**
|
||||
@ -106,4 +109,53 @@ class DocumentAdaptersUnitTests {
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // #725 #1973
|
||||
@DisplayName("should adapt returned explanations")
|
||||
void shouldAdaptReturnedExplanations() {
|
||||
|
||||
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
|
||||
.index("index") //
|
||||
.id("42") //
|
||||
.explanation(eb -> eb //
|
||||
.value(3.14f) //
|
||||
.description("explanation 3.14") //
|
||||
.details(edb -> edb.description("explanation noMatch").value(0f)))
|
||||
.build();
|
||||
|
||||
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
|
||||
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
|
||||
Explanation explanation = searchDocument.getExplanation();
|
||||
softly.assertThat(explanation).isNotNull();
|
||||
softly.assertThat(explanation.isMatch()).isTrue();
|
||||
softly.assertThat(explanation.getValue()).isCloseTo(3.14, Offset.offset(0.001));
|
||||
softly.assertThat(explanation.getDescription()).isEqualTo("explanation 3.14");
|
||||
List<Explanation> details = explanation.getDetails();
|
||||
softly.assertThat(details)
|
||||
.containsExactly(new Explanation(null, 0.0, "explanation noMatch", Collections.emptyList()));
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
@Test // DATAES-979 #1973
|
||||
@DisplayName("should adapt returned matched queries")
|
||||
void shouldAdaptReturnedMatchedQueries() {
|
||||
|
||||
Hit<EntityAsMap> searchHit = new Hit.Builder<EntityAsMap>() //
|
||||
.index("index") //
|
||||
.id("42") //
|
||||
.matchedQueries("query1", "query2") //
|
||||
.build();
|
||||
|
||||
SearchDocument searchDocument = DocumentAdapters.from(searchHit, jsonpMapper);
|
||||
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
|
||||
List<String> matchedQueries = searchDocument.getMatchedQueries();
|
||||
softly.assertThat(matchedQueries).isNotNull();
|
||||
softly.assertThat(matchedQueries).hasSize(2);
|
||||
softly.assertThat(matchedQueries).isEqualTo(Arrays.asList("query1", "query2"));
|
||||
softly.assertAll();
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ public class ReactiveElasticsearchClientUnitTests {
|
||||
});
|
||||
|
||||
URI uri = hostProvider.when(HOST).captureUri();
|
||||
assertThat(uri.getRawPath()).isEqualTo("/twitter/_doc/10/_create");
|
||||
assertThat(uri.getRawPath()).isEqualTo("/twitter/_create/10");
|
||||
}
|
||||
|
||||
@Test // DATAES-488
|
||||
@ -439,7 +439,7 @@ public class ReactiveElasticsearchClientUnitTests {
|
||||
hostProvider.when(HOST) //
|
||||
.receiveUpdateOk();
|
||||
|
||||
client.update(new UpdateRequest("twitter", "doc", "1").doc(Collections.singletonMap("user", "cstrobl"))).then() //
|
||||
client.update(new UpdateRequest("twitter", "1").doc(Collections.singletonMap("user", "cstrobl"))).then() //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
@ -449,7 +449,7 @@ public class ReactiveElasticsearchClientUnitTests {
|
||||
});
|
||||
|
||||
URI uri = hostProvider.when(HOST).captureUri();
|
||||
assertThat(uri.getRawPath()).isEqualTo("/twitter/doc/1/_update");
|
||||
assertThat(uri.getRawPath()).isEqualTo("/twitter/_update/1");
|
||||
}
|
||||
|
||||
@Test // DATAES-488
|
||||
|
@ -18,16 +18,29 @@ package org.springframework.data.elasticsearch.core;
|
||||
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreMode;
|
||||
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.ELCQueries;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptData;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
@ -54,7 +67,7 @@ public class ElasticsearchELCIntegrationTests extends ElasticsearchIntegrationTe
|
||||
|
||||
@Override
|
||||
protected Query queryWithIds(String... ids) {
|
||||
return NativeQuery.builder().withIds(ids).build();
|
||||
return ELCQueries.queryWithIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,7 +77,7 @@ public class ElasticsearchELCIntegrationTests extends ElasticsearchIntegrationTe
|
||||
|
||||
@Override
|
||||
protected BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
|
||||
return NativeQuery.builder().withQuery(matchAllQueryAsQuery());
|
||||
return ELCQueries.getBuilderWithMatchAllQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -93,4 +106,101 @@ public class ElasticsearchELCIntegrationTests extends ElasticsearchIntegrationTe
|
||||
.withMinScore(minScore) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(matchAllQueryAsQuery()) //
|
||||
.withFieldCollapse(FieldCollapse.of(fc -> {
|
||||
fc.field(collapseField);
|
||||
|
||||
if (innerHits != null) {
|
||||
fc.innerHits(ih -> ih.name(innerHits).size(size));
|
||||
}
|
||||
return fc;
|
||||
})).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getMatchAllQueryWithFilterForId(String id) {
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(matchAllQueryAsQuery()) //
|
||||
.withFilter(termQueryAsQuery("id", id)) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryForParentId(String type, String id, @Nullable String route) {
|
||||
|
||||
NativeQueryBuilder queryBuilder = NativeQuery.builder() //
|
||||
.withQuery(qb -> qb //
|
||||
.parentId(p -> p.type(type).id(id)) //
|
||||
);
|
||||
|
||||
if (route != null) {
|
||||
queryBuilder.withRoute(route);
|
||||
}
|
||||
|
||||
return queryBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@Nullable String includes, String fieldName,
|
||||
String script, Map<String, Object> params) {
|
||||
|
||||
NativeQueryBuilder nativeQueryBuilder = NativeQuery.builder().withQuery(matchAllQueryAsQuery());
|
||||
|
||||
if (includes != null) {
|
||||
nativeQueryBuilder.withSourceFilter(new FetchSourceFilterBuilder().withIncludes(includes).build());
|
||||
}
|
||||
|
||||
return nativeQueryBuilder.withScriptedField(new ScriptedField( //
|
||||
fieldName, //
|
||||
new ScriptData(ScriptType.INLINE, "expression", script, null, params))) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithRescorer() {
|
||||
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(q -> q //
|
||||
.bool(b -> b //
|
||||
.filter(f -> f.exists(e -> e.field("rate"))) //
|
||||
.should(s -> s.term(t -> t.field("message").value("message"))) //
|
||||
)) //
|
||||
.withResorerQuery( //
|
||||
new RescorerQuery(NativeQuery.builder() //
|
||||
.withQuery(q -> q //
|
||||
.functionScore(fs -> fs //
|
||||
.functions(f1 -> f1 //
|
||||
.filter(matchAllQueryAsQuery()) //
|
||||
.weight(1.0) //
|
||||
.gauss(d -> d //
|
||||
.field("rate") //
|
||||
.placement(dp -> dp //
|
||||
.origin(JsonData.of(0)) //
|
||||
.scale(JsonData.of(10)) //
|
||||
.decay(0.5)) //
|
||||
)) //
|
||||
.functions(f2 -> f2 //
|
||||
.filter(matchAllQueryAsQuery()).weight(100.0) //
|
||||
.gauss(d -> d //
|
||||
.field("rate") //
|
||||
.placement(dp -> dp //
|
||||
.origin(JsonData.of(0)) //
|
||||
.scale(JsonData.of(10)) //
|
||||
.decay(0.5)) //
|
||||
|
||||
)) //
|
||||
.scoreMode(FunctionScoreMode.Sum) //
|
||||
.maxBoost(80.0) //
|
||||
.boostMode(FunctionBoostMode.Replace)) //
|
||||
) //
|
||||
.build() //
|
||||
) //
|
||||
.withScoreMode(RescorerQuery.ScoreMode.Max) //
|
||||
.withWindowSize(100)) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,17 @@ import java.util.Map;
|
||||
import org.elasticsearch.action.support.ActiveShardCount;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.common.lucene.search.function.CombineFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
|
||||
import org.elasticsearch.join.query.ParentIdQueryBuilder;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@ -38,12 +47,16 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptField;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
@ -115,6 +128,79 @@ public class ElasticsearchERHLCIntegrationTests extends ElasticsearchIntegration
|
||||
.withMinScore(minScore).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
|
||||
CollapseBuilder collapseBuilder = new CollapseBuilder(collapseField);
|
||||
|
||||
if (innerHits != null) {
|
||||
InnerHitBuilder innerHitBuilder = new InnerHitBuilder(innerHits);
|
||||
|
||||
if (size != null) {
|
||||
innerHitBuilder.setSize(size);
|
||||
}
|
||||
|
||||
collapseBuilder.setInnerHits(innerHitBuilder);
|
||||
}
|
||||
|
||||
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withCollapseBuilder(collapseBuilder).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getMatchAllQueryWithFilterForId(String id) {
|
||||
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withFilter(boolQuery().filter(termQuery("id", id)))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryForParentId(String type, String id, @Nullable String route) {
|
||||
|
||||
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
|
||||
.withQuery(new ParentIdQueryBuilder(type, id));
|
||||
|
||||
if (route != null) {
|
||||
queryBuilder.withRoute(route);
|
||||
}
|
||||
return queryBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@Nullable String includes, String fieldName,
|
||||
String script, Map<String, java.lang.Object> params) {
|
||||
|
||||
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(matchAllQuery());
|
||||
|
||||
if (includes != null) {
|
||||
nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilterBuilder().withIncludes(includes).build());
|
||||
}
|
||||
return nativeSearchQueryBuilder.withScriptField(new ScriptField(fieldName,
|
||||
new Script(org.elasticsearch.script.ScriptType.INLINE, "expression", script, params))).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithRescorer() {
|
||||
return new NativeSearchQueryBuilder() //
|
||||
.withQuery( //
|
||||
boolQuery() //
|
||||
.filter(existsQuery("rate")) //
|
||||
.should(termQuery("message", "message"))) //
|
||||
.withRescorerQuery( //
|
||||
new RescorerQuery( //
|
||||
new NativeSearchQueryBuilder() //
|
||||
.withQuery(QueryBuilders
|
||||
.functionScoreQuery(new FunctionScoreQueryBuilder.FilterFunctionBuilder[] {
|
||||
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
|
||||
new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(1f)),
|
||||
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
|
||||
new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(100f)) }) //
|
||||
.scoreMode(FunctionScoreQuery.ScoreMode.SUM) //
|
||||
.maxBoost(80f) //
|
||||
.boostMode(CombineFunction.REPLACE)) //
|
||||
.build())//
|
||||
.withScoreMode(RescorerQuery.ScoreMode.Max) //
|
||||
.withWindowSize(100)) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test // DATAES-768
|
||||
void shouldUseAllOptionsFromUpdateQuery() {
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
@ -153,7 +239,7 @@ public class ElasticsearchERHLCIntegrationTests extends ElasticsearchIntegration
|
||||
assertThat(fetchSourceContext.excludes()).containsExactlyInAnyOrder("excl");
|
||||
}
|
||||
|
||||
@Test // #1446
|
||||
@Test // #1446, #2191
|
||||
void shouldUseAllOptionsFromUpdateByQuery() throws JSONException {
|
||||
|
||||
Query searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()) //
|
||||
@ -183,9 +269,10 @@ public class ElasticsearchERHLCIntegrationTests extends ElasticsearchIntegration
|
||||
" }" + " }" + '}';
|
||||
|
||||
// when
|
||||
UpdateByQueryRequest request = getRequestFactory().updateByQueryRequest(updateQuery, IndexCoordinates.of("index"));
|
||||
UpdateByQueryRequest request = getRequestFactory().updateByQueryRequest(updateQuery, IndexCoordinates.of("index1", "index2"));
|
||||
|
||||
// then
|
||||
assertThat(request.indices()).containsExactlyInAnyOrder("index1", "index2");
|
||||
assertThat(request).isNotNull();
|
||||
assertThat(request.getSearchRequest().indicesOptions()).usingRecursiveComparison()
|
||||
.isEqualTo(IndicesOptions.LENIENT_EXPAND_OPEN);
|
||||
|
@ -36,19 +36,6 @@ import java.util.stream.IntStream;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.common.lucene.search.function.CombineFunction;
|
||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
|
||||
import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder;
|
||||
import org.elasticsearch.join.query.ParentIdQueryBuilder;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
@ -58,7 +45,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.annotation.AccessType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@ -82,7 +71,8 @@ import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.join.JoinField;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery.ScoreMode;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
@ -170,7 +160,23 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
protected abstract BaseQueryBuilder<?, ?> getBuilderWithWildcardQuery(String field, String value);
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
protected abstract Query getQueryWithCollapse(String collapseField, @Nullable String innerHits,
|
||||
@Nullable Integer size);
|
||||
|
||||
protected abstract Query getMatchAllQueryWithFilterForId(String id);
|
||||
|
||||
protected abstract Query getQueryForParentId(String type, String id, @Nullable String route);
|
||||
|
||||
protected Query getMatchAllQueryWithInlineExpressionScript(String fieldName, String script,
|
||||
Map<String, java.lang.Object> params) {
|
||||
return getMatchAllQueryWithIncludesAndInlineExpressionScript(null, fieldName, script, params);
|
||||
}
|
||||
|
||||
protected abstract Query getMatchAllQueryWithIncludesAndInlineExpressionScript(@Nullable String includes,
|
||||
String fieldName, String script, Map<String, java.lang.Object> params);
|
||||
|
||||
protected abstract Query getQueryWithRescorer();
|
||||
|
||||
@Test
|
||||
public void shouldThrowDataAccessExceptionIfDocumentDoesNotExistWhileDoingPartialUpdate() {
|
||||
|
||||
@ -451,7 +457,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
public void shouldDoBulkUpdate() {
|
||||
|
||||
@ -618,11 +623,9 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(operations.count(searchQuery, IndexCoordinates.of("test-index-*"))).isEqualTo(2);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
public void shouldFilterSearchResultsForGivenFilter() {
|
||||
|
||||
// given
|
||||
String documentId = nextIdAsString();
|
||||
SampleEntity sampleEntity = SampleEntity.builder().id(documentId).message("some message")
|
||||
.version(System.currentTimeMillis()).build();
|
||||
@ -630,14 +633,11 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
IndexQuery indexQuery = getIndexQuery(sampleEntity);
|
||||
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withFilter(boolQuery().filter(termQuery("id", documentId))).build();
|
||||
Query query = getMatchAllQueryWithFilterForId(documentId);
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@ -825,7 +825,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
public void shouldUseScriptedFields() {
|
||||
|
||||
@ -847,10 +846,8 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
params.put("factor", 2);
|
||||
|
||||
// when
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withScriptField(
|
||||
new ScriptField("scriptedRate", new Script(ScriptType.INLINE, "expression", "doc['rate'] * factor", params)))
|
||||
.build();
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
Query query = getMatchAllQueryWithInlineExpressionScript("scriptedRate", "doc['rate'] * factor", params);
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
@ -1502,7 +1499,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(indexOperations.exists()).isFalse();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
public void shouldDoPartialUpdateForExistingDocument() {
|
||||
|
||||
@ -1534,10 +1530,9 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(indexedEntity.getMessage()).isEqualTo(messageAfterUpdate);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
void shouldDoUpdateByQueryForExistingDocument() {
|
||||
// given
|
||||
|
||||
final String documentId = nextIdAsString();
|
||||
final String messageBeforeUpdate = "some test message";
|
||||
final String messageAfterUpdate = "test message";
|
||||
@ -1549,7 +1544,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
operations.index(indexQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
final NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
final Query query = operations.matchAllQuery();
|
||||
|
||||
final UpdateQuery updateQuery = UpdateQuery.builder(query)
|
||||
.withScriptType(org.springframework.data.elasticsearch.core.ScriptType.INLINE)
|
||||
@ -1557,42 +1552,15 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
.withParams(Collections.singletonMap("newMessage", messageAfterUpdate)).withAbortOnVersionConflict(true)
|
||||
.build();
|
||||
|
||||
// when
|
||||
operations.updateByQuery(updateQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
assertThat(indexedEntity.getMessage()).isEqualTo(messageAfterUpdate);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-227
|
||||
public void shouldUseUpsertOnUpdate() {
|
||||
|
||||
// given
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("id", "1");
|
||||
doc.put("message", "test");
|
||||
|
||||
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
|
||||
.from(doc);
|
||||
|
||||
UpdateQuery updateQuery = UpdateQuery.builder("1") //
|
||||
.withDocument(document) //
|
||||
.withUpsert(document) //
|
||||
.build();
|
||||
|
||||
// when
|
||||
UpdateRequest request = getRequestFactory().updateRequest(updateQuery, IndexCoordinates.of("index"));
|
||||
|
||||
// then
|
||||
assertThat(request).isNotNull();
|
||||
assertThat(request.upsertRequest()).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
public void shouldDoUpsertIfDocumentDoesNotExist() {
|
||||
|
||||
@ -1650,63 +1618,57 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(entities.size()).isGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@DisabledIf(value = "newElasticsearchClient",
|
||||
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
|
||||
// and version to be set in the request
|
||||
@Test // DATAES-487
|
||||
public void shouldReturnSameEntityForMultiSearch() {
|
||||
|
||||
// given
|
||||
List<IndexQuery> indexQueries = new ArrayList<>();
|
||||
|
||||
indexQueries.add(buildIndex(SampleEntity.builder().id("1").message("ab").build()));
|
||||
indexQueries.add(buildIndex(SampleEntity.builder().id("2").message("bc").build()));
|
||||
indexQueries.add(buildIndex(SampleEntity.builder().id("3").message("ac").build()));
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
List<Query> queries = new ArrayList<>();
|
||||
queries.add(getTermQuery("message", "ab"));
|
||||
queries.add(getTermQuery("message", "bc"));
|
||||
queries.add(getTermQuery("message", "ac"));
|
||||
|
||||
// when
|
||||
List<NativeSearchQuery> queries = new ArrayList<>();
|
||||
|
||||
queries.add(new NativeSearchQueryBuilder().withQuery(termQuery("message", "ab")).build());
|
||||
queries.add(new NativeSearchQueryBuilder().withQuery(termQuery("message", "bc")).build());
|
||||
queries.add(new NativeSearchQueryBuilder().withQuery(termQuery("message", "ac")).build());
|
||||
|
||||
// then
|
||||
List<SearchHits<SampleEntity>> searchHits = operations.multiSearch(queries, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
for (SearchHits<SampleEntity> sampleEntity : searchHits) {
|
||||
assertThat(sampleEntity.getTotalHits()).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@DisabledIf(value = "newElasticsearchClient",
|
||||
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
|
||||
// and version to be set in the request
|
||||
@Test // DATAES-487
|
||||
public void shouldReturnDifferentEntityForMultiSearch() {
|
||||
|
||||
// given
|
||||
Class<Book> clazz = Book.class;
|
||||
IndexOperations bookIndexOperations = operations.indexOps(Book.class);
|
||||
bookIndexOperations.delete();
|
||||
bookIndexOperations.create();
|
||||
indexOperations.putMapping(clazz);
|
||||
bookIndexOperations.createWithMapping();
|
||||
bookIndexOperations.refresh();
|
||||
|
||||
IndexCoordinates bookIndex = IndexCoordinates.of("test-index-book-core-template");
|
||||
|
||||
IndexCoordinates bookIndex = IndexCoordinates.of("i-need-my-own-index");
|
||||
operations.index(buildIndex(SampleEntity.builder().id("1").message("ab").build()),
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
operations.index(buildIndex(Book.builder().id("2").description("bc").build()), bookIndex);
|
||||
|
||||
bookIndexOperations.refresh();
|
||||
|
||||
// when
|
||||
List<NativeSearchQuery> queries = new ArrayList<>();
|
||||
queries.add(new NativeSearchQueryBuilder().withQuery(termQuery("message", "ab")).build());
|
||||
queries.add(new NativeSearchQueryBuilder().withQuery(termQuery("description", "bc")).build());
|
||||
List<Query> queries = new ArrayList<>();
|
||||
queries.add(getTermQuery("message", "ab"));
|
||||
queries.add(getTermQuery("description", "bc"));
|
||||
|
||||
List<SearchHits<?>> searchHitsList = operations.multiSearch(queries, Lists.newArrayList(SampleEntity.class, clazz),
|
||||
IndexCoordinates.of(indexNameProvider.indexName(), bookIndex.getIndexName()));
|
||||
|
||||
// then
|
||||
bookIndexOperations.delete();
|
||||
|
||||
SearchHits<?> searchHits0 = searchHitsList.get(0);
|
||||
assertThat(searchHits0.getTotalHits()).isEqualTo(1L);
|
||||
SearchHit<SampleEntity> searchHit0 = (SearchHit<SampleEntity>) searchHits0.getSearchHit(0);
|
||||
@ -1950,7 +1912,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
@Test
|
||||
public void shouldIndexSampleEntityWithIndexAtRuntime() {
|
||||
|
||||
String indexName = "custom-" + indexNameProvider.indexName();
|
||||
String indexName = indexNameProvider.indexName() + "-custom";
|
||||
|
||||
// given
|
||||
String documentId = nextIdAsString();
|
||||
@ -2699,7 +2661,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(sampleEntities.get(2).getContent().getMessage()).isEqualTo(sampleEntity1.getMessage());
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-593
|
||||
public void shouldReturnDocumentWithCollapsedField() {
|
||||
|
||||
@ -2715,11 +2676,9 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withCollapseField("rate")
|
||||
.build();
|
||||
Query query = getQueryWithCollapse("rate", null, null);
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
@ -2730,7 +2689,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(searchHits.getSearchHit(1).getContent().getMessage()).isEqualTo("message 2");
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // #1493
|
||||
@DisplayName("should return document with collapse field and inner hits")
|
||||
public void shouldReturnDocumentWithCollapsedFieldAndInnerHits() {
|
||||
@ -2747,11 +2705,10 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withCollapseBuilder(new CollapseBuilder("rate").setInnerHits(new InnerHitBuilder("innerHits"))).build();
|
||||
Query query = getQueryWithCollapse("rate", "innerHits", null);
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
@ -2764,7 +2721,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(searchHits.getSearchHit(1).getInnerHits("innerHits").getTotalHits()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
|
||||
@Test // #1997
|
||||
@DisplayName("should return document with inner hits size zero")
|
||||
void shouldReturnDocumentWithInnerHitsSizeZero() {
|
||||
@ -2777,12 +2734,10 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withCollapseBuilder(new CollapseBuilder("rate").setInnerHits(new InnerHitBuilder("innerHits").setSize(0)))
|
||||
.build();
|
||||
Query query = getQueryWithCollapse("rate", "innerHits", 0);
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
@ -2830,6 +2785,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(settings).doesNotContainKey("index.max_result_window");
|
||||
}
|
||||
|
||||
@DisabledIf(value = "newElasticsearchClient", disabledReason = "ES 7.17.6 returns a null storage type that the new client cannot handle")
|
||||
@Test // DATAES-709
|
||||
public void shouldIncludeDefaultsOnGetIndexSettings() {
|
||||
|
||||
@ -2867,7 +2823,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
List<Object> sortValues = searchHit.getSortValues();
|
||||
assertThat(sortValues).hasSize(2);
|
||||
assertThat(sortValues.get(0)).isInstanceOf(String.class).isEqualTo("thousands");
|
||||
// transport client returns Long, RestHghlevelClient Integer, new ElasticsearchClient String
|
||||
// transport client returns Long, RestHighlevelClient Integer, new ElasticsearchClient String
|
||||
java.lang.Object o = sortValues.get(1);
|
||||
if (o instanceof Integer) {
|
||||
Integer i = (Integer) o;
|
||||
@ -2882,7 +2838,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-715
|
||||
void shouldReturnHighlightFieldsInSearchHit() {
|
||||
IndexCoordinates index = IndexCoordinates.of("test-index-highlight-entity-template");
|
||||
@ -2893,11 +2848,10 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
operations.index(indexQuery, index);
|
||||
operations.indexOps(index).refresh();
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(termQuery("message", "message")) //
|
||||
.withHighlightFields(new HighlightBuilder.Field("message")) //
|
||||
Query query = getBuilderWithTermQuery("message", "message") //
|
||||
.withHighlightQuery(
|
||||
new HighlightQuery(new Highlight(singletonList(new HighlightField("message"))), HighlightEntity.class))
|
||||
.build();
|
||||
|
||||
SearchHits<HighlightEntity> searchHits = operations.search(query, HighlightEntity.class, index);
|
||||
|
||||
assertThat(searchHits).isNotNull();
|
||||
@ -2910,10 +2864,9 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(highlightField.get(1)).contains("<em>message</em>");
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // #1686
|
||||
void shouldRunRescoreQueryInSearchQuery() {
|
||||
IndexCoordinates index = IndexCoordinates.of("test-index-rescore-entity-template");
|
||||
IndexCoordinates index = IndexCoordinates.of(indexNameProvider.getPrefix() + "rescore-entity");
|
||||
|
||||
// matches main query better
|
||||
SampleEntity entity = SampleEntity.builder() //
|
||||
@ -2933,17 +2886,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
operations.bulkIndex(indexQueries, index);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(boolQuery().filter(existsQuery("rate")).should(termQuery("message", "message"))) //
|
||||
.withRescorerQuery(
|
||||
new RescorerQuery(new NativeSearchQueryBuilder().withQuery(QueryBuilders
|
||||
.functionScoreQuery(new FunctionScoreQueryBuilder.FilterFunctionBuilder[] {
|
||||
new FilterFunctionBuilder(new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(1f)),
|
||||
new FilterFunctionBuilder(
|
||||
new GaussDecayFunctionBuilder("rate", 0, 10, null, 0.5).setWeight(100f)) })
|
||||
.scoreMode(FunctionScoreQuery.ScoreMode.SUM).maxBoost(80f).boostMode(CombineFunction.REPLACE)).build())
|
||||
.withScoreMode(ScoreMode.Max).withWindowSize(100))
|
||||
.build();
|
||||
Query query = getQueryWithRescorer();
|
||||
|
||||
SearchHits<SampleEntity> searchHits = operations.search(query, SampleEntity.class, index);
|
||||
|
||||
@ -2956,6 +2899,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(searchHit.getScore()).isEqualTo(80f);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
// DATAES-738
|
||||
void shouldSaveEntityWithIndexCoordinates() {
|
||||
@ -3128,7 +3072,9 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThatSeqNoPrimaryTermIsFilled(retrieved);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@DisabledIf(value = "newElasticsearchClient",
|
||||
disabledReason = "todo #2138 can't check response, open ES issue 161 that does not allow seqno")
|
||||
// and version to be set in the request
|
||||
@Test // DATAES-799
|
||||
void multiSearchShouldReturnSeqNoPrimaryTerm() {
|
||||
OptimisticEntity original = new OptimisticEntity();
|
||||
@ -3158,7 +3104,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThatSeqNoPrimaryTermIsFilled(retrieved);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799
|
||||
void shouldThrowOptimisticLockingFailureExceptionWhenConcurrentUpdateOccursOnEntityWithSeqNoPrimaryTermProperty() {
|
||||
OptimisticEntity original = new OptimisticEntity();
|
||||
@ -3175,7 +3120,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThatThrownBy(() -> operations.save(forEdit2)).isInstanceOf(OptimisticLockingFailureException.class);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799
|
||||
void shouldThrowOptimisticLockingFailureExceptionWhenConcurrentUpdateOccursOnVersionedEntityWithSeqNoPrimaryTermProperty() {
|
||||
OptimisticAndVersionedEntity original = new OptimisticAndVersionedEntity();
|
||||
@ -3204,7 +3148,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
operations.save(forEdit);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
void shouldSupportCRUDOpsForEntityWithJoinFields() throws Exception {
|
||||
String qId1 = java.util.UUID.randomUUID().toString();
|
||||
@ -3258,9 +3201,8 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
operations.save(
|
||||
Arrays.asList(sampleQuestionEntity1, sampleQuestionEntity2, sampleAnswerEntity1, sampleAnswerEntity2), index);
|
||||
|
||||
SearchHits<SampleJoinEntity> hits = operations.search(
|
||||
new NativeSearchQueryBuilder().withQuery(new ParentIdQueryBuilder("answer", qId1)).build(),
|
||||
SampleJoinEntity.class);
|
||||
Query query = getQueryForParentId("answer", qId1, null);
|
||||
SearchHits<SampleJoinEntity> hits = operations.search(query, SampleJoinEntity.class);
|
||||
|
||||
List<String> hitIds = hits.getSearchHits().stream()
|
||||
.map(sampleJoinEntitySearchHit -> sampleJoinEntitySearchHit.getId()).collect(Collectors.toList());
|
||||
@ -3287,8 +3229,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
// when
|
||||
operations.bulkUpdate(queries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
SearchHits<SampleJoinEntity> updatedHits = operations.search(
|
||||
new NativeSearchQueryBuilder().withQuery(new ParentIdQueryBuilder("answer", qId2)).build(),
|
||||
SearchHits<SampleJoinEntity> updatedHits = operations.search(getQueryForParentId("answer", qId2, null),
|
||||
SampleJoinEntity.class);
|
||||
|
||||
List<String> hitIds = updatedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
|
||||
@ -3300,8 +3241,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(hitIds.size()).isEqualTo(1);
|
||||
assertThat(hitIds.get(0)).isEqualTo(aId2);
|
||||
|
||||
updatedHits = operations.search(
|
||||
new NativeSearchQueryBuilder().withQuery(new ParentIdQueryBuilder("answer", qId1)).build(),
|
||||
updatedHits = operations.search(getQueryForParentId("answer", qId1, null),
|
||||
SampleJoinEntity.class);
|
||||
|
||||
hitIds = updatedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
|
||||
@ -3315,20 +3255,15 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
|
||||
private void shouldDeleteEntityWithJoinFields(String qId2, String aId2) throws Exception {
|
||||
Query query = new NativeSearchQueryBuilder().withQuery(new ParentIdQueryBuilder("answer", qId2)).withRoute(qId2)
|
||||
.build();
|
||||
operations.delete(query, SampleJoinEntity.class, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
SearchHits<SampleJoinEntity> deletedHits = operations.search(
|
||||
new NativeSearchQueryBuilder().withQuery(new ParentIdQueryBuilder("answer", qId2)).build(),
|
||||
operations.delete(getQueryForParentId("answer", qId2, qId2), SampleJoinEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
SearchHits<SampleJoinEntity> deletedHits = operations.search(getQueryForParentId("answer", qId2, null),
|
||||
SampleJoinEntity.class);
|
||||
|
||||
List<String> hitIds = deletedHits.getSearchHits().stream().map(new Function<SearchHit<SampleJoinEntity>, String>() {
|
||||
@Override
|
||||
public String apply(SearchHit<SampleJoinEntity> sampleJoinEntitySearchHit) {
|
||||
return sampleJoinEntitySearchHit.getId();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
List<String> hitIds = deletedHits.getSearchHits().stream()
|
||||
.map(sampleJoinEntitySearchHit -> sampleJoinEntitySearchHit.getId()).collect(Collectors.toList());
|
||||
assertThat(hitIds.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@ -3539,7 +3474,6 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(retrieved).isEqualTo(saved);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // #1488
|
||||
@DisplayName("should set scripted fields on immutable objects")
|
||||
void shouldSetScriptedFieldsOnImmutableObjects() {
|
||||
@ -3549,13 +3483,10 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("factor", 2);
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withSourceFilter(new FetchSourceFilterBuilder().withIncludes("*").build())
|
||||
.withScriptField(new ScriptField("scriptedRate",
|
||||
new Script(ScriptType.INLINE, "expression", "doc['rate'] * factor", params)))
|
||||
.build();
|
||||
Query query = getMatchAllQueryWithIncludesAndInlineExpressionScript("*", "scriptedRate", "doc['rate'] * factor",
|
||||
params);
|
||||
|
||||
SearchHits<ImmutableWithScriptedEntity> searchHits = operations.search(searchQuery,
|
||||
SearchHits<ImmutableWithScriptedEntity> searchHits = operations.search(query,
|
||||
ImmutableWithScriptedEntity.class);
|
||||
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
@ -3565,6 +3496,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
assertThat(foundEntity.getScriptedRate()).isEqualTo(84.0);
|
||||
}
|
||||
|
||||
|
||||
@Test // #1893
|
||||
@DisplayName("should index document from source with version")
|
||||
void shouldIndexDocumentFromSourceWithVersion() {
|
||||
@ -3789,6 +3721,21 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
}
|
||||
|
||||
@Test // #2230
|
||||
@DisplayName("should work with readonly id")
|
||||
void shouldWorkWithReadonlyId() {
|
||||
|
||||
ReadonlyIdEntity entity = new ReadonlyIdEntity();
|
||||
entity.setPart1("foo");
|
||||
entity.setPart2("bar");
|
||||
operations.save(entity);
|
||||
|
||||
ReadonlyIdEntity readEntity = operations.get(entity.getId(), ReadonlyIdEntity.class);
|
||||
|
||||
assertThat(readEntity.getPart1()).isEqualTo(entity.getPart1());
|
||||
assertThat(readEntity.getPart2()).isEqualTo(entity.getPart2());
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
private static class SampleEntityUUIDKeyed {
|
||||
@Nullable
|
||||
@ -3875,7 +3822,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-book-core-template")
|
||||
@Document(indexName = "i-need-my-own-index")
|
||||
static class Book {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@ -4419,7 +4366,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "immutable-class")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-immutable")
|
||||
private static final class ImmutableEntity {
|
||||
@Id
|
||||
@Nullable private final String id;
|
||||
@ -4477,7 +4424,7 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "immutable-scripted")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-immutable-scripted")
|
||||
public static final class ImmutableWithScriptedEntity {
|
||||
@Id private final String id;
|
||||
@Field(type = Integer)
|
||||
@ -4534,5 +4481,36 @@ public abstract class ElasticsearchIntegrationTests implements NewElasticsearchC
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
||||
static class ReadonlyIdEntity {
|
||||
@Field(type = FieldType.Keyword) private String part1;
|
||||
|
||||
@Field(type = FieldType.Keyword) private String part2;
|
||||
|
||||
@Id
|
||||
@ReadOnlyProperty
|
||||
@AccessType(AccessType.Type.PROPERTY)
|
||||
public String getId() {
|
||||
return part1 + '-' + part2;
|
||||
}
|
||||
|
||||
public String getPart1() {
|
||||
return part1;
|
||||
}
|
||||
|
||||
public void setPart1(String part1) {
|
||||
this.part1 = part1;
|
||||
}
|
||||
|
||||
public String getPart2() {
|
||||
return part2;
|
||||
}
|
||||
|
||||
public void setPart2(String part2) {
|
||||
this.part2 = part2;
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -15,11 +15,30 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsAggregate;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
|
||||
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.ELCQueries;
|
||||
import org.springframework.data.elasticsearch.client.elc.Aggregation;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
@ -37,9 +56,66 @@ public class ReactiveElasticsearchELCIntegrationTests extends ReactiveElasticsea
|
||||
return new IndexNameProvider("reactive-template");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Query getTermsAggsQuery(String aggsName, String aggsField) {
|
||||
return ELCQueries.getTermsAggsQuery(aggsName, aggsField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesNewElasticsearchClient() {
|
||||
return true;
|
||||
protected BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
|
||||
return ELCQueries.getBuilderWithMatchAllQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
|
||||
return ELCQueries.getBuilderWithTermQuery(field, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(matchAllQueryAsQuery()) //
|
||||
.withFieldCollapse(FieldCollapse.of(fc -> {
|
||||
fc.field(collapseField);
|
||||
|
||||
if (innerHits != null) {
|
||||
fc.innerHits(ih -> ih.name(innerHits).size(size));
|
||||
}
|
||||
return fc;
|
||||
})).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query queryWithIds(String... ids) {
|
||||
return ELCQueries.queryWithIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <A extends AggregationContainer<?>> void assertThatAggregationsAreCorrect(A aggregationContainer) {
|
||||
Aggregation aggregation = ((ElasticsearchAggregation) aggregationContainer).aggregation();
|
||||
assertThat(aggregation.getName()).isEqualTo("messages");
|
||||
Aggregate aggregate = aggregation.getAggregate();
|
||||
assertThat(aggregate.isSterms()).isTrue();
|
||||
StringTermsAggregate parsedStringTerms = (StringTermsAggregate) aggregate.sterms();
|
||||
Buckets<StringTermsBucket> buckets = parsedStringTerms.buckets();
|
||||
assertThat(buckets.isArray()).isTrue();
|
||||
List<StringTermsBucket> bucketList = buckets.array();
|
||||
assertThat(bucketList.size()).isEqualTo(3);
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
bucketList.forEach(stringTermsBucket -> {
|
||||
if ("message".equals(stringTermsBucket.key())) {
|
||||
count.getAndIncrement();
|
||||
assertThat(stringTermsBucket.docCount()).isEqualTo(3);
|
||||
}
|
||||
if ("some".equals(stringTermsBucket.key())) {
|
||||
count.getAndIncrement();
|
||||
assertThat(stringTermsBucket.docCount()).isEqualTo(2);
|
||||
}
|
||||
if ("other".equals(stringTermsBucket.key())) {
|
||||
count.getAndIncrement();
|
||||
assertThat(stringTermsBucket.docCount()).isEqualTo(1);
|
||||
}
|
||||
});
|
||||
assertThat(count.get()).isEqualTo(3);
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,23 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
|
||||
import org.elasticsearch.index.query.InnerHitBuilder;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
|
||||
import org.elasticsearch.search.collapse.CollapseBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
@ -37,4 +49,55 @@ public class ReactiveElasticsearchERHLCIntegrationTests extends ReactiveElastics
|
||||
return new IndexNameProvider("reactive-template-es7");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getTermsAggsQuery(String aggsName, String aggsField) {
|
||||
return new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.addAggregation(AggregationBuilders.terms("messages").field("message")).build();
|
||||
}
|
||||
@Override
|
||||
protected BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery() {
|
||||
return new NativeSearchQueryBuilder().withQuery(matchAllQuery());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value) {
|
||||
return new NativeSearchQueryBuilder().withQuery(termQuery(field, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQueryWithCollapse(String collapseField, @Nullable String innerHits, @Nullable Integer size) {
|
||||
CollapseBuilder collapseBuilder = new CollapseBuilder(collapseField);
|
||||
|
||||
if (innerHits != null) {
|
||||
InnerHitBuilder innerHitBuilder = new InnerHitBuilder(innerHits);
|
||||
|
||||
if (size != null) {
|
||||
innerHitBuilder.setSize(size);
|
||||
}
|
||||
|
||||
collapseBuilder.setInnerHits(innerHitBuilder);
|
||||
}
|
||||
|
||||
return new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withCollapseBuilder(collapseBuilder).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query queryWithIds(String... ids) {
|
||||
return new NativeSearchQueryBuilder().withIds(ids).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <A extends AggregationContainer<?>> void assertThatAggregationsAreCorrect(A aggregationContainer) {
|
||||
Aggregation aggregation = (Aggregation) aggregationContainer.aggregation();
|
||||
assertThat(aggregation.getName()).isEqualTo("messages");
|
||||
assertThat(aggregation instanceof ParsedStringTerms);
|
||||
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
|
||||
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(3);
|
||||
assertThat(parsedStringTerms.getBucketByKey("message").getDocCount()).isEqualTo(3);
|
||||
assertThat(parsedStringTerms.getBucketByKey("some").getDocCount()).isEqualTo(2);
|
||||
assertThat(parsedStringTerms.getBucketByKey("other").getDocCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.lang.Boolean;
|
||||
import java.lang.Integer;
|
||||
import java.lang.Long;
|
||||
import java.lang.Object;
|
||||
import java.net.ConnectException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
@ -40,36 +40,28 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.elasticsearch.index.query.IdsQueryBuilder;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.data.annotation.AccessType;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.NewElasticsearchClientDevelopment;
|
||||
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.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
|
||||
import org.springframework.data.elasticsearch.core.document.Explanation;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasAction;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
|
||||
@ -98,7 +90,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@SuppressWarnings("SpringJavaAutowiredMembersInspection")
|
||||
@SpringIntegrationTest
|
||||
public abstract class ReactiveElasticsearchIntegrationTests implements NewElasticsearchClientDevelopment {
|
||||
public abstract class ReactiveElasticsearchIntegrationTests {
|
||||
|
||||
@Autowired private ReactiveElasticsearchOperations operations;
|
||||
@Autowired private IndexNameProvider indexNameProvider;
|
||||
@ -118,28 +110,18 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
}
|
||||
// endregion
|
||||
|
||||
protected abstract Query getTermsAggsQuery(String aggsName, String aggsField);
|
||||
|
||||
protected abstract BaseQueryBuilder<?, ?> getBuilderWithMatchAllQuery();
|
||||
|
||||
protected abstract BaseQueryBuilder<?, ?> getBuilderWithTermQuery(String field, String value);
|
||||
|
||||
protected abstract Query getQueryWithCollapse(String collapseField, @Nullable String innerHits,
|
||||
@Nullable Integer size);
|
||||
|
||||
protected abstract Query queryWithIds(String... ids);
|
||||
|
||||
// region Tests
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void executeShouldProvideResource() {
|
||||
|
||||
Mono.from(operations.execute(ReactiveElasticsearchClient::ping)) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void executeShouldConvertExceptions() {
|
||||
|
||||
Mono.from(operations.execute(client -> {
|
||||
throw new RuntimeException(new ConnectException("we're doomed"));
|
||||
})) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectError(DataAccessResourceFailureException.class) //
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
public void insertWithIdShouldWork() {
|
||||
@ -157,7 +139,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void insertWithAutogeneratedIdShouldUpdateEntityId() {
|
||||
|
||||
@ -175,7 +156,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
return operations.exists(id, IndexCoordinates.of(index));
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void insertWithExplicitIndexNameShouldOverwriteMetadata() {
|
||||
|
||||
@ -290,7 +270,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-519
|
||||
public void existsShouldReturnFalseWhenIndexDoesNotExist() {
|
||||
|
||||
@ -300,7 +279,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void existsShouldReturnTrueWhenFound() {
|
||||
|
||||
@ -313,7 +291,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void existsShouldReturnFalseWhenNotFound() {
|
||||
|
||||
@ -432,7 +409,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-595, DATAES-767
|
||||
public void shouldThrowDataAccessExceptionWhenInvalidPreferenceForGivenCriteria() {
|
||||
|
||||
@ -445,6 +421,8 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
CriteriaQuery queryWithInvalidPreference = new CriteriaQuery(
|
||||
new Criteria("message").contains("some").and("message").contains("message"));
|
||||
queryWithInvalidPreference.setPreference("_only_nodes:oops");
|
||||
// add a pageable to not use scrolling,otherwise the exception class does not match
|
||||
queryWithInvalidPreference.setPageable(PageRequest.of(0, 10));
|
||||
|
||||
operations.search(queryWithInvalidPreference, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
@ -498,7 +476,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-567
|
||||
public void aggregateShouldReturnAggregations() {
|
||||
|
||||
@ -508,24 +485,17 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
|
||||
index(sampleEntity1, sampleEntity2, sampleEntity3);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.addAggregation(AggregationBuilders.terms("messages").field("message")).build();
|
||||
Query query = getTermsAggsQuery("messages", "message");
|
||||
|
||||
operations.aggregate(query, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(aggregationContainer -> {
|
||||
Aggregation aggregation = ((ElasticsearchAggregation) aggregationContainer).aggregation();
|
||||
assertThat(aggregation.getName()).isEqualTo("messages");
|
||||
assertThat(aggregation instanceof ParsedStringTerms);
|
||||
ParsedStringTerms parsedStringTerms = (ParsedStringTerms) aggregation;
|
||||
assertThat(parsedStringTerms.getBuckets().size()).isEqualTo(3);
|
||||
assertThat(parsedStringTerms.getBucketByKey("message").getDocCount()).isEqualTo(3);
|
||||
assertThat(parsedStringTerms.getBucketByKey("some").getDocCount()).isEqualTo(2);
|
||||
assertThat(parsedStringTerms.getBucketByKey("other").getDocCount()).isEqualTo(1);
|
||||
assertThatAggregationsAreCorrect(aggregationContainer);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
protected abstract <A extends AggregationContainer<?>> void assertThatAggregationsAreCorrect(A aggregationContainer);
|
||||
|
||||
@Test // DATAES-567, DATAES-767
|
||||
public void aggregateShouldErrorWhenIndexDoesNotExist() {
|
||||
operations
|
||||
@ -621,7 +591,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-519
|
||||
public void deleteByQueryShouldReturnZeroWhenIndexDoesNotExist() {
|
||||
|
||||
@ -634,7 +603,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-547
|
||||
public void shouldDeleteAcrossIndex() {
|
||||
|
||||
@ -650,11 +618,9 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
|
||||
operations.indexOps(thisIndex).refresh().then(operations.indexOps(thatIndex).refresh()).block();
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() //
|
||||
.withQuery(termQuery("message", "test")) //
|
||||
.build();
|
||||
Query query = getBuilderWithTermQuery("message", "test").build();
|
||||
|
||||
operations.delete(searchQuery, SampleEntity.class, IndexCoordinates.of(indexPrefix + '*')) //
|
||||
operations.delete(query, SampleEntity.class, IndexCoordinates.of(indexPrefix + '*')) //
|
||||
.map(ByQueryResponse::getDeleted) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(2L) //
|
||||
@ -663,7 +629,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
operations.indexOps(thisIndex).delete().then(operations.indexOps(thatIndex).delete()).block();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-547
|
||||
public void shouldDeleteAcrossIndexWhenNoMatchingDataPresent() {
|
||||
|
||||
@ -679,11 +644,9 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
|
||||
operations.indexOps(thisIndex).refresh().then(operations.indexOps(thatIndex).refresh()).block();
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() //
|
||||
.withQuery(termQuery("message", "negative")) //
|
||||
.build();
|
||||
Query query = getBuilderWithTermQuery("message", "negative").build();
|
||||
|
||||
operations.delete(searchQuery, SampleEntity.class, IndexCoordinates.of(indexPrefix + '*')) //
|
||||
operations.delete(query, SampleEntity.class, IndexCoordinates.of(indexPrefix + '*')) //
|
||||
.map(ByQueryResponse::getDeleted) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(0L) //
|
||||
@ -692,7 +655,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
operations.indexOps(thisIndex).delete().then(operations.indexOps(thatIndex).delete()).block();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void deleteByQueryShouldReturnNumberOfDeletedDocuments() {
|
||||
|
||||
@ -707,7 +669,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-504
|
||||
public void deleteByQueryShouldReturnZeroIfNothingDeleted() {
|
||||
|
||||
@ -722,7 +683,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-593
|
||||
public void shouldReturnDocumentWithCollapsedField() {
|
||||
|
||||
@ -734,28 +694,20 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
entity3.setRate(1);
|
||||
index(entity1, entity2, entity3);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(matchAllQuery()) //
|
||||
.withCollapseField("rate") //
|
||||
.withPageable(PageRequest.of(0, 25)) //
|
||||
.build();
|
||||
|
||||
Query query = getQueryWithCollapse("rate", null, null);
|
||||
operations.search(query, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextCount(2) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test
|
||||
void shouldReturnSortFields() {
|
||||
SampleEntity entity = randomEntity("test message");
|
||||
entity.rate = 42;
|
||||
index(entity);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(matchAllQuery()) //
|
||||
.withSort(new FieldSortBuilder("rate").order(SortOrder.DESC)) //
|
||||
Query query = getBuilderWithMatchAllQuery().withSort(Sort.by(Sort.Direction.DESC, "rate"))
|
||||
.build();
|
||||
|
||||
operations.search(query, SampleEntity.class) //
|
||||
@ -763,12 +715,23 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.consumeNextWith(it -> {
|
||||
List<Object> sortValues = it.getSortValues();
|
||||
assertThat(sortValues).hasSize(1);
|
||||
assertThat(sortValues.get(0)).isEqualTo(42);
|
||||
// old client returns Integer, new ElasticsearchClient String
|
||||
java.lang.Object o = sortValues.get(0);
|
||||
if (o instanceof Integer) {
|
||||
Integer i = (Integer) o;
|
||||
assertThat(o).isInstanceOf(Integer.class).isEqualTo(42);
|
||||
} else if (o instanceof Long) {
|
||||
Long l = (Long) o;
|
||||
assertThat(o).isInstanceOf(Long.class).isEqualTo(42L);
|
||||
} else if (o instanceof String) {
|
||||
assertThat(o).isInstanceOf(String.class).isEqualTo("42");
|
||||
} else {
|
||||
fail("unexpected object type " + o);
|
||||
}
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-623, #1678
|
||||
public void shouldReturnObjectsForGivenIdsUsingMultiGet() {
|
||||
SampleEntity entity1 = randomEntity("test message 1");
|
||||
@ -788,7 +751,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-623
|
||||
public void shouldReturnObjectsForGivenIdsUsingMultiGetWithFields() {
|
||||
SampleEntity entity1 = randomEntity("test message 1");
|
||||
@ -809,7 +771,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-623. #1678
|
||||
public void shouldDoBulkUpdate() {
|
||||
SampleEntity entity1 = randomEntity("test message 1");
|
||||
@ -847,7 +808,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-623
|
||||
void shouldSaveAll() {
|
||||
SampleEntity entity1 = randomEntity("test message 1");
|
||||
@ -858,7 +818,7 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
operations.saveAll(Mono.just(Arrays.asList(entity1, entity2)), IndexCoordinates.of(indexNameProvider.indexName())) //
|
||||
.then().block();
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
Query searchQuery = operations.matchAllQuery();
|
||||
operations.search(searchQuery, SampleEntity.class, IndexCoordinates.of(indexNameProvider.indexName())) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextMatches(hit -> entity1.equals(hit.getContent()) || entity2.equals(hit.getContent())) //
|
||||
@ -891,7 +851,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
assertThat(retrieved.seqNoPrimaryTerm.getPrimaryTerm()).isPositive();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799, #1678
|
||||
void multiGetShouldReturnSeqNoPrimaryTerm() {
|
||||
OptimisticEntity original = new OptimisticEntity();
|
||||
@ -910,7 +869,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
return new NativeSearchQueryBuilder().withIds(singletonList(id)).build();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799
|
||||
void searchShouldReturnSeqNoPrimaryTerm() {
|
||||
OptimisticEntity original = new OptimisticEntity();
|
||||
@ -927,10 +885,9 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
}
|
||||
|
||||
private Query searchQueryForOne(String id) {
|
||||
return new NativeSearchQueryBuilder().withFilter(new IdsQueryBuilder().addIds(id)).build();
|
||||
return queryWithIds(id);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799
|
||||
void shouldThrowOptimisticLockingFailureExceptionWhenConcurrentUpdateOccursOnEntityWithSeqNoPrimaryTermProperty() {
|
||||
OptimisticEntity original = new OptimisticEntity();
|
||||
@ -950,7 +907,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verify();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-799
|
||||
void shouldThrowOptimisticLockingFailureExceptionWhenConcurrentUpdateOccursOnVersionedEntityWithSeqNoPrimaryTermProperty() {
|
||||
OptimisticAndVersionedEntity original = new OptimisticAndVersionedEntity();
|
||||
@ -979,7 +935,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
operations.save(forEdit).as(StepVerifier::create).expectNextCount(1).verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // DATAES-909
|
||||
void shouldDoUpdate() {
|
||||
SampleEntity entity = randomEntity("test message");
|
||||
@ -1119,7 +1074,6 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973 still needs implementation
|
||||
@Test // #1646, #1718
|
||||
@DisplayName("should return a list of info for specific index")
|
||||
void shouldReturnInformationListOfAllIndices() {
|
||||
@ -1204,6 +1158,23 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #2230
|
||||
@DisplayName("should work with readonly id")
|
||||
void shouldWorkWithReadonlyId() {
|
||||
|
||||
ReadonlyIdEntity entity = new ReadonlyIdEntity();
|
||||
entity.setPart1("foo");
|
||||
entity.setPart2("bar");
|
||||
|
||||
operations.save(entity).block();
|
||||
|
||||
operations.get(entity.getId(), ReadonlyIdEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(readEntity -> { //
|
||||
assertThat(readEntity.getPart1()).isEqualTo(entity.getPart1()); //
|
||||
assertThat(readEntity.getPart2()).isEqualTo(entity.getPart2()); //
|
||||
}).verifyComplete();
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Helper functions
|
||||
@ -1537,5 +1508,35 @@ public abstract class ReactiveElasticsearchIntegrationTests implements NewElasti
|
||||
+ seqNoPrimaryTerm + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-readonly-id")
|
||||
static class ReadonlyIdEntity {
|
||||
@Field(type = FieldType.Keyword) private String part1;
|
||||
|
||||
@Field(type = FieldType.Keyword) private String part2;
|
||||
|
||||
@Id
|
||||
@ReadOnlyProperty
|
||||
@AccessType(AccessType.Type.PROPERTY)
|
||||
public String getId() {
|
||||
return part1 + '-' + part2;
|
||||
}
|
||||
|
||||
public String getPart1() {
|
||||
return part1;
|
||||
}
|
||||
|
||||
public void setPart1(String part1) {
|
||||
this.part1 = part1;
|
||||
}
|
||||
|
||||
public String getPart2() {
|
||||
return part2;
|
||||
}
|
||||
|
||||
public void setPart2(String part2) {
|
||||
this.part2 = part2;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ public class ReactiveElasticsearchTemplateUnitTests {
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.IMMEDIATE);
|
||||
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.NONE);
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
@ -170,7 +170,7 @@ public class ReactiveElasticsearchTemplateUnitTests {
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.IMMEDIATE);
|
||||
assertThat(captor.getValue().getRefreshPolicy()).isEqualTo(WriteRequest.RefreshPolicy.NONE);
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
@ -198,7 +198,7 @@ public class ReactiveElasticsearchTemplateUnitTests {
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(captor.getValue().isRefresh()).isTrue();
|
||||
assertThat(captor.getValue().isRefresh()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
@ -207,13 +207,13 @@ public class ReactiveElasticsearchTemplateUnitTests {
|
||||
ArgumentCaptor<DeleteByQueryRequest> captor = ArgumentCaptor.forClass(DeleteByQueryRequest.class);
|
||||
when(client.deleteBy(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
template.setRefreshPolicy(RefreshPolicy.NONE);
|
||||
template.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
|
||||
|
||||
template.delete(new StringQuery(QueryBuilders.matchAllQuery().toString()), Object.class, index) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(captor.getValue().isRefresh()).isFalse();
|
||||
assertThat(captor.getValue().isRefresh()).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAES-504
|
||||
|
@ -116,6 +116,42 @@ class RequestFactoryTests {
|
||||
assertThat(searchRequest.source().from()).isEqualTo(30);
|
||||
}
|
||||
|
||||
@Test // DATAES-227
|
||||
public void shouldUseUpsertOnUpdate() {
|
||||
|
||||
// given
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("id", "1");
|
||||
doc.put("message", "test");
|
||||
|
||||
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
|
||||
.from(doc);
|
||||
|
||||
UpdateQuery updateQuery = UpdateQuery.builder("1") //
|
||||
.withDocument(document) //
|
||||
.withUpsert(document) //
|
||||
.build();
|
||||
|
||||
// when
|
||||
UpdateRequest request = requestFactory.updateRequest(updateQuery, IndexCoordinates.of("index"));
|
||||
|
||||
// then
|
||||
assertThat(request).isNotNull();
|
||||
assertThat(request.upsertRequest()).isNotNull();
|
||||
}
|
||||
|
||||
@Test // #2287
|
||||
@DisplayName("should create update query when script language is not set")
|
||||
void shouldCreateUpdateQueryWhenScriptTypeIsSetToNull() {
|
||||
|
||||
UpdateQuery updateQuery = UpdateQuery.builder("1") //
|
||||
.withScript("script").build();
|
||||
|
||||
UpdateRequest request = requestFactory.updateRequest(updateQuery, IndexCoordinates.of("index"));
|
||||
|
||||
assertThat(request).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATAES-693
|
||||
public void shouldReturnSourceWhenRequested() {
|
||||
// given
|
||||
|
@ -21,11 +21,15 @@ import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.StatsBucketAggregate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.ELCQueries;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
import org.springframework.data.elasticsearch.client.elc.QueryBuilders;
|
||||
@ -55,18 +59,17 @@ public class AggregationELCIntegrationTests extends AggregationIntegrationTests
|
||||
|
||||
@Override
|
||||
protected Query getTermsAggsQuery(String aggsName, String aggsField) {
|
||||
return NativeQuery.builder() //
|
||||
.withQuery(QueryBuilders.matchAllQueryAsQuery()) //
|
||||
.withAggregation(aggsName, Aggregation.of(a -> a //
|
||||
.terms(ta -> ta.field(aggsField)))) //
|
||||
.withMaxResults(0) //
|
||||
.build();
|
||||
return ELCQueries.getTermsAggsQuery(aggsName, aggsField);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertThatAggsHasResult(AggregationsContainer<?> aggregationsContainer, String aggsName) {
|
||||
Map<String, Aggregate> aggregations = ((ElasticsearchAggregations) aggregationsContainer).aggregations();
|
||||
assertThat(aggregations).containsKey(aggsName);
|
||||
List<ElasticsearchAggregation> aggregations = ((ElasticsearchAggregations) aggregationsContainer).aggregations();
|
||||
List<String> aggNames = aggregations.stream() //
|
||||
.map(ElasticsearchAggregation::aggregation) //
|
||||
.map(org.springframework.data.elasticsearch.client.elc.Aggregation::getName) //
|
||||
.collect(Collectors.toList());
|
||||
assertThat(aggNames).contains(aggsName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,9 +87,13 @@ public class AggregationELCIntegrationTests extends AggregationIntegrationTests
|
||||
@Override
|
||||
protected void assertThatPipelineAggsAreCorrect(AggregationsContainer<?> aggregationsContainer, String aggsName,
|
||||
String pipelineAggsName) {
|
||||
Map<String, Aggregate> aggregations = ((ElasticsearchAggregations) aggregationsContainer).aggregations();
|
||||
assertThat(aggregations).containsKey(aggsName);
|
||||
Aggregate aggregate = aggregations.get(pipelineAggsName);
|
||||
Map<String, Aggregate> aggregates = ((ElasticsearchAggregations) aggregationsContainer).aggregations().stream() //
|
||||
.map(ElasticsearchAggregation::aggregation) //
|
||||
.collect(Collectors.toMap(org.springframework.data.elasticsearch.client.elc.Aggregation::getName,
|
||||
org.springframework.data.elasticsearch.client.elc.Aggregation::getAggregate));
|
||||
|
||||
assertThat(aggregates).containsKey(aggsName);
|
||||
Aggregate aggregate = aggregates.get(pipelineAggsName);
|
||||
assertThat(aggregate.isStatsBucket()).isTrue();
|
||||
StatsBucketAggregate statsBucketAggregate = aggregate.statsBucket();
|
||||
assertThat(statsBucketAggregate.min()).isEqualTo(1.0);
|
||||
|
@ -234,7 +234,7 @@ public abstract class AggregationIntegrationTests {
|
||||
*/
|
||||
static class ArticleEntityBuilder {
|
||||
|
||||
private ArticleEntity result;
|
||||
private final ArticleEntity result;
|
||||
|
||||
public ArticleEntityBuilder(String id) {
|
||||
result = new ArticleEntity(id);
|
||||
|
@ -33,7 +33,10 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@ -623,7 +626,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(target.address).isEqualTo(bigBunsCafe);
|
||||
}
|
||||
|
||||
@Test // DATAES-716
|
||||
@Test
|
||||
// DATAES-716
|
||||
void shouldWriteLocalDate() throws JSONException {
|
||||
Person person = new Person();
|
||||
person.setId("4711");
|
||||
@ -665,7 +669,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertEquals(expected, json, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-716
|
||||
@Test
|
||||
// DATAES-716
|
||||
void shouldReadLocalDate() {
|
||||
Document document = Document.create();
|
||||
document.put("id", "4711");
|
||||
@ -695,7 +700,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(entity.getDates()).hasSize(2).containsExactly(LocalDate.of(2020, 9, 15), LocalDate.of(2019, 5, 1));
|
||||
}
|
||||
|
||||
@Test // DATAES-763
|
||||
@Test
|
||||
// DATAES-763
|
||||
void writeEntityWithMapDataType() {
|
||||
|
||||
Notification notification = new Notification();
|
||||
@ -712,7 +718,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(document).isEqualTo(notificationAsMap);
|
||||
}
|
||||
|
||||
@Test // DATAES-763
|
||||
@Test
|
||||
// DATAES-763
|
||||
void readEntityWithMapDataType() {
|
||||
|
||||
Document document = Document.create();
|
||||
@ -729,7 +736,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(notification.params.get("content")).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAES-795
|
||||
@Test
|
||||
// DATAES-795
|
||||
void readGenericMapWithSimpleTypes() {
|
||||
Map<String, Object> mapWithSimpleValues = new HashMap<>();
|
||||
mapWithSimpleValues.put("int", 1);
|
||||
@ -743,7 +751,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(wrapper.getSchemaLessObject()).isEqualTo(mapWithSimpleValues);
|
||||
}
|
||||
|
||||
@Test // DATAES-797
|
||||
@Test
|
||||
// DATAES-797
|
||||
void readGenericListWithMaps() {
|
||||
Map<String, Object> simpleMap = new HashMap<>();
|
||||
simpleMap.put("int", 1);
|
||||
@ -761,7 +770,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(wrapper.getSchemaLessObject()).isEqualTo(mapWithSimpleList);
|
||||
}
|
||||
|
||||
@Test // DATAES-799
|
||||
@Test
|
||||
// DATAES-799
|
||||
void shouldNotWriteSeqNoPrimaryTermProperty() {
|
||||
EntityWithSeqNoPrimaryTerm entity = new EntityWithSeqNoPrimaryTerm();
|
||||
entity.seqNoPrimaryTerm = new SeqNoPrimaryTerm(1L, 2L);
|
||||
@ -772,7 +782,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(document).doesNotContainKey("seqNoPrimaryTerm");
|
||||
}
|
||||
|
||||
@Test // DATAES-799
|
||||
@Test
|
||||
// DATAES-799
|
||||
void shouldNotReadSeqNoPrimaryTermProperty() {
|
||||
Document document = Document.create().append("seqNoPrimaryTerm", emptyMap());
|
||||
|
||||
@ -781,7 +792,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(entity.seqNoPrimaryTerm).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAES-845
|
||||
@Test
|
||||
// DATAES-845
|
||||
void shouldWriteCollectionsWithNullValues() throws JSONException {
|
||||
EntityWithListProperty entity = new EntityWithListProperty();
|
||||
entity.setId("42");
|
||||
@ -798,7 +810,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertEquals(expected, json, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-857
|
||||
@Test
|
||||
// DATAES-857
|
||||
void shouldWriteEntityWithListOfGeoPoints() throws JSONException {
|
||||
|
||||
GeoPointListEntity entity = new GeoPointListEntity();
|
||||
@ -827,7 +840,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertEquals(expected, json, false);
|
||||
}
|
||||
|
||||
@Test // DATAES-857
|
||||
@Test
|
||||
// DATAES-857
|
||||
void shouldReadEntityWithListOfGeoPoints() {
|
||||
|
||||
String json = "{\n" + //
|
||||
@ -852,7 +866,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(entity.locations).containsExactly(new GeoPoint(12.34, 23.45), new GeoPoint(34.56, 45.67));
|
||||
}
|
||||
|
||||
@Test // DATAES-865
|
||||
@Test
|
||||
// DATAES-865
|
||||
void shouldWriteEntityWithMapAsObject() throws JSONException {
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
@ -1509,6 +1524,305 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
mappingElasticsearchConverter.updateQuery(query, EntityWithCustomValueConverters.class);
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single String into a List property")
|
||||
void shouldReadASingleStringIntoAListProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringList\": \"foo\"\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getStringList()).containsExactly("foo");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a String array into a List property")
|
||||
void shouldReadAStringArrayIntoAListProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringList\": [\"foo\", \"bar\"]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getStringList()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single String into a Set property")
|
||||
void shouldReadASingleStringIntoASetProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringSet\": \"foo\"\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getStringSet()).containsExactly("foo");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a String array into a Set property")
|
||||
void shouldReadAStringArrayIntoASetProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringSet\": [\n" + //
|
||||
" \"foo\",\n" + //
|
||||
" \"bar\"\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getStringSet()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single object into a List property")
|
||||
void shouldReadASingleObjectIntoAListProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenList\": {\n" + //
|
||||
" \"name\": \"child\"\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getChildrenList()).hasSize(1);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenList().get(0).getName()).isEqualTo("child");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read an object array into a List property")
|
||||
void shouldReadAnObjectArrayIntoAListProperty() {
|
||||
|
||||
String json = " {\n" + //
|
||||
" \"childrenList\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child1\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child2\"\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
" }\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getChildrenList()).hasSize(2);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenList().get(0).getName()).isEqualTo("child1");
|
||||
assertThat(entity.getChildrenList().get(1).getName()).isEqualTo("child2");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single object into a Set property")
|
||||
void shouldReadASingleObjectIntoASetProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenSet\": {\n" + //
|
||||
" \"name\": \"child\"\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getChildrenSet()).hasSize(1);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenSet().iterator().next().getName()).isEqualTo("child");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read an object array into a Set property")
|
||||
void shouldReadAnObjectArrayIntoASetProperty() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenSet\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child1\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child2\"\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
EntityWithCollections entity = mappingElasticsearchConverter.read(EntityWithCollections.class, source);
|
||||
|
||||
assertThat(entity.getChildrenSet()).hasSize(2);
|
||||
// noinspection ConstantConditions
|
||||
List<String> names = entity.getChildrenSet().stream().map(EntityWithCollections.Child::getName)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(names).containsExactlyInAnyOrder("child1", "child2");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single String into a List property immutable")
|
||||
void shouldReadASingleStringIntoAListPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringList\": \"foo\"\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getStringList()).containsExactly("foo");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a String array into a List property immutable")
|
||||
void shouldReadAStringArrayIntoAListPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringList\": [\n" + //
|
||||
" \"foo\",\n" + //
|
||||
" \"bar\"\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getStringList()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single String into a Set property immutable")
|
||||
void shouldReadASingleStringIntoASetPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringSet\": \"foo\"\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getStringSet()).containsExactly("foo");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a String array into a Set property immutable")
|
||||
void shouldReadAStringArrayIntoASetPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"stringSet\": [\n" + //
|
||||
" \"foo\",\n" + //
|
||||
" \"bar\"\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getStringSet()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single object into a List property immutable")
|
||||
void shouldReadASingleObjectIntoAListPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenList\": {\n" + //
|
||||
" \"name\": \"child\"\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getChildrenList()).hasSize(1);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenList().get(0).getName()).isEqualTo("child");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read an object array into a List property immutable")
|
||||
void shouldReadAnObjectArrayIntoAListPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenList\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child1\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child2\"\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getChildrenList()).hasSize(2);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenList().get(0).getName()).isEqualTo("child1");
|
||||
assertThat(entity.getChildrenList().get(1).getName()).isEqualTo("child2");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read a single object into a Set property immutable")
|
||||
void shouldReadASingleObjectIntoASetPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenSet\": {\n" + //
|
||||
" \"name\": \"child\"\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getChildrenSet()).hasSize(1);
|
||||
// noinspection ConstantConditions
|
||||
assertThat(entity.getChildrenSet().iterator().next().getName()).isEqualTo("child");
|
||||
}
|
||||
|
||||
@Test // #2280
|
||||
@DisplayName("should read an object array into a Set property immutable")
|
||||
void shouldReadAnObjectArrayIntoASetPropertyImmutable() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"childrenSet\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child1\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"name\": \"child2\"\n" + //
|
||||
" }\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
Document source = Document.parse(json);
|
||||
|
||||
ImmutableEntityWithCollections entity = mappingElasticsearchConverter.read(ImmutableEntityWithCollections.class,
|
||||
source);
|
||||
|
||||
assertThat(entity.getChildrenSet()).hasSize(2);
|
||||
// noinspection ConstantConditions
|
||||
List<String> names = entity.getChildrenSet().stream().map(ImmutableEntityWithCollections.Child::getName)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(names).containsExactlyInAnyOrder("child1", "child2");
|
||||
}
|
||||
|
||||
private Map<String, Object> writeToMap(Object source) {
|
||||
|
||||
Document sink = Document.create();
|
||||
@ -1678,34 +1992,46 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Person person = (Person) o;
|
||||
|
||||
if (id != null ? !id.equals(person.id) : person.id != null)
|
||||
if (id != null ? !id.equals(person.id) : person.id != null) {
|
||||
return false;
|
||||
if (name != null ? !name.equals(person.name) : person.name != null)
|
||||
}
|
||||
if (name != null ? !name.equals(person.name) : person.name != null) {
|
||||
return false;
|
||||
if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null)
|
||||
}
|
||||
if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) {
|
||||
return false;
|
||||
if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null)
|
||||
}
|
||||
if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) {
|
||||
return false;
|
||||
if (birthDate != null ? !birthDate.equals(person.birthDate) : person.birthDate != null)
|
||||
}
|
||||
if (birthDate != null ? !birthDate.equals(person.birthDate) : person.birthDate != null) {
|
||||
return false;
|
||||
if (gender != person.gender)
|
||||
}
|
||||
if (gender != person.gender) {
|
||||
return false;
|
||||
if (address != null ? !address.equals(person.address) : person.address != null)
|
||||
}
|
||||
if (address != null ? !address.equals(person.address) : person.address != null) {
|
||||
return false;
|
||||
if (coWorkers != null ? !coWorkers.equals(person.coWorkers) : person.coWorkers != null)
|
||||
}
|
||||
if (coWorkers != null ? !coWorkers.equals(person.coWorkers) : person.coWorkers != null) {
|
||||
return false;
|
||||
if (inventoryList != null ? !inventoryList.equals(person.inventoryList) : person.inventoryList != null)
|
||||
}
|
||||
if (inventoryList != null ? !inventoryList.equals(person.inventoryList) : person.inventoryList != null) {
|
||||
return false;
|
||||
}
|
||||
if (shippingAddresses != null ? !shippingAddresses.equals(person.shippingAddresses)
|
||||
: person.shippingAddresses != null)
|
||||
: person.shippingAddresses != null) {
|
||||
return false;
|
||||
}
|
||||
return inventoryMap != null ? inventoryMap.equals(person.inventoryMap) : person.inventoryMap == null;
|
||||
}
|
||||
|
||||
@ -1792,15 +2118,18 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Gun gun = (Gun) o;
|
||||
|
||||
if (shotsPerMagazine != gun.shotsPerMagazine)
|
||||
if (shotsPerMagazine != gun.shotsPerMagazine) {
|
||||
return false;
|
||||
}
|
||||
return label.equals(gun.label);
|
||||
}
|
||||
|
||||
@ -1826,10 +2155,12 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (!(o instanceof Grenade))
|
||||
}
|
||||
if (!(o instanceof Grenade)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Grenade grenade = (Grenade) o;
|
||||
|
||||
@ -1862,17 +2193,21 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (!(o instanceof Rifle))
|
||||
}
|
||||
if (!(o instanceof Rifle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Rifle rifle = (Rifle) o;
|
||||
|
||||
if (Double.compare(rifle.weight, weight) != 0)
|
||||
if (Double.compare(rifle.weight, weight) != 0) {
|
||||
return false;
|
||||
if (maxShotsPerMagazine != rifle.maxShotsPerMagazine)
|
||||
}
|
||||
if (maxShotsPerMagazine != rifle.maxShotsPerMagazine) {
|
||||
return false;
|
||||
}
|
||||
return label.equals(rifle.label);
|
||||
}
|
||||
|
||||
@ -1903,10 +2238,12 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (!(o instanceof ShotGun))
|
||||
}
|
||||
if (!(o instanceof ShotGun)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ShotGun shotGun = (ShotGun) o;
|
||||
|
||||
@ -1953,17 +2290,21 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (!(o instanceof Address))
|
||||
}
|
||||
if (!(o instanceof Address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Address address = (Address) o;
|
||||
|
||||
if (location != null ? !location.equals(address.location) : address.location != null)
|
||||
if (location != null ? !location.equals(address.location) : address.location != null) {
|
||||
return false;
|
||||
if (street != null ? !street.equals(address.street) : address.street != null)
|
||||
}
|
||||
if (street != null ? !street.equals(address.street) : address.street != null) {
|
||||
return false;
|
||||
}
|
||||
return city != null ? city.equals(address.city) : address.city == null;
|
||||
}
|
||||
|
||||
@ -1990,10 +2331,12 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
if (this == o) {
|
||||
return true;
|
||||
if (!(o instanceof Place))
|
||||
}
|
||||
if (!(o instanceof Place)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Place place = (Place) o;
|
||||
|
||||
@ -2462,6 +2805,128 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
return reverse(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static class EntityWithCollections {
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private List<String> stringList;
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private Set<String> stringSet;
|
||||
|
||||
@Field(type = FieldType.Object)
|
||||
@Nullable private List<Child> childrenList;
|
||||
|
||||
@Field(type = FieldType.Object)
|
||||
@Nullable private Set<Child> childrenSet;
|
||||
|
||||
@Nullable
|
||||
public List<String> getStringList() {
|
||||
return stringList;
|
||||
}
|
||||
|
||||
public void setStringList(@Nullable List<String> stringList) {
|
||||
this.stringList = stringList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<String> getStringSet() {
|
||||
return stringSet;
|
||||
}
|
||||
|
||||
public void setStringSet(@Nullable Set<String> stringSet) {
|
||||
this.stringSet = stringSet;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Child> getChildrenList() {
|
||||
return childrenList;
|
||||
}
|
||||
|
||||
public void setChildrenList(@Nullable List<Child> childrenList) {
|
||||
this.childrenList = childrenList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<Child> getChildrenSet() {
|
||||
return childrenSet;
|
||||
}
|
||||
|
||||
public void setChildrenSet(@Nullable Set<Child> childrenSet) {
|
||||
this.childrenSet = childrenSet;
|
||||
}
|
||||
|
||||
public static class Child {
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private String name;
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ImmutableEntityWithCollections {
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private List<String> stringList;
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private Set<String> stringSet;
|
||||
|
||||
@Field(type = FieldType.Object)
|
||||
@Nullable private List<Child> childrenList;
|
||||
|
||||
@Field(type = FieldType.Object)
|
||||
@Nullable private Set<Child> childrenSet;
|
||||
|
||||
public ImmutableEntityWithCollections(@Nullable List<String> stringList, @Nullable Set<String> stringSet,
|
||||
@Nullable List<Child> childrenList, @Nullable Set<Child> childrenSet) {
|
||||
this.stringList = stringList;
|
||||
this.stringSet = stringSet;
|
||||
this.childrenList = childrenList;
|
||||
this.childrenSet = childrenSet;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getStringList() {
|
||||
return stringList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<String> getStringSet() {
|
||||
return stringSet;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Child> getChildrenList() {
|
||||
return childrenList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<Child> getChildrenSet() {
|
||||
return childrenSet;
|
||||
}
|
||||
|
||||
public static class Child {
|
||||
|
||||
@Field(type = FieldType.Keyword)
|
||||
@Nullable private String name;
|
||||
|
||||
public Child(@Nullable String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
private static String reverse(Object o) {
|
||||
|
@ -241,7 +241,7 @@ abstract class CallbackIntegrationTests {
|
||||
@Id private String id;
|
||||
@Nullable private String text;
|
||||
|
||||
@ReadOnlyProperty
|
||||
// @ReadOnlyProperty
|
||||
@Nullable private String className;
|
||||
|
||||
@Nullable
|
||||
|
@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
|
||||
@ -119,7 +118,6 @@ public abstract class ReactiveCallbackIntegrationTests {
|
||||
@Id private String id;
|
||||
private String text;
|
||||
|
||||
@ReadOnlyProperty
|
||||
@Nullable private String className;
|
||||
|
||||
public SampleEntity(String id, String text) {
|
||||
|
@ -116,7 +116,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
|
||||
operations.bulkIndex(indexQueries, AnnotatedCompletionEntity.class);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973, ES issue 150
|
||||
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
|
||||
@Test
|
||||
public void shouldFindSuggestionsForGivenCriteriaQueryUsingCompletionEntity() {
|
||||
|
||||
@ -148,7 +148,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
|
||||
operations.get("1", CompletionEntity.class);
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973, ES issue 150
|
||||
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
|
||||
@Test
|
||||
public void shouldFindSuggestionsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
|
||||
|
||||
@ -172,7 +172,7 @@ public abstract class CompletionIntegrationTests implements NewElasticsearchClie
|
||||
assertThat(options.get(1).getText()).isIn("Marchand", "Mohsin");
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973, ES issue 150
|
||||
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES 1issue 150")
|
||||
@Test
|
||||
public void shouldFindSuggestionsWithWeightsForGivenCriteriaQueryUsingAnnotatedCompletionEntity() {
|
||||
|
||||
|
@ -67,7 +67,7 @@ public abstract class ReactiveSuggestIntegrationTests implements NewElasticsearc
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
|
||||
}
|
||||
|
||||
@DisabledIf("newElasticsearchClient") // todo #1973, ES issue 150
|
||||
@DisabledIf(value = "newElasticsearchClient", disabledReason = "todo #2139, ES issue 150")
|
||||
@Test // #1302
|
||||
@DisplayName("should find suggestions for given prefix completion")
|
||||
void shouldFindSuggestionsForGivenPrefixCompletion() {
|
||||
|
@ -144,10 +144,37 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri
|
||||
.isEqualTo("{ 'bool' : { 'must' : { 'term' : { 'car' : 'Toyota-Prius' } } } }");
|
||||
}
|
||||
|
||||
@Test // #2135
|
||||
@DisplayName("should handle array-of-strings parameters correctly")
|
||||
void shouldHandleArrayOfStringsParametersCorrectly() throws Exception {
|
||||
|
||||
List<String> otherNames = Arrays.asList("Wesley", "Emmett");
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByOtherNames", otherNames);
|
||||
|
||||
assertThat(query).isInstanceOf(StringQuery.class);
|
||||
assertThat(((StringQuery) query).getSource())
|
||||
.isEqualTo("{ 'bool' : { 'must' : { 'terms' : { 'otherNames' : [\"Wesley\",\"Emmett\"] } } } }");
|
||||
}
|
||||
|
||||
@Test // #2135
|
||||
@DisplayName("should handle array-of-Integers parameters correctly")
|
||||
void shouldHandleArrayOfIntegerParametersCorrectly() throws Exception {
|
||||
|
||||
List<Integer> ages = Arrays.asList(42, 57);
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByAges", ages);
|
||||
|
||||
assertThat(query).isInstanceOf(StringQuery.class);
|
||||
assertThat(((StringQuery) query).getSource())
|
||||
.isEqualTo("{ 'bool' : { 'must' : { 'terms' : { 'ages' : [42,57] } } } }");
|
||||
}
|
||||
|
||||
private org.springframework.data.elasticsearch.core.query.Query createQuery(String methodName, Object... args)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass)
|
||||
.map(clazz -> Collection.class.isAssignableFrom(clazz) ? List.class : clazz).toArray(Class[]::new);
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(methodName, argTypes);
|
||||
ReactiveElasticsearchStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
|
||||
|
||||
@ -195,6 +222,12 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri
|
||||
@Query("{ 'bool' : { 'must' : { 'term' : { 'car' : '?0' } } } }")
|
||||
Mono<Person> findByCar(Car car);
|
||||
|
||||
@Query("{ 'bool' : { 'must' : { 'terms' : { 'otherNames' : ?0 } } } }")
|
||||
Flux<Person> findByOtherNames(List<String> otherNames);
|
||||
|
||||
@Query("{ 'bool' : { 'must' : { 'terms' : { 'ages' : ?0 } } } }")
|
||||
Flux<Person> findByAges(List<Integer> ages);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,6 +244,8 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri
|
||||
|
||||
@Nullable private String name;
|
||||
|
||||
@Nullable private List<String> otherNames;
|
||||
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested) private List<Car> car;
|
||||
|
||||
@ -235,6 +270,15 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getOtherNames() {
|
||||
return otherNames;
|
||||
}
|
||||
|
||||
public void setOtherNames(List<String> otherNames) {
|
||||
this.otherNames = otherNames;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Car> getCar() {
|
||||
return car;
|
||||
|
@ -310,6 +310,17 @@ abstract class QueryKeywordsIntegrationTests {
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test // #2162
|
||||
@DisplayName("should run exists query")
|
||||
void shouldRunExistsQuery() {
|
||||
|
||||
Boolean existsCaneSugar = repository.existsByText("Cane sugar");
|
||||
Boolean existsSand = repository.existsByText("Sand");
|
||||
|
||||
assertThat(existsCaneSugar).isTrue();
|
||||
assertThat(existsSand).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class Product {
|
||||
@ -452,6 +463,8 @@ abstract class QueryKeywordsIntegrationTests {
|
||||
SearchHits<Product> findByNameEmpty();
|
||||
|
||||
SearchHits<Product> findByNameNotEmpty();
|
||||
|
||||
Boolean existsByText(String text);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,8 +19,11 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.lang.Boolean;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
@ -121,6 +124,21 @@ public abstract class ReactiveQueryKeywordsIntegrationTests {
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #2162
|
||||
@DisplayName("should run exists query")
|
||||
void shouldRunExistsQuery() {
|
||||
|
||||
loadEntities();
|
||||
repository.existsByMessage("message") //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true) //
|
||||
.verifyComplete();
|
||||
repository.existsByMessage("without") //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(false) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@SuppressWarnings("SpringDataMethodInconsistencyInspection")
|
||||
interface SampleRepository extends ReactiveElasticsearchRepository<SampleEntity, String> {
|
||||
Flux<SearchHit<SampleEntity>> findByMessageExists();
|
||||
@ -132,6 +150,8 @@ public abstract class ReactiveQueryKeywordsIntegrationTests {
|
||||
Flux<SearchHit<SampleEntity>> findByMessageIsNotEmpty();
|
||||
|
||||
Flux<SearchHit<SampleEntity>> findByMessageIsEmpty();
|
||||
|
||||
Mono<Boolean> existsByMessage(String message);
|
||||
}
|
||||
|
||||
private void loadEntities() {
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
@ContextConfiguration(classes = {ElasticsearchRepositoryELCIntegrationTests.Config.class })
|
||||
public class ElasticsearchRepositoryELCIntegrationTests extends ElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ElasticsearchTemplateConfiguration.class })
|
||||
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repository.support" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("repository");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
@ContextConfiguration(classes = {ElasticsearchRepositoryERHLCIntegrationTests.Config.class })
|
||||
public class ElasticsearchRepositoryERHLCIntegrationTests extends ElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ElasticsearchRestTemplateConfiguration.class })
|
||||
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repository.support" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("repository-es7");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -17,12 +17,9 @@ package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
import static org.springframework.data.elasticsearch.utils.IdGenerator.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.Long;
|
||||
import java.lang.Object;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -30,12 +27,10 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
@ -45,18 +40,16 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
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.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.IndexOperations;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexInitializer;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
@ -68,29 +61,22 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Murali Chevuri
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
@ContextConfiguration(classes = { SimpleElasticsearchRepositoryIntegrationTests.Config.class })
|
||||
class SimpleElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ElasticsearchRestTemplateConfiguration.class })
|
||||
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.repository.support" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config {}
|
||||
abstract class ElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Autowired private SampleElasticsearchRepository repository;
|
||||
|
||||
@Autowired private ElasticsearchOperations operations;
|
||||
private IndexOperations indexOperations;
|
||||
@Autowired private IndexNameProvider indexNameProvider;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
indexOperations = operations.indexOps(SampleEntity.class);
|
||||
IndexInitializer.init(indexOperations);
|
||||
indexNameProvider.increment();
|
||||
operations.indexOps(SampleEntity.class).createWithMapping();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void after() {
|
||||
indexOperations.delete();
|
||||
@Test
|
||||
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
|
||||
public void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -369,7 +355,7 @@ class SimpleElasticsearchRepositoryIntegrationTests {
|
||||
repository.deleteAllById(Arrays.asList(id1, id3));
|
||||
|
||||
// then
|
||||
assertThat(repository.findAll()).extracting(SampleEntity::getId).containsExactly(id2);
|
||||
Assertions.assertThat(repository.findAll()).extracting(SampleEntity::getId).containsExactly(id2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -539,8 +525,8 @@ class SimpleElasticsearchRepositoryIntegrationTests {
|
||||
repository.deleteAll(sampleEntities);
|
||||
|
||||
// then
|
||||
assertThat(repository.findById(documentId1)).isNotPresent();
|
||||
assertThat(repository.findById(documentId2)).isNotPresent();
|
||||
Assertions.assertThat(repository.findById(documentId1)).isNotPresent();
|
||||
Assertions.assertThat(repository.findById(documentId2)).isNotPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -677,14 +663,14 @@ class SimpleElasticsearchRepositoryIntegrationTests {
|
||||
return sampleEntities;
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-sample-simple-repository")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class SampleEntity {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
@Field(type = FieldType.Text, store = true, fielddata = true) private String type;
|
||||
@Nullable
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
@Field(type = FieldType.Text, store = true, fielddata = true) private String message;
|
||||
@Nullable private int rate;
|
||||
@Nullable private boolean available;
|
||||
@Nullable
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryELCIntegrationTests.Config.class })
|
||||
public class SimpleReactiveElasticsearchRepositoryELCIntegrationTests
|
||||
extends SimpleReactiveElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
|
||||
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("simple-reactive-repository");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryERHLCIntegrationTests.Config.class })
|
||||
public class SimpleReactiveElasticsearchRepositoryERHLCIntegrationTests
|
||||
extends SimpleReactiveElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
|
||||
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("simple-reactive-repository-es7");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,51 +16,44 @@
|
||||
package org.springframework.data.elasticsearch.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
|
||||
import static org.springframework.data.elasticsearch.core.query.Query.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.lang.Boolean;
|
||||
import java.lang.Long;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Version;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.elasticsearch.RestStatusException;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.annotations.CountQuery;
|
||||
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.Highlight;
|
||||
import org.springframework.data.elasticsearch.annotations.HighlightField;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
@ -68,31 +61,30 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
@ContextConfiguration(classes = { SimpleReactiveElasticsearchRepositoryTests.Config.class })
|
||||
class SimpleReactiveElasticsearchRepositoryTests {
|
||||
abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
|
||||
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {}
|
||||
@Autowired
|
||||
ReactiveElasticsearchOperations operations;
|
||||
@Autowired
|
||||
ReactiveSampleEntityRepository repository;
|
||||
|
||||
static final String INDEX = "test-index-sample-simple-reactive";
|
||||
|
||||
@Autowired ReactiveElasticsearchOperations operations;
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
@Autowired ReactiveSampleEntityRepository repository;
|
||||
@Autowired
|
||||
private IndexNameProvider indexNameProvider;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
|
||||
void before() {
|
||||
indexNameProvider.increment();
|
||||
operations.indexOps(SampleEntity.class).createWithMapping().block();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void after() {
|
||||
operations.indexOps(IndexCoordinates.of(INDEX)).delete().block();
|
||||
@Test
|
||||
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
|
||||
public void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void saveShouldSaveSingleEntity() {
|
||||
|
||||
repository.save(new SampleEntity()) //
|
||||
@ -103,10 +95,11 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
}
|
||||
|
||||
private Mono<Boolean> documentWithIdExistsInIndex(String id) {
|
||||
return operations.exists(id, IndexCoordinates.of(INDEX));
|
||||
return operations.exists(id, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void saveShouldComputeMultipleEntities() {
|
||||
|
||||
repository.saveAll(Arrays.asList(new SampleEntity(), new SampleEntity(), new SampleEntity()))
|
||||
@ -119,46 +112,53 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519, DATAES-767, DATAES-822
|
||||
@Test
|
||||
// DATAES-519, DATAES-767, DATAES-822
|
||||
void findByIdShouldErrorIfIndexDoesNotExist() {
|
||||
|
||||
operations.indexOps(SampleEntity.class).delete().block();
|
||||
repository.findById("id-two") //
|
||||
.as(StepVerifier::create) //
|
||||
.expectError(RestStatusException.class);
|
||||
.expectError(NoSuchIndexException.class) //
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void findShouldRetrieveSingleEntityById() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two"), //
|
||||
new SampleEntity("id-three")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findById("id-two").as(StepVerifier::create)//
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-two")) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void findByIdShouldCompleteIfNothingFound() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two"), //
|
||||
new SampleEntity("id-three")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findById("does-not-exist").as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-720
|
||||
@Test
|
||||
// DATAES-720
|
||||
void findAllShouldReturnAllElements() {
|
||||
// make sure to be above the default page size of the Query interface
|
||||
int count = DEFAULT_PAGE_SIZE * 2;
|
||||
bulkIndex(IntStream.range(1, count + 1) //
|
||||
.mapToObj(it -> new SampleEntity(String.valueOf(it))) //
|
||||
.toArray(SampleEntity[]::new)) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAll() //
|
||||
.as(StepVerifier::create) //
|
||||
@ -166,18 +166,20 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void findAllByIdByIdShouldCompleteIfIndexDoesNotExist() {
|
||||
repository.findAllById(Arrays.asList("id-two", "id-two")).as(StepVerifier::create).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void findAllByIdShouldRetrieveMatchingDocuments() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two"), //
|
||||
new SampleEntity("id-three")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllById(Arrays.asList("id-one", "id-two")) //
|
||||
.as(StepVerifier::create)//
|
||||
@ -186,26 +188,28 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void findAllByIdShouldCompleteWhenNothingFound() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two"), //
|
||||
new SampleEntity("id-three")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllById(Arrays.asList("can't", "touch", "this")) //
|
||||
.as(StepVerifier::create)//
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-717
|
||||
@Test
|
||||
// DATAES-717
|
||||
void shouldReturnFluxOfSearchHit() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "message"), //
|
||||
new SampleEntity("id-three", "message")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.queryAllByMessage("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -214,13 +218,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-717
|
||||
@Test
|
||||
// DATAES-717
|
||||
void shouldReturnFluxOfSearchHitForStringQuery() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "message"), //
|
||||
new SampleEntity("id-three", "message")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.queryByMessageWithString("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -229,13 +234,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-372
|
||||
@Test
|
||||
// DATAES-372
|
||||
void shouldReturnHighlightsOnAnnotatedMethod() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "message"), //
|
||||
new SampleEntity("id-three", "message")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.queryAllByMessage("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -247,13 +253,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-372
|
||||
@Test
|
||||
// DATAES-372
|
||||
void shouldReturnHighlightsOnAnnotatedStringQueryMethod() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "message"), //
|
||||
new SampleEntity("id-three", "message")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.queryByMessageWithString("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -265,30 +272,36 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519, DATAES-767, DATAES-822
|
||||
@Test
|
||||
// DATAES-519, DATAES-767, DATAES-822
|
||||
void countShouldErrorWhenIndexDoesNotExist() {
|
||||
|
||||
operations.indexOps(SampleEntity.class).delete().block();
|
||||
repository.count() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectError(RestStatusException.class);
|
||||
.expectError(NoSuchIndexException.class) //
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void countShouldCountDocuments() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.count().as(StepVerifier::create).expectNext(2L).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void existsByIdShouldReturnTrueIfExists() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.existsById("id-two") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -296,13 +309,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void existsByIdShouldReturnFalseIfNotExists() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.existsById("wrecking ball") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -310,7 +324,8 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void countShouldCountMatchingDocuments() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
@ -337,13 +352,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void existsShouldReturnTrueIfExists() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.existsAllByMessage("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -351,13 +367,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void existsShouldReturnFalseIfNotExists() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.existsAllByMessage("these days") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -365,24 +382,27 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void deleteByIdShouldCompleteIfNothingDeleted() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.deleteById("does-not-exist").as(StepVerifier::create).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519, DATAES-767, DATAES-822, DATAES-678
|
||||
@Test
|
||||
// DATAES-519, DATAES-767, DATAES-822, DATAES-678
|
||||
void deleteByIdShouldCompleteWhenIndexDoesNotExist() {
|
||||
repository.deleteById("does-not-exist") //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void deleteByIdShouldDeleteEntry() {
|
||||
|
||||
SampleEntity toBeDeleted = new SampleEntity("id-two");
|
||||
@ -394,19 +414,22 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-976
|
||||
@Test
|
||||
// DATAES-976
|
||||
void deleteAllByIdShouldDeleteEntry() {
|
||||
|
||||
SampleEntity toBeDeleted = new SampleEntity("id-two");
|
||||
bulkIndex(new SampleEntity("id-one"), toBeDeleted) //
|
||||
.block();
|
||||
|
||||
repository.deleteAllById(Collections.singletonList(toBeDeleted.getId())).as(StepVerifier::create).verifyComplete();
|
||||
repository.deleteAllById(Collections.singletonList(toBeDeleted.getId())).as(StepVerifier::create)
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void deleteShouldDeleteEntry() {
|
||||
|
||||
SampleEntity toBeDeleted = new SampleEntity("id-two");
|
||||
@ -418,7 +441,8 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
assertThat(documentWithIdExistsInIndex(toBeDeleted.getId()).block()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void deleteAllShouldDeleteGivenEntries() {
|
||||
|
||||
SampleEntity toBeDeleted = new SampleEntity("id-one");
|
||||
@ -435,13 +459,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
assertThat(documentWithIdExistsInIndex(hangInThere.getId()).block()).isTrue();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void deleteAllShouldDeleteAllEntries() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one"), //
|
||||
new SampleEntity("id-two"), //
|
||||
new SampleEntity("id-three")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.deleteAll().as(StepVerifier::create).verifyComplete();
|
||||
|
||||
@ -451,13 +476,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderMethodShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllByMessageLike("test") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -465,13 +491,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderMethodShouldBeExecutedCorrectlyWhenGivenPublisher() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllByMessage(Mono.just("test")) //
|
||||
.as(StepVerifier::create) //
|
||||
@ -479,13 +506,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderWithDerivedSortMethodShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "test", 3), //
|
||||
new SampleEntity("id-two", "test test", 1), //
|
||||
new SampleEntity("id-three", "test test", 2)) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllByMessageLikeOrderByRate("test") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -495,13 +523,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderMethodWithSortParameterShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "test", 3), //
|
||||
new SampleEntity("id-two", "test test", 1), //
|
||||
new SampleEntity("id-three", "test test", 2)) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllByMessage("test", Sort.by(Order.asc("rate"))) //
|
||||
.as(StepVerifier::create) //
|
||||
@ -511,13 +540,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderMethodWithPageableParameterShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "test", 3), //
|
||||
new SampleEntity("id-two", "test test", 1), //
|
||||
new SampleEntity("id-three", "test test", 2)) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findAllByMessage("test", PageRequest.of(0, 2, Sort.by(Order.asc("rate")))) //
|
||||
.as(StepVerifier::create) //
|
||||
@ -526,13 +556,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedFinderMethodReturningMonoShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.findFirstByMessageLike("test") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -540,7 +571,8 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void annotatedFinderMethodShouldBeExecutedCorrectly() {
|
||||
|
||||
int count = 30;
|
||||
@ -556,7 +588,8 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #1917
|
||||
@Test
|
||||
// #1917
|
||||
void annotatedFinderMethodPagedShouldBeExecutedCorrectly() {
|
||||
|
||||
int count = 30;
|
||||
@ -576,13 +609,14 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@Test
|
||||
// DATAES-519
|
||||
void derivedDeleteMethodShouldBeExecutedCorrectly() {
|
||||
|
||||
bulkIndex(new SampleEntity("id-one", "message"), //
|
||||
new SampleEntity("id-two", "test message"), //
|
||||
new SampleEntity("id-three", "test test")) //
|
||||
.block();
|
||||
.block();
|
||||
|
||||
repository.deleteAllByMessage("message") //
|
||||
.as(StepVerifier::create) //
|
||||
@ -594,8 +628,58 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
assertThat(documentWithIdExistsInIndex("id-three").block()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
// #2135
|
||||
void FluxOfSearchHitForArrayQuery() {
|
||||
bulkIndex(new SampleEntity("id-one", "message1"), //
|
||||
new SampleEntity("id-two", "message2"), //
|
||||
new SampleEntity("id-three", "message3")) //
|
||||
.block();
|
||||
|
||||
repository.findAllViaAnnotatedQueryByMessageIn(Arrays.asList("message1", "message3")) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-one")) //
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) //
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
// #2135
|
||||
void FluxOfSearchHitForIntegerArrayQuery() {
|
||||
bulkIndex(new SampleEntity("id-one", "test", 3), //
|
||||
new SampleEntity("id-two", "test test", 1), //
|
||||
new SampleEntity("id-three", "test test", 2)) //
|
||||
.block();
|
||||
|
||||
repository.findAllViaAnnotatedQueryByRatesIn(Arrays.asList(2, 3)) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-one")) //
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) //
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
// #2135
|
||||
void FluxOfSearchHitForStringAndIntegerArrayQuery() {
|
||||
bulkIndex(new SampleEntity("id-one", "message1", 1), //
|
||||
new SampleEntity("id-two", "message2", 2), //
|
||||
new SampleEntity("id-three", "message3", 3), //
|
||||
new SampleEntity("id-four", "message4", 4), //
|
||||
new SampleEntity("id-five", "message5", 5)) //
|
||||
.block();
|
||||
|
||||
repository.findAllViaAnnotatedQueryByMessageInAndRatesIn(Arrays.asList("message5", "message3"), Arrays.asList(2,
|
||||
3)) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) //
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
Mono<Void> bulkIndex(SampleEntity... entities) {
|
||||
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(INDEX)).then();
|
||||
return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(indexNameProvider.indexName())).then();
|
||||
}
|
||||
|
||||
interface ReactiveSampleEntityRepository extends ReactiveCrudRepository<SampleEntity, String> {
|
||||
@ -610,11 +694,11 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
|
||||
Flux<SampleEntity> findAllByMessage(Publisher<String> message);
|
||||
|
||||
@Highlight(fields = { @HighlightField(name = "message") })
|
||||
@Highlight(fields = {@HighlightField(name = "message")})
|
||||
Flux<SearchHit<SampleEntity>> queryAllByMessage(String message);
|
||||
|
||||
@Query("{\"bool\": {\"must\": [{\"term\": {\"message\": \"?0\"}}]}}")
|
||||
@Highlight(fields = { @HighlightField(name = "message") })
|
||||
@Highlight(fields = {@HighlightField(name = "message")})
|
||||
Flux<SearchHit<SampleEntity>> queryByMessageWithString(String message);
|
||||
|
||||
@Query("{ \"bool\" : { \"must\" : { \"term\" : { \"message\" : \"?0\" } } } }")
|
||||
@ -633,22 +717,39 @@ class SimpleReactiveElasticsearchRepositoryTests {
|
||||
|
||||
@CountQuery(value = "{\"bool\": {\"must\": [{\"term\": {\"message\": \"?0\"}}]}}")
|
||||
Mono<Long> retrieveCountByText(String message);
|
||||
|
||||
@Query("{ \"terms\": { \"message\": ?0 } }")
|
||||
Flux<SampleEntity> findAllViaAnnotatedQueryByMessageIn(List<String> messages);
|
||||
|
||||
@Query("{ \"terms\": { \"rate\": ?0 } }")
|
||||
Flux<SampleEntity> findAllViaAnnotatedQueryByRatesIn(List<Integer> rates);
|
||||
|
||||
@Query("{\"bool\": {\"must\": [{ \"terms\": { \"message\": ?0 } }, { \"terms\": { \"rate\": ?1 } }] } }")
|
||||
Flux<SampleEntity> findAllViaAnnotatedQueryByMessageInAndRatesIn(List<String> messages, List<Integer> rates);
|
||||
|
||||
}
|
||||
|
||||
@Document(indexName = INDEX)
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class SampleEntity {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Id
|
||||
private String id;
|
||||
@Nullable
|
||||
@Field(type = Text, store = true, fielddata = true) private String type;
|
||||
@Field(type = FieldType.Text, store = true, fielddata = true)
|
||||
private String type;
|
||||
@Nullable
|
||||
@Field(type = Text, store = true, fielddata = true) private String message;
|
||||
@Nullable private int rate;
|
||||
@Nullable private boolean available;
|
||||
@Field(type = FieldType.Text, store = true, fielddata = true)
|
||||
private String message;
|
||||
@Nullable
|
||||
@Version private Long version;
|
||||
private int rate;
|
||||
@Nullable
|
||||
private boolean available;
|
||||
@Nullable
|
||||
@Version
|
||||
private Long version;
|
||||
|
||||
public SampleEntity() {}
|
||||
public SampleEntity() {
|
||||
}
|
||||
|
||||
public SampleEntity(@Nullable String id) {
|
||||
this.id = id;
|
@ -15,7 +15,7 @@
|
||||
#
|
||||
#
|
||||
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
|
||||
sde.testcontainers.image-version=7.17.1
|
||||
sde.testcontainers.image-version=7.17.6
|
||||
#
|
||||
#
|
||||
# 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user