1
0
mirror of synced 2026-05-24 13:13:17 +00:00

Compare commits

...

31 Commits

Author SHA1 Message Date
Mark Paluch 3a876901c0 Release version 4.4.6 (2021.2.6).
See #2335
2022-11-18 10:45:33 +01:00
Mark Paluch f59d9c6eae Prepare 4.4.6 (2021.2.6).
See #2335
2022-11-18 10:44:45 +01:00
Tiny a1094527a9 Update Dynamic.java
Original Pull Request #2357
Closes #2359

(cherry picked from commit f8ddf16c0c)
2022-11-10 20:18:24 +01:00
Peter-Josef Meisch cd8fabe499 Upgrade to Elasticsearch 7.17.7.
Original Pull Request #2347
Closes #2346
2022-11-01 20:29:29 +01:00
Mark Paluch b61f44d872 Update CI properties.
See #2335
2022-10-31 13:18:18 +01:00
Peter-Josef Meisch 933ebab0e0 Fix repository methods value converting.
Original Pull Request #2339
Closes #2338

(cherry picked from commit e67150a55b)
2022-10-19 22:42:06 +02:00
Spring Builds edac49c470 After release cleanups.
See #2334
2022-10-13 13:46:55 +00:00
Spring Builds cb65bc5ead Prepare next development iteration.
See #2334
2022-10-13 13:46:42 +00:00
Spring Builds c99d633ac6 Release version 4.4.5 (2021.2.5).
See #2334
2022-10-13 13:24:06 +00:00
Spring Builds 7372f9120d Prepare 4.4.5 (2021.2.5).
See #2334
2022-10-13 13:21:37 +00:00
Spring Builds c09138f985 After release cleanups.
See #2296
2022-10-13 09:23:59 +00:00
Spring Builds 5ac167290c Prepare next development iteration.
See #2296
2022-10-13 09:23:46 +00:00
Spring Builds 06db4fde05 Release version 4.4.4 (2021.2.4).
See #2296
2022-10-13 08:59:56 +00:00
Spring Builds c823ce1946 Prepare 4.4.4 (2021.2.4).
See #2296
2022-10-13 08:57:08 +00:00
Peter-Josef Meisch 2fa15f772a Escape backslash in StringQuery.
Original Pull Request
Closes #2326

(cherry picked from commit 03ecc48b09)
2022-10-11 22:38:22 +02:00
Peter-Josef Meisch 02b6d54cc9 Prefer config supplied content-type and accept header.
Original Pull Request #2328
Closes #2327
2022-10-08 15:10:26 +02:00
Peter-Josef Meisch 8bb3474c05 Revert "Support partially update document by entity."
This reverts commit 7e904cdbe7.
2022-09-24 22:00:49 +02:00
puppylpg 7e904cdbe7 Support partially update document by entity.
Original Pull Request #2305
Closes #2304
2022-09-24 21:57:41 +02:00
Spring Builds e46b4977a4 After release cleanups.
See #2221
2022-09-19 12:00:24 +00:00
Spring Builds 5de2e0b96e Prepare next development iteration.
See #2221
2022-09-19 12:00:12 +00:00
Spring Builds c36b878cee Release version 4.4.3 (2021.2.3).
See #2221
2022-09-19 11:38:46 +00:00
Spring Builds 2efa79d469 Prepare 4.4.3 (2021.2.3).
See #2221
2022-09-19 11:36:24 +00:00
Peter-Josef Meisch 20fde914df Upgrade to Elasticsearch 7.17.6.
Original Pull Request #2289
Closes #2285
2022-09-04 12:42:14 +02:00
Peter-Josef Meisch 988736dd41 Fix NPE in RequestFactory when language in UpdateQuery is not set (4.4.x).
Original Pull Request #2288
Closes #2287
2022-09-03 07:50:01 +02:00
Peter-Josef Meisch 346c5cce58 Fix mapping of property values into a collection.
When reading from Elasticsearch into a property of type Collection<T> (List<T> or Set<T>) the MappingElasticsearchConverter now can read both from the returned JSON:

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

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

Original Pull Request #2282
Closes #2280

(cherry picked from commit 86634ceb38)
2022-08-29 21:06:38 +02:00
Peter-Josef Meisch a3ebd8be78 Fix update call in reactive client (Elasticsearch 7 client)
Original Pull Request #2281
Closes #2276

