1
0
mirror of synced 2026-05-23 04:33:17 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Spring Builds 200a18b4b1 Release version 4.3.8 (2021.1.8).
See #2297
2022-10-13 07:28:26 +00:00
Spring Builds d0f7f79968 Prepare 4.3.8 (2021.1.8).
See #2297
2022-10-13 07:26:04 +00:00
Peter-Josef Meisch 027ff29436 Escape backslash in StringQuery.
Original Pull Request
Closes #2326

(cherry picked from commit 03ecc48b09)
(cherry picked from commit 2fa15f772a)
2022-10-11 22:49:30 +02:00
Spring Builds 69d80bd602 After release cleanups.
See #2220
2022-09-19 09:04:59 +00:00
Spring Builds f7a788ce2d Prepare next development iteration.
See #2220
2022-09-19 09:04:47 +00:00
Spring Builds 230616a3c3 Release version 4.3.7 (2021.1.7).
See #2220
2022-09-19 08:40:25 +00:00
Spring Builds 44ae2b2af5 Prepare 4.3.7 (2021.1.7).
See #2220
2022-09-19 08:38:11 +00:00
Peter-Josef Meisch e934a2eea8 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)
(cherry picked from commit 346c5cce58)
2022-08-29 21:22:12 +02:00
Peter-Josef Meisch 468b573b77 Fix update call in reactive client (Elasticsearch 7 client)
Original Pull Request #2281
Closes #2276

(cherry picked from commit 8377f64a8a)
(cherry picked from commit a3ebd8be78)
2022-08-26 08:41:26 +02:00
Peter-Josef Meisch 9a442df5b9 Fix exists query for imperative repository implementation.
Original Pull Request #2236
Closes #2162

