diff --git a/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java b/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java index 8fbba40c6..257ddde1e 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java @@ -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 read(Class type, Document source) { - TypeInformation typeHint = ClassTypeInformation.from((Class) ClassUtils.getUserClass(type)); - R r = read(typeHint, source); + TypeInformation typeInformation = ClassTypeInformation.from((Class) 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 read(TypeInformation type, Map source) { + private R read(TypeInformation typeInformation, Map source) { Assert.notNull(source, "Source must not be null!"); - TypeInformation typeToUse = typeMapper.readType(source, type); + TypeInformation typeToUse = typeMapper.readType(source, typeInformation); Class 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 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) value); + return getCollectionWithSingleElement(type, collectionComponentType, o); + } return (T) read(type, (Map) 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 getCollectionWithSingleElement(TypeInformation collectionType, + TypeInformation componentType, Object element) { + Collection 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 updateFieldNames(List 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 updateFieldNames(List 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(); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverterUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverterUnitTests.java index 7d388c394..23c7c1ec5 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverterUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverterUnitTests.java @@ -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 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 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 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 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 names = entity.getChildrenSet().stream().map(ImmutableEntityWithCollections.Child::getName) + .collect(Collectors.toList()); + assertThat(names).containsExactlyInAnyOrder("child1", "child2"); + } + private Map 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 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 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 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 cars; + @Id + @Nullable String id; + @Field(type = FieldType.Text) + @Nullable private String name; + @Field(type = FieldType.Nested) + @Nullable private List 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 stringList; + + @Field(type = FieldType.Keyword) + @Nullable private Set stringSet; + + @Field(type = FieldType.Object) + @Nullable private List childrenList; + + @Field(type = FieldType.Object) + @Nullable private Set childrenSet; + + @Nullable + public List getStringList() { + return stringList; + } + + public void setStringList(@Nullable List stringList) { + this.stringList = stringList; + } + + @Nullable + public Set getStringSet() { + return stringSet; + } + + public void setStringSet(@Nullable Set stringSet) { + this.stringSet = stringSet; + } + + @Nullable + public List getChildrenList() { + return childrenList; + } + + public void setChildrenList(@Nullable List childrenList) { + this.childrenList = childrenList; + } + + @Nullable + public Set getChildrenSet() { + return childrenSet; + } + + public void setChildrenSet(@Nullable Set 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 stringList; + + @Field(type = FieldType.Keyword) + @Nullable private Set stringSet; + + @Field(type = FieldType.Object) + @Nullable private List childrenList; + + @Field(type = FieldType.Object) + @Nullable private Set childrenSet; + + public ImmutableEntityWithCollections(@Nullable List stringList, @Nullable Set stringSet, + @Nullable List childrenList, @Nullable Set childrenSet) { + this.stringList = stringList; + this.stringSet = stringSet; + this.childrenList = childrenList; + this.childrenSet = childrenSet; + } + + @Nullable + public List getStringList() { + return stringList; + } + + @Nullable + public Set getStringSet() { + return stringSet; + } + + @Nullable + public List getChildrenList() { + return childrenList; + } + + @Nullable + public Set 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) {