(cherry picked from commit 8377f64a8a)
2022-08-26 08:12:22 +02:00
Peter-Josef Meisch 3c6d96e49f Don't try to write non-writeable properties.
Original Pull Request #2249
Closes #2230

(cherry picked from commit acf02a1dc9)

# Conflicts:
#	src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java
2022-08-04 12:11:22 +02:00
Peter-Josef Meisch be70a398be Fix exists query for imperative repository implementation.
Original Pull Request #2236
Closes #2162

(cherry picked from commit 373be49f97)
2022-07-22 22:01:26 +02:00
Peter-Josef Meisch e3e666fd2e Upgrade to Elasticsearch 7.17.5.
Original Pull Request #2224
Closes #2215
2022-07-16 21:30:41 +02:00
Christoph Strobl cb3d1e11d3 After release cleanups.
See #2186
2022-07-15 11:24:08 +02:00
Christoph Strobl 9e17bf3df8 Prepare next development iteration.
See #2186
2022-07-15 11:24:05 +02:00
40 changed files with 1489 additions and 277 deletions
+6 -6
View File
@@ -1,7 +1,7 @@
# Java versions
java.main.tag=8u332-b09-jdk
java.next.tag=11.0.15_10-jdk
java.lts.tag=17.0.3_7-jdk
java.main.tag=8u345-b01-jdk-focal
java.next.tag=11.0.16.1_1-jdk-focal
java.lts.tag=17.0.4.1_1-jdk-focal
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
@@ -10,14 +10,14 @@ docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclip
# Supported versions of MongoDB
docker.mongodb.4.0.version=4.0.28
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
docker.mongodb.4.4.version=4.4.17
docker.mongodb.5.0.version=5.0.13
# Supported versions of Redis
docker.redis.6.version=6.2.6
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.12
docker.cassandra.3.version=3.11.14
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
+5 -5
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.4.2</version>
<version>4.4.6</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.7.2</version>
<version>2.7.6</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -19,12 +19,12 @@
<properties>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.4</elasticsearch-rhlc>
<elasticsearch-rhlc>7.17.7</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<elasticsearch-java>7.17.4</elasticsearch-java>
<elasticsearch-java>7.17.7</elasticsearch-java>
<log4j>2.17.1</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>2.7.2</springdata.commons>
<springdata.commons>2.7.6</springdata.commons>
<testcontainers>1.16.2</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<java-module-name>spring.data.elasticsearch</java-module-name>
+4 -4
View File
@@ -29,15 +29,15 @@ Requires an installation of https://www.elastic.co/products/elasticsearch[Elasti
[[preface.versions]]
=== Versions
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of
Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring
Data release train. The Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of
Spring Data Elasticsearch included in that, as well as the Spring Boot versions referring to that particular Spring
Data release train. The Elasticsearch version given shows with which client libraries Spring Data Elasticsearch was
built and tested.
[cols="^,^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
| 2021.2 (Raj) | 4.4.x | 7.17.4 | 5.3.x | 2.7.x
| 2021.2 (Raj) | 4.4.x | 7.17.7 | 5.3.x | 2.7.x
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
| 2021.0 (Pascal) | 4.2.xfootnote:oom[Out of maintenance] | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham)footnote:oom[] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
@@ -82,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.4</version>
<version>7.17.7</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -93,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.4</version>
<version>7.17.7</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.4.
* Upgrade to Elasticsearch 7.17.6.
[[new-features.4-3-0]]
== New in Spring Data Elasticsearch 4.3
@@ -46,7 +46,7 @@ public enum Dynamic {
/**
* Inherit the dynamic setting from their parent object or from the mapping type.
*/
INHERIT("nherit");
INHERIT("inherit");
private final String mappedName;
@@ -32,7 +32,16 @@ import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch._types.query_dsl.Like;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
import co.elastic.clients.elasticsearch.core.DeleteRequest;
import co.elastic.clients.elasticsearch.core.GetRequest;
import co.elastic.clients.elasticsearch.core.IndexRequest;
import co.elastic.clients.elasticsearch.core.MgetRequest;
import co.elastic.clients.elasticsearch.core.MsearchRequest;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.UpdateByQueryRequest;
import co.elastic.clients.elasticsearch.core.UpdateRequest;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
@@ -42,7 +51,6 @@ import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Rescore;
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpDeserializer;
@@ -820,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();
}
@@ -963,9 +975,12 @@ class RequestConverter {
.pipeline(updateQuery.getPipeline()) //
.requestsPerSecond(
updateQuery.getRequestsPerSecond() != null ? updateQuery.getRequestsPerSecond().longValue() : null) //
.slices(updateQuery.getSlices() != null ? Long.valueOf(updateQuery.getSlices()) : 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);
}
@@ -256,6 +256,15 @@ public interface WebClientProvider {
HttpHeaders suppliedHeaders = clientConfiguration.getHeadersSupplier().get();
if (suppliedHeaders != null && suppliedHeaders != HttpHeaders.EMPTY) {
// remove content-type and accept if they are provided by the client configuration (ES7 compatibility headers)
if (suppliedHeaders.containsKey(HttpHeaders.CONTENT_TYPE)) {
httpHeaders.remove(HttpHeaders.CONTENT_TYPE);
}
if (suppliedHeaders.containsKey(HttpHeaders.ACCEPT)) {
httpHeaders.remove(HttpHeaders.ACCEPT);
}
httpHeaders.addAll(suppliedHeaders);
}
}));
@@ -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());
}
@@ -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());
}
@@ -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);
}
@@ -40,6 +40,7 @@ import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
@@ -192,7 +193,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 +212,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 +233,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 +255,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 +341,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 +373,6 @@ public class MappingElasticsearchConverter
}
return result;
}
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
@@ -397,7 +404,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 +462,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 +688,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 +1157,29 @@ public class MappingElasticsearchConverter
Assert.notNull(query, "query must not be null");
if (domainClass != null) {
if (query instanceof BaseQuery) {
updateFieldsAndSourceFilter(query, domainClass);
if (query instanceof CriteriaQuery) {
updateCriteriaQuery((CriteriaQuery) query, domainClass);
if (((BaseQuery) query).queryIsUpdatedByConverter()) {
return;
}
}
if (domainClass == null) {
return;
}
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
if (query instanceof CriteriaQuery) {
updatePropertiesInCriteriaQuery((CriteriaQuery) query, domainClass);
}
if (query instanceof BaseQuery) {
((BaseQuery) query).setQueryIsUpdatedByConverter(true);
}
}
private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
@@ -1165,14 +1217,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 +1241,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();
@@ -73,6 +73,8 @@ public class BaseQuery implements Query {
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
@Nullable protected List<IndexBoost> indicesBoost;
private boolean queryIsUpdatedByConverter = false;
public BaseQuery() {}
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
@@ -440,4 +442,23 @@ public class BaseQuery implements Query {
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
/**
* used internally. Not considered part of the API.
*
* @since 5.0
*/
public boolean queryIsUpdatedByConverter() {
return queryIsUpdatedByConverter;
}
/**
* used internally. Not considered part of the API.
*
* @since 5.0
*/
public void setQueryIsUpdatedByConverter(boolean queryIsUpdatedByConverter) {
this.queryIsUpdatedByConverter = queryIsUpdatedByConverter;
}
}
@@ -15,9 +15,24 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import java.util.Collections;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* AbstractElasticsearchRepositoryQuery
@@ -32,11 +47,13 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
protected static final int DEFAULT_STREAM_BATCH_SIZE = 500;
protected ElasticsearchQueryMethod queryMethod;
protected ElasticsearchOperations elasticsearchOperations;
protected final ElasticsearchConverter elasticsearchConverter;
public AbstractElasticsearchRepositoryQuery(ElasticsearchQueryMethod queryMethod,
ElasticsearchOperations elasticsearchOperations) {
this.queryMethod = queryMethod;
this.elasticsearchOperations = elasticsearchOperations;
this.elasticsearchConverter = elasticsearchOperations.getElasticsearchConverter();
}
@Override
@@ -49,4 +66,117 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
* @since 4.2
*/
public abstract boolean isCountQuery();
protected abstract boolean isDeleteQuery();
protected abstract boolean isExistsQuery();
@Override
public Object execute(Object[] parameters) {
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
Class<?> clazz = getResultClass();
Query query = createQuery(parameters);
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
if (isDeleteQuery()) {
result = countOrGetDocumentsForDelete(query, parameterAccessor);
elasticsearchOperations.delete(query, clazz, index);
elasticsearchOperations.indexOps(index).refresh();
} else if (isCountQuery()) {
result = elasticsearchOperations.count(query, clazz, index);
} else if (isExistsQuery()) {
result = elasticsearchOperations.count(query, clazz, index) > 0;
} else if (queryMethod.isPageQuery()) {
query.setPageable(parameterAccessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(query, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
} else {
result = SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, query.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
query.setPageable(parameterAccessor.getPageable().isPaged() ? parameterAccessor.getPageable()
: PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(query, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
if (parameterAccessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
if (itemCount == 0) {
result = new SearchHitsImpl<>(0, TotalHitsRelation.EQUAL_TO, Float.NaN, null, Collections.emptyList(), null,
null);
} else {
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
}
} else {
query.setPageable(parameterAccessor.getPageable());
}
if (result == null) {
result = elasticsearchOperations.search(query, clazz, index);
}
} else {
result = elasticsearchOperations.searchOne(query, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
}
public Query createQuery(Object[] parameters) {
Class<?> clazz = getResultClass();
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
Query query = createQuery(parameterAccessor);
Assert.notNull(query, "unsupported query");
if (queryMethod.hasAnnotatedHighlight()) {
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
return query;
}
private Class<?> getResultClass() {
return queryMethod.getResultProcessor().getReturnedType().getDomainType();
}
private ParametersParameterAccessor getParameterAccessor(Object[] parameters) {
return new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
}
@Nullable
private Object countOrGetDocumentsForDelete(Query query, ParametersParameterAccessor accessor) {
Object result = null;
Class<?> entityClass = queryMethod.getEntityInformation().getJavaType();
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(entityClass);
if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, entityClass, index);
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
result = elasticsearchOperations.search(query, entityClass, index);
}
if (ClassUtils.isAssignable(Number.class, queryMethod.getReturnedObjectType())) {
result = elasticsearchOperations.count(query, entityClass, index);
}
return result;
}
protected abstract Query createQuery(ParametersParameterAccessor accessor);
}
@@ -15,26 +15,18 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import java.util.Collections;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* ElasticsearchPartQuery
@@ -49,13 +41,11 @@ import org.springframework.util.ClassUtils;
public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery {
private final PartTree tree;
private final ElasticsearchConverter elasticsearchConverter;
private final MappingContext<?, ElasticsearchPersistentProperty> mappingContext;
public ElasticsearchPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations) {
super(method, elasticsearchOperations);
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
this.elasticsearchConverter = elasticsearchOperations.getElasticsearchConverter();
this.mappingContext = elasticsearchConverter.getMappingContext();
}
@@ -65,103 +55,24 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
}
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
protected boolean isDeleteQuery() {
return tree.isDelete();
}
CriteriaQuery query = createQuery(accessor);
@Override
protected boolean isExistsQuery() {
return tree.isExistsProjection();
}
Assert.notNull(query, "unsupported query");
protected Query createQuery(ParametersParameterAccessor accessor) {
elasticsearchConverter.updateQuery(query, clazz);
if (queryMethod.hasAnnotatedHighlight()) {
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
BaseQuery query = new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
if (tree.isLimiting()) {
// noinspection ConstantConditions
query.setMaxResults(tree.getMaxResults());
}
if (tree.isDelete()) {
result = countOrGetDocumentsForDelete(query, accessor);
elasticsearchOperations.delete(query, clazz, index);
elasticsearchOperations.indexOps(index).refresh();
} else if (queryMethod.isPageQuery()) {
query.setPageable(accessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(query, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
} else {
result = SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, query.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
if (accessor.getPageable().isUnpaged()) {
query.setPageable(PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
} else {
query.setPageable(accessor.getPageable());
}
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(query, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
if (itemCount == 0) {
result = new SearchHitsImpl<>(0, TotalHitsRelation.EQUAL_TO, Float.NaN, null, Collections.emptyList(), null,
null);
} else {
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
}
} else {
query.setPageable(accessor.getPageable());
}
if (result == null) {
result = elasticsearchOperations.search(query, clazz, index);
}
} else if (tree.isCountProjection()) {
result = elasticsearchOperations.count(query, clazz, index);
} else {
result = elasticsearchOperations.searchOne(query, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
}
@Nullable
private Object countOrGetDocumentsForDelete(CriteriaQuery query, ParametersParameterAccessor accessor) {
Object result = null;
Class<?> clazz = queryMethod.getEntityInformation().getJavaType();
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
result = elasticsearchOperations.search(query, clazz, index);
}
if (ClassUtils.isAssignable(Number.class, queryMethod.getReturnedObjectType())) {
result = elasticsearchOperations.count(query, clazz, index);
}
return result;
}
public CriteriaQuery createQuery(ParametersParameterAccessor accessor) {
return new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
return query;
}
}
@@ -15,16 +15,11 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.repository.support.StringQueryUtil;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.Assert;
/**
@@ -38,13 +33,13 @@ import org.springframework.util.Assert;
*/
public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQuery {
private String query;
private final String queryString;
public ElasticsearchStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations,
String query) {
String queryString) {
super(queryMethod, elasticsearchOperations);
Assert.notNull(query, "Query cannot be empty");
this.query = query;
Assert.notNull(queryString, "Query cannot be empty");
this.queryString = queryString;
}
@Override
@@ -53,54 +48,20 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue
}
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
StringQuery stringQuery = createQuery(accessor);
Assert.notNull(stringQuery, "unsupported query");
if (queryMethod.hasAnnotatedHighlight()) {
stringQuery.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
if (isCountQuery()) {
result = elasticsearchOperations.count(stringQuery, clazz, index);
} else if (queryMethod.isPageQuery()) {
stringQuery.setPageable(accessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(stringQuery, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable());
} else {
result = SearchHitSupport
.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
stringQuery.setPageable(
accessor.getPageable().isPaged() ? accessor.getPageable() : PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(stringQuery, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
stringQuery.setPageable(accessor.getPageable().isPaged() ? accessor.getPageable() : Pageable.unpaged());
result = elasticsearchOperations.search(stringQuery, clazz, index);
} else {
result = elasticsearchOperations.searchOne(stringQuery, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
protected boolean isDeleteQuery() {
return false;
}
protected StringQuery createQuery(ParametersParameterAccessor parameterAccessor) {
@Override
protected boolean isExistsQuery() {
return false;
}
protected Query createQuery(ParametersParameterAccessor parameterAccessor) {
String queryString = new StringQueryUtil(elasticsearchOperations.getElasticsearchConverter().getConversionService())
.replacePlaceholders(this.query, parameterAccessor);
.replacePlaceholders(this.queryString, parameterAccessor);
return new StringQuery(queryString);
}
}
@@ -46,7 +46,11 @@ final public class StringQueryUtil {
String placeholder = Pattern.quote(matcher.group()) + "(?!\\d+)";
int index = NumberUtils.parseNumber(matcher.group(1), Integer.class);
result = result.replaceAll(placeholder, Matcher.quoteReplacement(getParameterWithIndex(accessor, index)));
String replacement = Matcher.quoteReplacement(getParameterWithIndex(accessor, index));
result = result.replaceAll(placeholder, replacement);
// need to escape backslashes that are not escapes for quotes so that they are sent as double-backslashes
// to Elasticsearch
result = result.replaceAll("\\\\([^\"'])", "\\\\\\\\$1");
}
return result;
}
@@ -56,9 +60,7 @@ final public class StringQueryUtil {
Object parameter = accessor.getBindableValue(index);
String parameterValue = "null";
// noinspection ConstantConditions
if (parameter != null) {
parameterValue = convert(parameter);
}
+5 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 4.4.2 (2021.2.2)
Spring Data Elasticsearch 4.4.6 (2021.2.6)
Copyright (c) [2013-2021] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -35,6 +35,10 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -21,8 +21,8 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
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.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
/**
@@ -36,12 +36,11 @@ public class ElasticsearchPartQueryELCIntegrationTests extends ElasticsearchPart
static class Config {}
@Override
protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz) {
protected String buildQueryString(Query query, Class<?> clazz) {
JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper();
RequestConverter requestConverter = new RequestConverter(operations.getElasticsearchConverter(), jsonpMapper);
SearchRequest request = requestConverter.searchRequest(criteriaQuery, clazz, IndexCoordinates.of("dummy"), false,
false);
SearchRequest request = requestConverter.searchRequest(query, clazz, IndexCoordinates.of("dummy"), false, false);
return JsonUtils.toJson(request, jsonpMapper);
// return "{\"query\":" + JsonUtils.toJson(request.query(), jsonpMapper) + "}";
@@ -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
@@ -45,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;
@@ -2783,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() {
@@ -3718,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
@@ -4463,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
}
@@ -19,8 +19,8 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
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.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
@@ -38,9 +38,9 @@ public class ElasticsearchPartQueryERHLCIntegrationTests extends ElasticsearchPa
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {}
protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz) {
protected String buildQueryString(Query query, Class<?> clazz) {
SearchSourceBuilder source = new RequestFactory(operations.getElasticsearchConverter())
.searchRequest(criteriaQuery, clazz, IndexCoordinates.of("dummy")).source();
.searchRequest(query, clazz, IndexCoordinates.of("dummy")).source();
// remove defaultboost values
return source.toString().replaceAll("(\\^\\d+\\.\\d+)", "");
}
@@ -103,15 +103,15 @@ public class ReactiveElasticsearchELCIntegrationTests extends ReactiveElasticsea
assertThat(bucketList.size()).isEqualTo(3);
AtomicInteger count = new AtomicInteger();
bucketList.forEach(stringTermsBucket -> {
if ("message".equals(stringTermsBucket.key())) {
if ("message".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(3);
}
if ("some".equals(stringTermsBucket.key())) {
if ("some".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(2);
}
if ("other".equals(stringTermsBucket.key())) {
if ("other".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(1);
}
@@ -49,7 +49,9 @@ import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
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;
@@ -1156,6 +1158,23 @@ public abstract class ReactiveElasticsearchIntegrationTests {
.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
@@ -1489,5 +1508,35 @@ public abstract class ReactiveElasticsearchIntegrationTests {
+ 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
}
@@ -140,6 +140,18 @@ class RequestFactoryTests {
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
@@ -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) {
@@ -36,7 +36,6 @@ import org.springframework.data.elasticsearch.repository.query.ElasticsearchPart
import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.lang.Nullable;
/**
@@ -637,19 +636,18 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
new DefaultRepositoryMetadata(SampleRepository.class), new SpelAwareProxyProjectionFactory(),
operations.getElasticsearchConverter().getMappingContext());
ElasticsearchPartQuery partQuery = new ElasticsearchPartQuery(queryMethod, operations);
CriteriaQuery criteriaQuery = partQuery
.createQuery(new ParametersParameterAccessor(queryMethod.getParameters(), parameters));
return buildQueryString(criteriaQuery, Book.class);
Query query = partQuery.createQuery(parameters);
return buildQueryString(query, Book.class);
}
/**
* builds the query String that would be sent to Elasticsearch
*
* @param criteriaQuery the {@link CriteriaQuery}
* @param query the {@link Query}
* @param clazz the entity class
* @return the created query string
*/
abstract protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz);
abstract protected String buildQueryString(Query query, Class<?> clazz);
@FunctionalInterface
interface AssertFunction {
@@ -129,6 +129,18 @@ public class ElasticsearchStringQueryUnitTests extends ElasticsearchStringQueryU
"{ 'bool' : { 'must' : { 'terms' : { 'name' : [\"hello \\\"Stranger\\\"\",\"Another string\"] } } } }");
}
@Test // #2326
@DisplayName("should escape backslashes in collection query parameters")
void shouldEscapeBackslashesInCollectionQueryParameters() throws NoSuchMethodException {
final List<String> parameters = Arrays.asList("param\\1", "param\\2");
List<String> params = new ArrayList<>(parameters);
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByNameIn", params);
assertThat(query).isInstanceOf(StringQuery.class);
assertThat(((StringQuery) query).getSource()).isEqualTo(
"{ 'bool' : { 'must' : { 'terms' : { 'name' : [\"param\\\\1\",\"param\\\\2\"] } } } }");
}
private org.springframework.data.elasticsearch.core.query.Query createQuery(String methodName, Object... args)
throws NoSuchMethodException {
@@ -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,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.query.valueconverter;
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 5.0
*/
@ContextConfiguration(classes = { ReactiveValueConverterELCIntegrationTests.Config.class })
public class ReactiveValueConverterELCIntegrationTests extends ReactiveValueConverterIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-valueconverter");
}
}
}
@@ -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.query.valueconverter;
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
* @since 5.0
*/
@ContextConfiguration(classes = { ReactiveValueConverterERHLCIntegrationTests.Config.class })
public class ReactiveValueConverterERHLCIntegrationTests extends ReactiveValueConverterIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-valueconverter-es7");
}
}
}
@@ -0,0 +1,153 @@
/*
* 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.query.valueconverter;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
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;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
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.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
/**
* Integration tests to check that {@link org.springframework.data.elasticsearch.annotations.ValueConverter} annotated
* properties are handle correctly (method name derived queries, for
*
* @{@link org.springframework.data.elasticsearch.core.query.Query} methods we don't know which parameters map to which
* property.
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
public abstract class ReactiveValueConverterIntegrationTests {
@Autowired private IndexNameProvider indexNameProvider;
@Autowired private ReactiveElasticsearchOperations operations;
@Autowired private EntityRepository repository;
@BeforeEach
void setUp() {
indexNameProvider.increment();
operations.indexOps(Entity.class).createWithMapping().block();
}
@Test
@Order(java.lang.Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete().block();
}
@Test // #2338
@DisplayName("should apply ValueConverter")
void shouldApplyValueConverter() {
ValueConverterIntegrationTests.Entity entity = new ValueConverterIntegrationTests.Entity();
entity.setId("42");
entity.setText("answer");
operations.save(entity).block();
repository.queryByText("text-answer") //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
repository.findByText("answer") //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
}
interface EntityRepository extends ReactiveElasticsearchRepository<Entity, String> {
Flux<SearchHit<Entity>> findByText(String text);
@Query("{ \"term\": { \"text\": \"?0\" } }")
Flux<SearchHit<Entity>> queryByText(String text);
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Entity {
@Id
@Nullable private String id;
@Field(type = FieldType.Keyword)
@ValueConverter(ValueConverterIntegrationTests.TextConverter.class)
@Nullable private String text;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
static class TextConverter implements PropertyValueConverter {
public static final String PREFIX = "text-";
@Override
public Object write(Object value) {
return PREFIX + value.toString();
}
@Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
}
@@ -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.query.valueconverter;
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;
/**
* {@link ValueConverterIntegrationTests} using a Repository backed by an ElasticsearchTemplate.
*
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ValueConverterELCIntegrationTests.Config.class })
public class ValueConverterELCIntegrationTests extends ValueConverterIntegrationTests {
@Configuration
@Import({ ElasticsearchTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("valueconverter");
}
}
}
@@ -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.query.valueconverter;
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;
/**
* {@link ValueConverterIntegrationTests} using a Repository backed by an ElasticsearchTemplate.
*
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ValueConverterERHLCIntegrationTests.Config.class })
public class ValueConverterERHLCIntegrationTests extends ValueConverterIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("valueconverter-es7");
}
}
}
@@ -0,0 +1,140 @@
/*
* 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.query.valueconverter;
import static org.assertj.core.api.Assertions.*;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
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.Query;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
/**
* Integration tests to check that {@link org.springframework.data.elasticsearch.annotations.ValueConverter} annotated
* properties are handle correctly (method name derived queries, for
*
* @{@link org.springframework.data.elasticsearch.core.query.Query} methods we don't know which parameters map to which
* property.
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
abstract class ValueConverterIntegrationTests {
@Autowired private EntityRepository repository;
@Autowired ElasticsearchOperations operations;
@Autowired IndexNameProvider indexNameProvider;
@BeforeEach
public void before() {
indexNameProvider.increment();
operations.indexOps(Entity.class).createWithMapping();
}
@Test
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete();
}
@Test // #2338
@DisplayName("should apply ValueConverter")
void shouldApplyValueConverter() {
Entity entity = new Entity();
entity.setId("42");
entity.setText("answer");
operations.save(entity);
SearchHits<Entity> searchHits = repository.queryByText("text-answer");
assertThat(searchHits.getTotalHits()).isEqualTo(1);
searchHits = repository.findByText("answer");
assertThat(searchHits.getTotalHits()).isEqualTo(1);
}
interface EntityRepository extends ElasticsearchRepository<Entity, String> {
SearchHits<Entity> findByText(String text);
@Query("{ \"term\": { \"text\": \"?0\" } }")
SearchHits<Entity> queryByText(String text);
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Entity {
@Id
@Nullable private String id;
@Field(type = FieldType.Keyword)
@ValueConverter(TextConverter.class)
@Nullable private String text;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
static class TextConverter implements PropertyValueConverter {
public static final String PREFIX = "text-";
@Override
public Object write(Object value) {
return PREFIX + value.toString();
}
@Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
}
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=7.17.4
sde.testcontainers.image-version=7.17.7
#
#
# 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