(cherry picked from commit 373be49f97)
(cherry picked from commit be70a398be)
2022-07-22 22:32:39 +02:00
Mark Paluch 7b96041bc3 Refine CI triggers.
See #2220
2022-07-18 09:18:28 +02:00
Christoph Strobl ac73ba4774 After release cleanups.
See #2185
2022-07-15 10:47:37 +02:00
Christoph Strobl fa7a285606 Prepare next development iteration.
See #2185
2022-07-15 10:47:34 +02:00
12 changed files with 702 additions and 107 deletions
Vendored
+4 -2
View File
@@ -20,8 +20,9 @@ pipeline {
stages {
stage("test: baseline (main)") {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
@@ -108,8 +109,9 @@ pipeline {
stage('Release to artifactory') {
when {
beforeAgent(true)
anyOf {
branch 'main'
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
+3 -3
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.3.6</version>
<version>4.3.8</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.6.6</version>
<version>2.6.8</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -21,7 +21,7 @@
<elasticsearch>7.15.2</elasticsearch>
<log4j>2.17.0</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>2.6.6</springdata.commons>
<springdata.commons>2.6.8</springdata.commons>
<testcontainers>1.15.3</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<java-module-name>spring.data.elasticsearch</java-module-name>
@@ -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());
@@ -21,6 +21,14 @@ import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -51,16 +59,7 @@ import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.mapping.model.*;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.format.datetime.DateFormatterRegistrar;
@@ -196,7 +195,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 {
@@ -215,10 +214,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);
@@ -229,11 +235,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)) {
@@ -251,8 +257,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) {
@@ -370,7 +376,6 @@ public class MappingElasticsearchConverter
}
return result;
}
private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProvider(
@@ -460,12 +465,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());
@@ -1131,17 +1168,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);
@@ -1174,14 +1212,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");
@@ -1190,17 +1236,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();
@@ -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);
}
@@ -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);
+3 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 4.3.6 (2021.1.6)
Spring Data Elasticsearch 4.3.8 (2021.1.8)
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,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -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
@@ -33,6 +33,8 @@ 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.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
@@ -623,7 +625,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 +668,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 +699,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 +717,8 @@ public class MappingElasticsearchConverterUnitTests {
assertThat(document).isEqualTo(notificationAsMap);
}
@Test // DATAES-763
@Test
// DATAES-763
void readEntityWithMapDataType() {
Document document = Document.create();
@@ -729,7 +735,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 +750,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 +769,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 +781,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 +791,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 +809,8 @@ public class MappingElasticsearchConverterUnitTests {
assertEquals(expected, json, false);
}
@Test // DATAES-857
@Test
// DATAES-857
void shouldWriteEntityWithListOfGeoPoints() throws JSONException {
GeoPointListEntity entity = new GeoPointListEntity();
@@ -827,7 +839,8 @@ public class MappingElasticsearchConverterUnitTests {
assertEquals(expected, json, false);
}
@Test // DATAES-857
@Test
// DATAES-857
void shouldReadEntityWithListOfGeoPoints() {
String json = "{\n" + //
@@ -852,7 +865,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 +1523,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();
@@ -1561,12 +1874,15 @@ public class MappingElasticsearchConverterUnitTests {
}
static class Person {
@Nullable @Id String id;
@Nullable
@Id String id;
@Nullable String name;
@Nullable @Field(name = "first-name") String firstName;
@Nullable @Field(name = "last-name") String lastName;
@Nullable @Field(name = "birth-date", type = FieldType.Date, format = {},
pattern = "dd.MM.uuuu") LocalDate birthDate;
@Nullable
@Field(name = "first-name") String firstName;
@Nullable
@Field(name = "last-name") String lastName;
@Nullable
@Field(name = "birth-date", type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") LocalDate birthDate;
@Nullable Gender gender;
@Nullable Address address;
@Nullable List<Person> coWorkers;
@@ -1675,34 +1991,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;
}
@@ -1724,8 +2052,10 @@ public class MappingElasticsearchConverterUnitTests {
}
static class LocalDatesEntity {
@Nullable @Id private String id;
@Nullable @Field(name = "dates", type = FieldType.Date, format = DateFormat.custom,
@Nullable
@Id private String id;
@Nullable
@Field(name = "dates", type = FieldType.Date, format = DateFormat.custom,
pattern = "dd.MM.uuuu") private List<LocalDate> dates;
@Nullable
@@ -1787,15 +2117,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);
}
@@ -1821,10 +2154,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;
@@ -1857,17 +2192,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);
}
@@ -1898,10 +2237,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;
@@ -1948,17 +2289,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;
}
@@ -1985,10 +2330,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;
@@ -2124,16 +2471,20 @@ public class MappingElasticsearchConverterUnitTests {
@org.springframework.data.elasticsearch.annotations.Document(indexName = "test-index-geo-core-entity-mapper")
static class GeoEntity {
@Nullable @Id private String id;
@Nullable
@Id private String id;
// geo shape - Spring Data
@Nullable private Box box;
@Nullable private Circle circle;
@Nullable private Polygon polygon;
// geo point - Custom implementation + Spring Data
@Nullable @GeoPointField private Point pointA;
@Nullable
@GeoPointField private Point pointA;
@Nullable private GeoPoint pointB;
@Nullable @GeoPointField private String pointC;
@Nullable @GeoPointField private double[] pointD;
@Nullable
@GeoPointField private String pointC;
@Nullable
@GeoPointField private double[] pointD;
@Nullable
public String getId() {
@@ -2237,7 +2588,8 @@ public class MappingElasticsearchConverterUnitTests {
}
static class EntityWithListProperty {
@Nullable @Id private String id;
@Nullable
@Id private String id;
@Nullable private List<String> values;
@Nullable
@@ -2283,7 +2635,8 @@ public class MappingElasticsearchConverterUnitTests {
}
static class EntityWithObject {
@Nullable @Id private String id;
@Nullable
@Id private String id;
@Nullable private Object content;
@Nullable
@@ -2306,9 +2659,12 @@ public class MappingElasticsearchConverterUnitTests {
}
static class EntityWithNullField {
@Nullable @Id private String id;
@Nullable @Field(type = FieldType.Text) private String notSaved;
@Nullable @Field(type = FieldType.Text, storeNullValue = true) private String saved;
@Nullable
@Id private String id;
@Nullable
@Field(type = FieldType.Text) private String notSaved;
@Nullable
@Field(type = FieldType.Text, storeNullValue = true) private String saved;
@Nullable
public String getId() {
@@ -2341,9 +2697,12 @@ public class MappingElasticsearchConverterUnitTests {
private static class ElectricCar extends Car {}
private static class PersonWithCars {
@Id @Nullable String id;
@Field(type = FieldType.Text) @Nullable private String name;
@Field(type = FieldType.Nested) @Nullable private List<? extends Car> cars;
@Id
@Nullable String id;
@Field(type = FieldType.Text)
@Nullable private String name;
@Field(type = FieldType.Nested)
@Nullable private List<? extends Car> cars;
@Nullable
public String getId() {
@@ -2374,9 +2733,12 @@ public class MappingElasticsearchConverterUnitTests {
}
private static class EntityWithCustomValueConverters {
@Nullable @Id private String id;
@Nullable @ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
@Nullable @ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
@Nullable
@Id private String id;
@Nullable
@ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
@Nullable
@ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
@Nullable private String dontConvert;
@Nullable
@@ -2442,6 +2804,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) {
@@ -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 {
@@ -321,6 +321,17 @@ class QueryKeywordsTests {
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 = "test-index-product-query-keywords")
static class Product {
@@ -459,6 +470,8 @@ class QueryKeywordsTests {
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;
@@ -139,6 +142,21 @@ public 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();
@@ -150,6 +168,8 @@ public class ReactiveQueryKeywordsIntegrationTests {
Flux<SearchHit<SampleEntity>> findByMessageIsNotEmpty();
Flux<SearchHit<SampleEntity>> findByMessageIsEmpty();
Mono<Boolean> existsByMessage(String message);
}
private void loadEntities() {