Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c99d633ac6 | |||
| 7372f9120d | |||
| c09138f985 | |||
| 5ac167290c | |||
| 06db4fde05 | |||
| c823ce1946 | |||
| 2fa15f772a | |||
| 02b6d54cc9 | |||
| 8bb3474c05 | |||
| 7e904cdbe7 | |||
| e46b4977a4 | |||
| 5de2e0b96e | |||
| c36b878cee | |||
| 2efa79d469 | |||
| 20fde914df | |||
| 988736dd41 | |||
| 346c5cce58 | |||
| a3ebd8be78 | |||
| 3c6d96e49f | |||
| be70a398be | |||
| e3e666fd2e | |||
| cb3d1e11d3 | |||
| 9e17bf3df8 |
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.4.2</version>
|
||||
<version>4.4.5</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.7.2</version>
|
||||
<version>2.7.5</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.6</elasticsearch-rhlc>
|
||||
<!-- version of the new ElasticsearchClient -->
|
||||
<elasticsearch-java>7.17.4</elasticsearch-java>
|
||||
<elasticsearch-java>7.17.6</elasticsearch-java>
|
||||
<log4j>2.17.1</log4j>
|
||||
<netty>4.1.65.Final</netty>
|
||||
<springdata.commons>2.7.2</springdata.commons>
|
||||
<springdata.commons>2.7.5</springdata.commons>
|
||||
<testcontainers>1.16.2</testcontainers>
|
||||
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
|
||||
@@ -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.6 | 5.3.x | 2.7.x
|
||||
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
|
||||
| 2021.0 (Pascal) | 4.2.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.6</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.6</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
== New in Spring Data Elasticsearch 4.4
|
||||
|
||||
* Introduction of new imperative and reactive clients using the classes from the new Elasticsearch Java client
|
||||
* Upgrade to Elasticsearch 7.17.4.
|
||||
* Upgrade to Elasticsearch 7.17.6.
|
||||
|
||||
[[new-features.4-3-0]]
|
||||
== New in Spring Data Elasticsearch 4.3
|
||||
|
||||
+19
-4
@@ -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);
|
||||
}
|
||||
|
||||
+9
@@ -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);
|
||||
}
|
||||
}));
|
||||
|
||||
+14
-4
@@ -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());
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+70
-22
@@ -192,7 +192,7 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to do the actual writing. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* Class to do the actual reading. The methods originally were in the MappingElasticsearchConverter class, but are
|
||||
* refactored to allow for keeping state during the conversion of an object.
|
||||
*/
|
||||
private static class Reader extends Base {
|
||||
@@ -211,10 +211,17 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* Reads the given source into the given type.
|
||||
*
|
||||
* @param type they type to convert the given source to.
|
||||
* @param source the source to create an object of the given type from.
|
||||
* @return the object that was read
|
||||
*/
|
||||
<R> R read(Class<R> type, Document source) {
|
||||
|
||||
TypeInformation<R> typeHint = ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeHint, source);
|
||||
TypeInformation<R> typeInformation = ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type));
|
||||
R r = read(typeInformation, source);
|
||||
|
||||
if (r == null) {
|
||||
throw new ConversionException("could not convert into object of class " + type);
|
||||
@@ -225,11 +232,11 @@ public class MappingElasticsearchConverter
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R read(TypeInformation<R> type, Map<String, Object> source) {
|
||||
private <R> R read(TypeInformation<R> typeInformation, Map<String, Object> source) {
|
||||
|
||||
Assert.notNull(source, "Source must not be null!");
|
||||
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, type);
|
||||
TypeInformation<? extends R> typeToUse = typeMapper.readType(source, typeInformation);
|
||||
Class<? extends R> rawType = typeToUse.getType();
|
||||
|
||||
if (conversions.hasCustomReadTarget(source.getClass(), rawType)) {
|
||||
@@ -247,8 +254,8 @@ public class MappingElasticsearchConverter
|
||||
if (typeToUse.equals(ClassTypeInformation.OBJECT)) {
|
||||
return (R) source;
|
||||
}
|
||||
// Retrieve persistent entity info
|
||||
|
||||
// Retrieve persistent entity info
|
||||
ElasticsearchPersistentEntity<?> entity = mappingContext.getPersistentEntity(typeToUse);
|
||||
|
||||
if (entity == null) {
|
||||
@@ -333,7 +340,7 @@ public class MappingElasticsearchConverter
|
||||
PersistentPropertyAccessor<R> propertyAccessor = new ConvertingPropertyAccessor<>(
|
||||
targetEntity.getPropertyAccessor(result), conversionService);
|
||||
// Only deal with String because ES generated Ids are strings !
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
if (idProperty != null && idProperty.isWritable() && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, document.getId());
|
||||
}
|
||||
}
|
||||
@@ -365,7 +372,6 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
|
||||
@@ -397,7 +403,7 @@ public class MappingElasticsearchConverter
|
||||
|
||||
for (ElasticsearchPersistentProperty prop : entity) {
|
||||
|
||||
if (entity.isConstructorArgument(prop) || !prop.isReadable()) {
|
||||
if (entity.isConstructorArgument(prop) || !prop.isReadable() || !prop.isWritable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -455,12 +461,44 @@ public class MappingElasticsearchConverter
|
||||
} else if (value.getClass().isArray()) {
|
||||
return (T) readCollectionOrArray(type, Arrays.asList((Object[]) value));
|
||||
} else if (value instanceof Map) {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null) {
|
||||
Object o = read(collectionComponentType, (Map<String, Object>) value);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
return (T) read(type, (Map<String, Object>) value);
|
||||
} else {
|
||||
|
||||
TypeInformation<?> collectionComponentType = getCollectionComponentType(type);
|
||||
if (collectionComponentType != null
|
||||
&& collectionComponentType.isAssignableFrom(ClassTypeInformation.from(value.getClass()))) {
|
||||
Object o = getPotentiallyConvertedSimpleRead(value, collectionComponentType);
|
||||
return getCollectionWithSingleElement(type, collectionComponentType, o);
|
||||
}
|
||||
|
||||
return (T) getPotentiallyConvertedSimpleRead(value, rawType);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getCollectionWithSingleElement(TypeInformation<?> collectionType,
|
||||
TypeInformation<?> componentType, Object element) {
|
||||
Collection<Object> collection = CollectionFactory.createCollection(collectionType.getType(),
|
||||
componentType.getType(), 1);
|
||||
collection.add(element);
|
||||
return (T) collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type the type to check
|
||||
* @return true if type is a collectoin, null otherwise,
|
||||
*/
|
||||
@Nullable
|
||||
TypeInformation<?> getCollectionComponentType(TypeInformation<?> type) {
|
||||
return type.isCollectionLike() ? type.getComponentType() : null;
|
||||
}
|
||||
|
||||
private Object propertyConverterRead(ElasticsearchPersistentProperty property, Object source) {
|
||||
PropertyValueConverter propertyValueConverter = Objects.requireNonNull(property.getPropertyValueConverter());
|
||||
|
||||
@@ -649,7 +687,8 @@ public class MappingElasticsearchConverter
|
||||
* @see org.springframework.data.mapping.model.SpELExpressionParameterValueProvider#potentiallyConvertSpelValue(java.lang.Object, org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||
*/
|
||||
@Override
|
||||
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
protected <T> T potentiallyConvertSpelValue(Object object,
|
||||
Parameter<T, ElasticsearchPersistentProperty> parameter) {
|
||||
return readValue(object, parameter.getType());
|
||||
}
|
||||
}
|
||||
@@ -1117,17 +1156,18 @@ public class MappingElasticsearchConverter
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
if (domainClass != null) {
|
||||
if (domainClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateFieldsAndSourceFilter(query, domainClass);
|
||||
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
|
||||
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updateCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
if (query instanceof CriteriaQuery) {
|
||||
updatePropertiesInCriteriaQuery((CriteriaQuery) query, domainClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||
|
||||
@@ -1165,14 +1205,22 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> updateFieldNames(List<String> fields, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fields.stream().map(fieldName -> {
|
||||
/**
|
||||
* relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
|
||||
* fieldname. If no such property exists, the original fieldName is kept.
|
||||
*
|
||||
* @param fieldNames list of fieldnames
|
||||
* @param persistentEntity the persistent entity to check
|
||||
* @return an updated list of field names
|
||||
*/
|
||||
private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fieldNames.stream().map(fieldName -> {
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
|
||||
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
|
||||
Assert.notNull(criteriaQuery, "criteriaQuery must not be null");
|
||||
Assert.notNull(domainClass, "domainClass must not be null");
|
||||
@@ -1181,17 +1229,17 @@ public class MappingElasticsearchConverter
|
||||
|
||||
if (persistentEntity != null) {
|
||||
for (Criteria chainedCriteria : criteriaQuery.getCriteria().getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
for (Criteria subCriteria : criteriaQuery.getCriteria().getSubCriteria()) {
|
||||
for (Criteria chainedCriteria : subCriteria.getCriteriaChain()) {
|
||||
updateCriteria(chainedCriteria, persistentEntity);
|
||||
updatePropertiesInCriteria(chainedCriteria, persistentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
private void updatePropertiesInCriteria(Criteria criteria, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Field field = criteria.getField();
|
||||
|
||||
|
||||
+3
@@ -128,6 +128,9 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
|
||||
|
||||
} else if (tree.isCountProjection()) {
|
||||
result = elasticsearchOperations.count(query, clazz, index);
|
||||
} else if (tree.isExistsProjection()) {
|
||||
long count = elasticsearchOperations.count(query, clazz, index);
|
||||
result = count > 0;
|
||||
} else {
|
||||
result = elasticsearchOperations.searchOne(query, clazz, index);
|
||||
}
|
||||
|
||||
+5
-2
@@ -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,7 +60,6 @@ final public class StringQueryUtil {
|
||||
Object parameter = accessor.getBindableValue(index);
|
||||
String parameterValue = "null";
|
||||
|
||||
// noinspection ConstantConditions
|
||||
if (parameter != null) {
|
||||
|
||||
parameterValue = convert(parameter);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 4.4.2 (2021.2.2)
|
||||
Spring Data Elasticsearch 4.4.5 (2021.2.5)
|
||||
Copyright (c) [2013-2021] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -36,6 +36,9 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+49
@@ -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
|
||||
}
|
||||
|
||||
+49
@@ -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
|
||||
|
||||
+506
-41
@@ -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) {
|
||||
|
||||
+1
-1
@@ -241,7 +241,7 @@ abstract class CallbackIntegrationTests {
|
||||
@Id private String id;
|
||||
@Nullable private String text;
|
||||
|
||||
@ReadOnlyProperty
|
||||
// @ReadOnlyProperty
|
||||
@Nullable private String className;
|
||||
|
||||
@Nullable
|
||||
|
||||
-2
@@ -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) {
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
|
||||
+13
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
@@ -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() {
|
||||
|
||||
@@ -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.6
|
||||
#
|
||||
#
|
||||
# needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13
|
||||
|
||||
Reference in New Issue
Block a user