mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-07-16 07:13:27 +00:00
Add custom property converters.
Original Pull Request #1953 Closes #1945
This commit is contained in:
parent
464fc31d87
commit
7ae55b9e75
@ -71,3 +71,8 @@ Spring Data Elasticsearch now uses `org.springframework.data.elasticsearch.core.
|
|||||||
=== Completion classes
|
=== Completion classes
|
||||||
|
|
||||||
The classes from the package `org.springframework.data.elasticsearch.core.completion` have been moved to `org.springframework.data.elasticsearch.core.suggest`.
|
The classes from the package `org.springframework.data.elasticsearch.core.completion` have been moved to `org.springframework.data.elasticsearch.core.suggest`.
|
||||||
|
|
||||||
|
=== Other renamings
|
||||||
|
|
||||||
|
The `org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter` interface has been renamed to `org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter`.
|
||||||
|
Likewise the implementations classes named _XXPersistentPropertyConverter_ have been renamed to _XXPropertyValueConverter_.
|
||||||
|
@ -56,6 +56,8 @@ This means, that no mapping entry is written for the property and that Elasticse
|
|||||||
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom analyzers and normalizer.
|
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom analyzers and normalizer.
|
||||||
* `@GeoPoint`: Marks a field as _geo_point_ datatype.
|
* `@GeoPoint`: Marks a field as _geo_point_ datatype.
|
||||||
Can be omitted if the field is an instance of the `GeoPoint` class.
|
Can be omitted if the field is an instance of the `GeoPoint` class.
|
||||||
|
* `@ValueConverter` defines a class to be used to convert the given property.
|
||||||
|
In difference to a registered Spring `Converter` this only converts the annotated property and not every property of the given type.
|
||||||
|
|
||||||
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
||||||
|
|
||||||
@ -184,6 +186,7 @@ public class Person { <1>
|
|||||||
"lastname" : "Connor"
|
"lastname" : "Connor"
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> By default the domain types class name is used for the type hint.
|
<1> By default the domain types class name is used for the type hint.
|
||||||
====
|
====
|
||||||
|
|
||||||
@ -211,6 +214,7 @@ public class Person {
|
|||||||
"id" : ...
|
"id" : ...
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> The configured alias is used when writing the entity.
|
<1> The configured alias is used when writing the entity.
|
||||||
====
|
====
|
||||||
|
|
||||||
@ -421,6 +425,7 @@ public class Config extends AbstractElasticsearchConfiguration {
|
|||||||
"localidad" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
"localidad" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> Add `Converter` implementations.
|
<1> Add `Converter` implementations.
|
||||||
<2> Set up the `Converter` used for writing `DomainType` to Elasticsearch.
|
<2> Set up the `Converter` used for writing `DomainType` to Elasticsearch.
|
||||||
<3> Set up the `Converter` used for reading `DomainType` from search result.
|
<3> Set up the `Converter` used for reading `DomainType` from search result.
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.elasticsearch.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation to put on a property of an entity to define a value converter which can convert the property to a type
|
||||||
|
* that Elasticsearch understands and back.
|
||||||
|
*
|
||||||
|
* @author Peter-Josef Meisch
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
public @interface ValueConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the class implementing the {@link PropertyValueConverter} interface. If this is a normal class, it must
|
||||||
|
* provide a default constructor with no arguments. If this is an enum and thus implementing a singleton by enum it
|
||||||
|
* must only have one enum value.
|
||||||
|
*
|
||||||
|
* @return the class to use for conversion
|
||||||
|
*/
|
||||||
|
Class<? extends PropertyValueConverter> value();
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.core.convert;
|
package org.springframework.data.elasticsearch.core.convert;
|
||||||
|
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||||
import org.springframework.data.mapping.PersistentProperty;
|
import org.springframework.data.mapping.PersistentProperty;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -23,11 +23,11 @@ import org.springframework.util.Assert;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPersistentPropertyConverter implements ElasticsearchPersistentPropertyConverter {
|
public abstract class AbstractPropertyValueConverter implements PropertyValueConverter {
|
||||||
|
|
||||||
private final PersistentProperty<?> property;
|
private final PersistentProperty<?> property;
|
||||||
|
|
||||||
public AbstractPersistentPropertyConverter(PersistentProperty<?> property) {
|
public AbstractPropertyValueConverter(PersistentProperty<?> property) {
|
||||||
|
|
||||||
Assert.notNull(property, "property must not be null.");
|
Assert.notNull(property, "property must not be null.");
|
||||||
this.property = property;
|
this.property = property;
|
@ -26,14 +26,14 @@ import org.springframework.util.Assert;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRangePersistentPropertyConverter<T> extends AbstractPersistentPropertyConverter {
|
public abstract class AbstractRangePropertyValueConverter<T> extends AbstractPropertyValueConverter {
|
||||||
|
|
||||||
protected static final String LT_FIELD = "lt";
|
protected static final String LT_FIELD = "lt";
|
||||||
protected static final String LTE_FIELD = "lte";
|
protected static final String LTE_FIELD = "lte";
|
||||||
protected static final String GT_FIELD = "gt";
|
protected static final String GT_FIELD = "gt";
|
||||||
protected static final String GTE_FIELD = "gte";
|
protected static final String GTE_FIELD = "gte";
|
||||||
|
|
||||||
public AbstractRangePersistentPropertyConverter(PersistentProperty<?> property) {
|
public AbstractRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||||
super(property);
|
super(property);
|
||||||
}
|
}
|
||||||
|
|
@ -26,14 +26,13 @@ import org.springframework.data.mapping.PersistentProperty;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class DatePersistentPropertyConverter extends AbstractPersistentPropertyConverter {
|
public class DatePropertyValueConverter extends AbstractPropertyValueConverter {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(DatePersistentPropertyConverter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(DatePropertyValueConverter.class);
|
||||||
|
|
||||||
private final List<ElasticsearchDateConverter> dateConverters;
|
private final List<ElasticsearchDateConverter> dateConverters;
|
||||||
|
|
||||||
public DatePersistentPropertyConverter(PersistentProperty<?> property,
|
public DatePropertyValueConverter(PersistentProperty<?> property, List<ElasticsearchDateConverter> dateConverters) {
|
||||||
List<ElasticsearchDateConverter> dateConverters) {
|
|
||||||
|
|
||||||
super(property);
|
super(property);
|
||||||
this.dateConverters = dateConverters;
|
this.dateConverters = dateConverters;
|
@ -26,13 +26,13 @@ import org.springframework.data.mapping.PersistentProperty;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class DateRangePersistentPropertyConverter extends AbstractRangePersistentPropertyConverter<Date> {
|
public class DateRangePropertyValueConverter extends AbstractRangePropertyValueConverter<Date> {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(DateRangePersistentPropertyConverter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(DateRangePropertyValueConverter.class);
|
||||||
|
|
||||||
private final List<ElasticsearchDateConverter> dateConverters;
|
private final List<ElasticsearchDateConverter> dateConverters;
|
||||||
|
|
||||||
public DateRangePersistentPropertyConverter(PersistentProperty<?> property,
|
public DateRangePropertyValueConverter(PersistentProperty<?> property,
|
||||||
List<ElasticsearchDateConverter> dateConverters) {
|
List<ElasticsearchDateConverter> dateConverters) {
|
||||||
|
|
||||||
super(property);
|
super(property);
|
@ -19,6 +19,7 @@ import org.springframework.data.convert.EntityConverter;
|
|||||||
import org.springframework.data.elasticsearch.core.document.Document;
|
import org.springframework.data.elasticsearch.core.document.Document;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||||
|
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.query.Query;
|
import org.springframework.data.elasticsearch.core.query.Query;
|
||||||
import org.springframework.data.projection.ProjectionFactory;
|
import org.springframework.data.projection.ProjectionFactory;
|
||||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||||
@ -93,8 +94,7 @@ public interface ElasticsearchConverter
|
|||||||
/**
|
/**
|
||||||
* Updates a {@link Query} by renaming the property names in the query to the correct mapped field names and the
|
* Updates a {@link Query} by renaming the property names in the query to the correct mapped field names and the
|
||||||
* values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
|
* values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
|
||||||
* {@link org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter}. If
|
* {@link PropertyValueConverter}. If domainClass is null it's a noop.
|
||||||
* domainClass is null it's a noop.
|
|
||||||
*
|
*
|
||||||
* @param query the query that is internally updated, must not be {@literal null}
|
* @param query the query that is internally updated, must not be {@literal null}
|
||||||
* @param domainClass the class of the object that is searched with the query
|
* @param domainClass the class of the object that is searched with the query
|
||||||
|
@ -39,7 +39,7 @@ import org.springframework.data.elasticsearch.core.document.Document;
|
|||||||
import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
||||||
@ -425,8 +425,9 @@ public class MappingElasticsearchConverter
|
|||||||
|
|
||||||
Class<?> rawType = type.getType();
|
Class<?> rawType = type.getType();
|
||||||
|
|
||||||
if (property.hasPropertyConverter()) {
|
if (property.hasPropertyValueConverter()) {
|
||||||
value = propertyConverterRead(property, value);
|
// noinspection unchecked
|
||||||
|
return (R) propertyConverterRead(property, value);
|
||||||
} else if (TemporalAccessor.class.isAssignableFrom(property.getType())
|
} else if (TemporalAccessor.class.isAssignableFrom(property.getType())
|
||||||
&& !conversions.hasCustomReadTarget(value.getClass(), rawType)) {
|
&& !conversions.hasCustomReadTarget(value.getClass(), rawType)) {
|
||||||
|
|
||||||
@ -466,8 +467,7 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object propertyConverterRead(ElasticsearchPersistentProperty property, Object source) {
|
private Object propertyConverterRead(ElasticsearchPersistentProperty property, Object source) {
|
||||||
ElasticsearchPersistentPropertyConverter propertyConverter = Objects
|
PropertyValueConverter propertyValueConverter = Objects.requireNonNull(property.getPropertyValueConverter());
|
||||||
.requireNonNull(property.getPropertyConverter());
|
|
||||||
|
|
||||||
if (source instanceof String[]) {
|
if (source instanceof String[]) {
|
||||||
// convert to a List
|
// convert to a List
|
||||||
@ -475,18 +475,19 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (source instanceof List) {
|
if (source instanceof List) {
|
||||||
source = ((List<?>) source).stream().map(it -> convertOnRead(propertyConverter, it))
|
source = ((List<?>) source).stream().map(it -> convertOnRead(propertyValueConverter, it))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
} else if (source instanceof Set) {
|
} else if (source instanceof Set) {
|
||||||
source = ((Set<?>) source).stream().map(it -> convertOnRead(propertyConverter, it)).collect(Collectors.toSet());
|
source = ((Set<?>) source).stream().map(it -> convertOnRead(propertyValueConverter, it))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
} else {
|
} else {
|
||||||
source = convertOnRead(propertyConverter, source);
|
source = convertOnRead(propertyValueConverter, source);
|
||||||
}
|
}
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object convertOnRead(ElasticsearchPersistentPropertyConverter propertyConverter, Object source) {
|
private Object convertOnRead(PropertyValueConverter propertyValueConverter, Object source) {
|
||||||
return propertyConverter.read(source);
|
return propertyValueConverter.read(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -897,7 +898,7 @@ public class MappingElasticsearchConverter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.hasPropertyConverter()) {
|
if (property.hasPropertyValueConverter()) {
|
||||||
value = propertyConverterWrite(property, value);
|
value = propertyConverterWrite(property, value);
|
||||||
sink.set(property, value);
|
sink.set(property, value);
|
||||||
} else if (TemporalAccessor.class.isAssignableFrom(property.getActualType())
|
} else if (TemporalAccessor.class.isAssignableFrom(property.getActualType())
|
||||||
@ -1070,15 +1071,14 @@ public class MappingElasticsearchConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object propertyConverterWrite(ElasticsearchPersistentProperty property, Object value) {
|
private Object propertyConverterWrite(ElasticsearchPersistentProperty property, Object value) {
|
||||||
ElasticsearchPersistentPropertyConverter propertyConverter = Objects
|
PropertyValueConverter propertyValueConverter = Objects.requireNonNull(property.getPropertyValueConverter());
|
||||||
.requireNonNull(property.getPropertyConverter());
|
|
||||||
|
|
||||||
if (value instanceof List) {
|
if (value instanceof List) {
|
||||||
value = ((List<?>) value).stream().map(propertyConverter::write).collect(Collectors.toList());
|
value = ((List<?>) value).stream().map(propertyValueConverter::write).collect(Collectors.toList());
|
||||||
} else if (value instanceof Set) {
|
} else if (value instanceof Set) {
|
||||||
value = ((Set<?>) value).stream().map(propertyConverter::write).collect(Collectors.toSet());
|
value = ((Set<?>) value).stream().map(propertyValueConverter::write).collect(Collectors.toSet());
|
||||||
} else {
|
} else {
|
||||||
value = propertyConverter.write(value);
|
value = propertyValueConverter.write(value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -1252,18 +1252,18 @@ public class MappingElasticsearchConverter
|
|||||||
|
|
||||||
if (persistentProperty != null) {
|
if (persistentProperty != null) {
|
||||||
|
|
||||||
if (persistentProperty.hasPropertyConverter()) {
|
if (persistentProperty.hasPropertyValueConverter()) {
|
||||||
ElasticsearchPersistentPropertyConverter propertyConverter = Objects
|
PropertyValueConverter propertyValueConverter = Objects
|
||||||
.requireNonNull(persistentProperty.getPropertyConverter());
|
.requireNonNull(persistentProperty.getPropertyValueConverter());
|
||||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||||
Object value = criteriaEntry.getValue();
|
Object value = criteriaEntry.getValue();
|
||||||
if (value.getClass().isArray()) {
|
if (value.getClass().isArray()) {
|
||||||
Object[] objects = (Object[]) value;
|
Object[] objects = (Object[]) value;
|
||||||
for (int i = 0; i < objects.length; i++) {
|
for (int i = 0; i < objects.length; i++) {
|
||||||
objects[i] = propertyConverter.write(objects[i]);
|
objects[i] = propertyValueConverter.write(objects[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
criteriaEntry.setValue(propertyConverter.write(value));
|
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,9 @@ import org.springframework.data.mapping.PersistentProperty;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class NumberRangePersistentPropertyConverter extends AbstractRangePersistentPropertyConverter<Number> {
|
public class NumberRangePropertyValueConverter extends AbstractRangePropertyValueConverter<Number> {
|
||||||
|
|
||||||
public NumberRangePersistentPropertyConverter(PersistentProperty<?> property) {
|
public NumberRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||||
super(property);
|
super(property);
|
||||||
}
|
}
|
||||||
|
|
@ -26,13 +26,13 @@ import org.springframework.data.mapping.PersistentProperty;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class TemporalPersistentPropertyConverter extends AbstractPersistentPropertyConverter {
|
public class TemporalPropertyValueConverter extends AbstractPropertyValueConverter {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalPersistentPropertyConverter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalPropertyValueConverter.class);
|
||||||
|
|
||||||
private final List<ElasticsearchDateConverter> dateConverters;
|
private final List<ElasticsearchDateConverter> dateConverters;
|
||||||
|
|
||||||
public TemporalPersistentPropertyConverter(PersistentProperty<?> property,
|
public TemporalPropertyValueConverter(PersistentProperty<?> property,
|
||||||
List<ElasticsearchDateConverter> dateConverters) {
|
List<ElasticsearchDateConverter> dateConverters) {
|
||||||
|
|
||||||
super(property);
|
super(property);
|
@ -27,14 +27,13 @@ import org.springframework.util.Assert;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class TemporalRangePersistentPropertyConverter
|
public class TemporalRangePropertyValueConverter extends AbstractRangePropertyValueConverter<TemporalAccessor> {
|
||||||
extends AbstractRangePersistentPropertyConverter<TemporalAccessor> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalRangePersistentPropertyConverter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalRangePropertyValueConverter.class);
|
||||||
|
|
||||||
private final List<ElasticsearchDateConverter> dateConverters;
|
private final List<ElasticsearchDateConverter> dateConverters;
|
||||||
|
|
||||||
public TemporalRangePersistentPropertyConverter(PersistentProperty<?> property,
|
public TemporalRangePropertyValueConverter(PersistentProperty<?> property,
|
||||||
List<ElasticsearchDateConverter> dateConverters) {
|
List<ElasticsearchDateConverter> dateConverters) {
|
||||||
|
|
||||||
super(property);
|
super(property);
|
@ -48,17 +48,17 @@ public interface ElasticsearchPersistentProperty extends PersistentProperty<Elas
|
|||||||
boolean isSeqNoPrimaryTermProperty();
|
boolean isSeqNoPrimaryTermProperty();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if an {@link ElasticsearchPersistentPropertyConverter} is available for this instance.
|
* @return true if an {@link PropertyValueConverter} is available for this instance.
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
boolean hasPropertyConverter();
|
boolean hasPropertyValueConverter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link ElasticsearchPersistentPropertyConverter} for this instance.
|
* @return the {@link PropertyValueConverter} for this instance.
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ElasticsearchPersistentPropertyConverter getPropertyConverter();
|
PropertyValueConverter getPropertyValueConverter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the property may be read.
|
* Returns true if the property may be read.
|
||||||
|
@ -16,26 +16,26 @@
|
|||||||
package org.springframework.data.elasticsearch.core.mapping;
|
package org.springframework.data.elasticsearch.core.mapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface defining methods to convert a persistent property value to an elasticsearch property value and back.
|
* Interface defining methods to convert the value of an entity-property to a value in Elasticsearch and back.
|
||||||
*
|
*
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
*/
|
*/
|
||||||
public interface ElasticsearchPersistentPropertyConverter {
|
public interface PropertyValueConverter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a persistent property value to an elasticsearch property value.
|
* Converts a property value to an elasticsearch value.
|
||||||
*
|
*
|
||||||
* @param value the persistent property value to convert, must not be {@literal null}
|
* @param value the value to convert, must not be {@literal null}
|
||||||
* @return The elasticsearch property value.
|
* @return The elasticsearch property value, must not be {@literal null}
|
||||||
*/
|
*/
|
||||||
Object write(Object value);
|
Object write(Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an elasticsearch property value to a persistent property value.
|
* Converts an elasticsearch property value to a property value.
|
||||||
*
|
*
|
||||||
* @param value the elasticsearch property value to convert, must not be {@literal null}
|
* @param value the elasticsearch property value to convert, must not be {@literal null}
|
||||||
* @return The persistent property value.
|
* @return The converted value, must not be {@literal null}
|
||||||
*/
|
*/
|
||||||
Object read(Object value);
|
Object read(Object value);
|
||||||
}
|
}
|
@ -23,24 +23,26 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
||||||
import org.springframework.data.elasticsearch.annotations.GeoShapeField;
|
import org.springframework.data.elasticsearch.annotations.GeoShapeField;
|
||||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.ValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.Range;
|
import org.springframework.data.elasticsearch.core.Range;
|
||||||
import org.springframework.data.elasticsearch.core.suggest.Completion;
|
import org.springframework.data.elasticsearch.core.convert.DatePropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.convert.DatePersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.convert.DateRangePropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.convert.DateRangePersistentPropertyConverter;
|
|
||||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchDateConverter;
|
import org.springframework.data.elasticsearch.core.convert.ElasticsearchDateConverter;
|
||||||
import org.springframework.data.elasticsearch.core.convert.NumberRangePersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.convert.NumberRangePropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.convert.TemporalPersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.convert.TemporalPropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.convert.TemporalRangePersistentPropertyConverter;
|
import org.springframework.data.elasticsearch.core.convert.TemporalRangePropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoJson;
|
import org.springframework.data.elasticsearch.core.geo.GeoJson;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||||
import org.springframework.data.elasticsearch.core.join.JoinField;
|
import org.springframework.data.elasticsearch.core.join.JoinField;
|
||||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||||
|
import org.springframework.data.elasticsearch.core.suggest.Completion;
|
||||||
import org.springframework.data.mapping.Association;
|
import org.springframework.data.mapping.Association;
|
||||||
import org.springframework.data.mapping.MappingException;
|
import org.springframework.data.mapping.MappingException;
|
||||||
import org.springframework.data.mapping.PersistentEntity;
|
import org.springframework.data.mapping.PersistentEntity;
|
||||||
@ -75,7 +77,7 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
private final boolean isId;
|
private final boolean isId;
|
||||||
private final boolean isSeqNoPrimaryTerm;
|
private final boolean isSeqNoPrimaryTerm;
|
||||||
private final @Nullable String annotatedFieldName;
|
private final @Nullable String annotatedFieldName;
|
||||||
@Nullable private ElasticsearchPersistentPropertyConverter propertyConverter;
|
@Nullable private PropertyValueConverter propertyValueConverter;
|
||||||
private final boolean storeNullValue;
|
private final boolean storeNullValue;
|
||||||
|
|
||||||
public SimpleElasticsearchPersistentProperty(Property property,
|
public SimpleElasticsearchPersistentProperty(Property property,
|
||||||
@ -98,20 +100,20 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
throw new MappingException("@Field annotation must not be used on a @MultiField property.");
|
throw new MappingException("@Field annotation must not be used on a @MultiField property.");
|
||||||
}
|
}
|
||||||
|
|
||||||
initPropertyConverter();
|
initPropertyValueConverter();
|
||||||
|
|
||||||
storeNullValue = isField && getRequiredAnnotation(Field.class).storeNullValue();
|
storeNullValue = isField && getRequiredAnnotation(Field.class).storeNullValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPropertyConverter() {
|
public boolean hasPropertyValueConverter() {
|
||||||
return propertyConverter != null;
|
return propertyValueConverter != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public ElasticsearchPersistentPropertyConverter getPropertyConverter() {
|
public PropertyValueConverter getPropertyValueConverter() {
|
||||||
return propertyConverter;
|
return propertyValueConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -136,7 +138,13 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
/**
|
/**
|
||||||
* Initializes the property converter for this {@link PersistentProperty}, if any.
|
* Initializes the property converter for this {@link PersistentProperty}, if any.
|
||||||
*/
|
*/
|
||||||
private void initPropertyConverter() {
|
private void initPropertyValueConverter() {
|
||||||
|
|
||||||
|
initPropertyValueConverterFromAnnotation();
|
||||||
|
|
||||||
|
if (hasPropertyValueConverter()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Class<?> actualType = getActualTypeOrNull();
|
Class<?> actualType = getActualTypeOrNull();
|
||||||
if (actualType == null) {
|
if (actualType == null) {
|
||||||
@ -158,9 +166,9 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TemporalAccessor.class.isAssignableFrom(actualType)) {
|
if (TemporalAccessor.class.isAssignableFrom(actualType)) {
|
||||||
propertyConverter = new TemporalPersistentPropertyConverter(this, dateConverters);
|
propertyValueConverter = new TemporalPropertyValueConverter(this, dateConverters);
|
||||||
} else if (Date.class.isAssignableFrom(actualType)) {
|
} else if (Date.class.isAssignableFrom(actualType)) {
|
||||||
propertyConverter = new DatePersistentPropertyConverter(this, dateConverters);
|
propertyValueConverter = new DatePropertyValueConverter(this, dateConverters);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Unsupported type '{}' for date property '{}'.", actualType, getName());
|
LOGGER.warn("Unsupported type '{}' for date property '{}'.", actualType, getName());
|
||||||
}
|
}
|
||||||
@ -179,9 +187,9 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
|
|
||||||
Class<?> genericType = getTypeInformation().getTypeArguments().get(0).getType();
|
Class<?> genericType = getTypeInformation().getTypeArguments().get(0).getType();
|
||||||
if (TemporalAccessor.class.isAssignableFrom(genericType)) {
|
if (TemporalAccessor.class.isAssignableFrom(genericType)) {
|
||||||
propertyConverter = new TemporalRangePersistentPropertyConverter(this, dateConverters);
|
propertyValueConverter = new TemporalRangePropertyValueConverter(this, dateConverters);
|
||||||
} else if (Date.class.isAssignableFrom(genericType)) {
|
} else if (Date.class.isAssignableFrom(genericType)) {
|
||||||
propertyConverter = new DateRangePersistentPropertyConverter(this, dateConverters);
|
propertyValueConverter = new DateRangePropertyValueConverter(this, dateConverters);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Unsupported generic type '{}' for date range property '{}'.", genericType, getName());
|
LOGGER.warn("Unsupported generic type '{}' for date range property '{}'.", genericType, getName());
|
||||||
}
|
}
|
||||||
@ -205,7 +213,7 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyConverter = new NumberRangePersistentPropertyConverter(this);
|
propertyValueConverter = new NumberRangePropertyValueConverter(this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ip_Range: {
|
case Ip_Range: {
|
||||||
@ -216,6 +224,26 @@ public class SimpleElasticsearchPersistentProperty extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initPropertyValueConverterFromAnnotation() {
|
||||||
|
|
||||||
|
ValueConverter annotation = findAnnotation(ValueConverter.class);
|
||||||
|
|
||||||
|
if (annotation != null) {
|
||||||
|
Class<? extends PropertyValueConverter> clazz = annotation.value();
|
||||||
|
|
||||||
|
if (Enum.class.isAssignableFrom(clazz)) {
|
||||||
|
PropertyValueConverter[] enumConstants = clazz.getEnumConstants();
|
||||||
|
|
||||||
|
if (enumConstants == null || enumConstants.length != 1) {
|
||||||
|
throw new IllegalArgumentException(clazz + " is an enum with more than 1 constant and cannot be used here");
|
||||||
|
}
|
||||||
|
propertyValueConverter = enumConstants[0];
|
||||||
|
} else {
|
||||||
|
propertyValueConverter = BeanUtils.instantiateClass(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<ElasticsearchDateConverter> getDateConverters(Field field, Class<?> actualType) {
|
private List<ElasticsearchDateConverter> getDateConverters(Field field, Class<?> actualType) {
|
||||||
|
|
||||||
DateFormat[] dateFormats = field.format();
|
DateFormat[] dateFormats = field.format();
|
||||||
|
@ -26,14 +26,11 @@ import org.junit.jupiter.api.Test;
|
|||||||
* @author Sascha Woo
|
* @author Sascha Woo
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
public class RangeTests {
|
public class RangeUnitTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldContainsLocalDate() {
|
public void shouldContainsLocalDate() {
|
||||||
|
|
||||||
// given
|
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1)).contains(LocalDate.of(2021, 1, 10)))
|
assertThat(Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1)).contains(LocalDate.of(2021, 1, 10)))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
}
|
}
|
||||||
@ -41,21 +38,17 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldEqualToSameRange() {
|
public void shouldEqualToSameRange() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<LocalDate> range1 = Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1));
|
Range<LocalDate> range1 = Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1));
|
||||||
Range<LocalDate> range2 = Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1));
|
Range<LocalDate> range2 = Range.open(LocalDate.of(2021, 1, 1), LocalDate.of(2021, 2, 1));
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range1).isEqualTo(range2);
|
assertThat(range1).isEqualTo(range2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldHaveClosedBoundaries() {
|
public void shouldHaveClosedBoundaries() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.closed(1, 3);
|
Range<Integer> range = Range.closed(1, 3);
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isTrue();
|
assertThat(range.contains(1)).isTrue();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isTrue();
|
assertThat(range.contains(3)).isTrue();
|
||||||
@ -64,10 +57,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveJustOneValue() {
|
public void shouldHaveJustOneValue() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.just(2);
|
Range<Integer> range = Range.just(2);
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isFalse();
|
assertThat(range.contains(1)).isFalse();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isFalse();
|
assertThat(range.contains(3)).isFalse();
|
||||||
@ -76,10 +67,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveLeftOpenBoundary() {
|
public void shouldHaveLeftOpenBoundary() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.leftOpen(1, 3);
|
Range<Integer> range = Range.leftOpen(1, 3);
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isFalse();
|
assertThat(range.contains(1)).isFalse();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isTrue();
|
assertThat(range.contains(3)).isTrue();
|
||||||
@ -88,10 +77,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveLeftUnboundedAndRightExclusive() {
|
public void shouldHaveLeftUnboundedAndRightExclusive() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.leftUnbounded(Range.Bound.exclusive(3));
|
Range<Integer> range = Range.leftUnbounded(Range.Bound.exclusive(3));
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(0)).isTrue();
|
assertThat(range.contains(0)).isTrue();
|
||||||
assertThat(range.contains(1)).isTrue();
|
assertThat(range.contains(1)).isTrue();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
@ -101,10 +88,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveLeftUnboundedAndRightInclusive() {
|
public void shouldHaveLeftUnboundedAndRightInclusive() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.leftUnbounded(Range.Bound.inclusive(3));
|
Range<Integer> range = Range.leftUnbounded(Range.Bound.inclusive(3));
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(0)).isTrue();
|
assertThat(range.contains(0)).isTrue();
|
||||||
assertThat(range.contains(1)).isTrue();
|
assertThat(range.contains(1)).isTrue();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
@ -114,10 +99,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveOpenBoundaries() {
|
public void shouldHaveOpenBoundaries() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.open(1, 3);
|
Range<Integer> range = Range.open(1, 3);
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isFalse();
|
assertThat(range.contains(1)).isFalse();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isFalse();
|
assertThat(range.contains(3)).isFalse();
|
||||||
@ -126,10 +109,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveRightOpenBoundary() {
|
public void shouldHaveRightOpenBoundary() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.rightOpen(1, 3);
|
Range<Integer> range = Range.rightOpen(1, 3);
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isTrue();
|
assertThat(range.contains(1)).isTrue();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isFalse();
|
assertThat(range.contains(3)).isFalse();
|
||||||
@ -138,10 +119,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveRightUnboundedAndLeftExclusive() {
|
public void shouldHaveRightUnboundedAndLeftExclusive() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.rightUnbounded(Range.Bound.exclusive(1));
|
Range<Integer> range = Range.rightUnbounded(Range.Bound.exclusive(1));
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isFalse();
|
assertThat(range.contains(1)).isFalse();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isTrue();
|
assertThat(range.contains(3)).isTrue();
|
||||||
@ -151,10 +130,8 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldHaveRightUnboundedAndLeftInclusive() {
|
public void shouldHaveRightUnboundedAndLeftInclusive() {
|
||||||
|
|
||||||
// given
|
|
||||||
Range<Integer> range = Range.rightUnbounded(Range.Bound.inclusive(1));
|
Range<Integer> range = Range.rightUnbounded(Range.Bound.inclusive(1));
|
||||||
// when
|
|
||||||
// then
|
|
||||||
assertThat(range.contains(1)).isTrue();
|
assertThat(range.contains(1)).isTrue();
|
||||||
assertThat(range.contains(2)).isTrue();
|
assertThat(range.contains(2)).isTrue();
|
||||||
assertThat(range.contains(3)).isTrue();
|
assertThat(range.contains(3)).isTrue();
|
||||||
@ -164,12 +141,7 @@ public class RangeTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldThrowExceptionIfNotComparable() {
|
public void shouldThrowExceptionIfNotComparable() {
|
||||||
|
|
||||||
// given
|
assertThatThrownBy(() -> Range.just(Collections.singletonList("test"))).isInstanceOf(IllegalArgumentException.class)
|
||||||
// when
|
|
||||||
Throwable thrown = catchThrowable(() -> Range.just(Collections.singletonList("test")));
|
|
||||||
// then
|
|
||||||
assertThat(thrown).isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessageContaining("value must implements Comparable!");
|
.hasMessageContaining("value must implements Comparable!");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -52,6 +52,7 @@ import org.springframework.data.elasticsearch.annotations.DateFormat;
|
|||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
import org.springframework.data.elasticsearch.annotations.GeoPointField;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.ValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.Range;
|
import org.springframework.data.elasticsearch.core.Range;
|
||||||
import org.springframework.data.elasticsearch.core.document.Document;
|
import org.springframework.data.elasticsearch.core.document.Document;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoJsonEntity;
|
import org.springframework.data.elasticsearch.core.geo.GeoJsonEntity;
|
||||||
@ -63,6 +64,7 @@ import org.springframework.data.elasticsearch.core.geo.GeoJsonMultiPolygon;
|
|||||||
import org.springframework.data.elasticsearch.core.geo.GeoJsonPoint;
|
import org.springframework.data.elasticsearch.core.geo.GeoJsonPoint;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoJsonPolygon;
|
import org.springframework.data.elasticsearch.core.geo.GeoJsonPolygon;
|
||||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||||
|
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||||
import org.springframework.data.geo.Box;
|
import org.springframework.data.geo.Box;
|
||||||
@ -71,6 +73,7 @@ import org.springframework.data.geo.Point;
|
|||||||
import org.springframework.data.geo.Polygon;
|
import org.springframework.data.geo.Polygon;
|
||||||
import org.springframework.data.mapping.context.MappingContext;
|
import org.springframework.data.mapping.context.MappingContext;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link MappingElasticsearchConverter}.
|
* Unit tests for {@link MappingElasticsearchConverter}.
|
||||||
@ -1446,6 +1449,53 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
assertEquals(expected, document.toJson(), true);
|
assertEquals(expected, document.toJson(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1945
|
||||||
|
@DisplayName("should write using ValueConverters")
|
||||||
|
void shouldWriteUsingValueConverters() throws JSONException {
|
||||||
|
|
||||||
|
EntityWithCustomValueConverters entity = new EntityWithCustomValueConverters();
|
||||||
|
entity.setId("42");
|
||||||
|
entity.setFieldWithClassBasedConverter("classbased");
|
||||||
|
entity.setFieldWithEnumBasedConverter("enumbased");
|
||||||
|
entity.setDontConvert("Monty Python's Flying Circus");
|
||||||
|
|
||||||
|
String expected = "{\n" + //
|
||||||
|
" \"id\": \"42\",\n" + //
|
||||||
|
" \"fieldWithClassBasedConverter\": \"desabssalc\",\n" + //
|
||||||
|
" \"fieldWithEnumBasedConverter\": \"desabmune\",\n" + //
|
||||||
|
" \"dontConvert\": \"Monty Python's Flying Circus\"\n" + //
|
||||||
|
"}\n"; //
|
||||||
|
|
||||||
|
Document document = Document.create();
|
||||||
|
|
||||||
|
mappingElasticsearchConverter.write(entity, document);
|
||||||
|
|
||||||
|
assertEquals(expected, document.toJson(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // #1945
|
||||||
|
@DisplayName("should read using ValueConverters")
|
||||||
|
void shouldReadUsingValueConverters() throws JSONException {
|
||||||
|
|
||||||
|
String json = "{\n" + //
|
||||||
|
" \"id\": \"42\",\n" + //
|
||||||
|
" \"fieldWithClassBasedConverter\": \"desabssalc\",\n" + //
|
||||||
|
" \"fieldWithEnumBasedConverter\": \"desabmune\",\n" + //
|
||||||
|
" \"dontConvert\": \"Monty Python's Flying Circus\"\n" + //
|
||||||
|
"}\n"; //
|
||||||
|
|
||||||
|
Document source = Document.parse(json);
|
||||||
|
|
||||||
|
// when
|
||||||
|
EntityWithCustomValueConverters entity = mappingElasticsearchConverter.read(EntityWithCustomValueConverters.class,
|
||||||
|
source);
|
||||||
|
|
||||||
|
assertThat(entity.getId()).isEqualTo("42");
|
||||||
|
assertThat(entity.getFieldWithClassBasedConverter()).isEqualTo("classbased");
|
||||||
|
assertThat(entity.getFieldWithEnumBasedConverter()).isEqualTo("enumbased");
|
||||||
|
assertThat(entity.getDontConvert()).isEqualTo("Monty Python's Flying Circus");
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> writeToMap(Object source) {
|
private Map<String, Object> writeToMap(Object source) {
|
||||||
|
|
||||||
Document sink = Document.create();
|
Document sink = Document.create();
|
||||||
@ -2309,5 +2359,82 @@ public class MappingElasticsearchConverterUnitTests {
|
|||||||
this.cars = cars;
|
this.cars = cars;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class EntityWithCustomValueConverters {
|
||||||
|
@Nullable @Id private String id;
|
||||||
|
@Nullable @ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
|
||||||
|
@Nullable @ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
|
||||||
|
@Nullable private String dontConvert;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(@Nullable String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getFieldWithClassBasedConverter() {
|
||||||
|
return fieldWithClassBasedConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFieldWithClassBasedConverter(@Nullable String fieldWithClassBasedConverter) {
|
||||||
|
this.fieldWithClassBasedConverter = fieldWithClassBasedConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getFieldWithEnumBasedConverter() {
|
||||||
|
return fieldWithEnumBasedConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFieldWithEnumBasedConverter(@Nullable String fieldWithEnumBasedConverter) {
|
||||||
|
this.fieldWithEnumBasedConverter = fieldWithEnumBasedConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getDontConvert() {
|
||||||
|
return dontConvert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDontConvert(@Nullable String dontConvert) {
|
||||||
|
this.dontConvert = dontConvert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassBasedValueConverter implements PropertyValueConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object write(Object value) {
|
||||||
|
return reverse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Object value) {
|
||||||
|
return reverse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum EnumBasedValueConverter implements PropertyValueConverter {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object write(Object value) {
|
||||||
|
return reverse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Object value) {
|
||||||
|
return reverse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String reverse(Object o) {
|
||||||
|
|
||||||
|
Assert.notNull(o, "o must not be null");
|
||||||
|
|
||||||
|
return new StringBuilder().append(o.toString()).reverse().toString();
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,13 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
||||||
import org.springframework.data.elasticsearch.annotations.Field;
|
import org.springframework.data.elasticsearch.annotations.Field;
|
||||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||||
import org.springframework.data.elasticsearch.annotations.InnerField;
|
import org.springframework.data.elasticsearch.annotations.InnerField;
|
||||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||||
|
import org.springframework.data.elasticsearch.annotations.ValueConverter;
|
||||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||||
import org.springframework.data.mapping.MappingException;
|
import org.springframework.data.mapping.MappingException;
|
||||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||||
@ -92,20 +94,20 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
||||||
|
|
||||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
||||||
assertThat(persistentProperty.hasPropertyConverter()).isTrue();
|
assertThat(persistentProperty.hasPropertyValueConverter()).isTrue();
|
||||||
assertThat(persistentProperty.getPropertyConverter()).isNotNull();
|
assertThat(persistentProperty.getPropertyValueConverter()).isNotNull();
|
||||||
|
|
||||||
persistentProperty = persistentEntity.getRequiredPersistentProperty("localDateTime");
|
persistentProperty = persistentEntity.getRequiredPersistentProperty("localDateTime");
|
||||||
assertThat(persistentProperty.hasPropertyConverter()).isTrue();
|
assertThat(persistentProperty.hasPropertyValueConverter()).isTrue();
|
||||||
assertThat(persistentProperty.getPropertyConverter()).isNotNull();
|
assertThat(persistentProperty.getPropertyValueConverter()).isNotNull();
|
||||||
|
|
||||||
persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate");
|
persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate");
|
||||||
assertThat(persistentProperty.hasPropertyConverter()).isTrue();
|
assertThat(persistentProperty.hasPropertyValueConverter()).isTrue();
|
||||||
assertThat(persistentProperty.getPropertyConverter()).isNotNull();
|
assertThat(persistentProperty.getPropertyValueConverter()).isNotNull();
|
||||||
|
|
||||||
persistentProperty = persistentEntity.getRequiredPersistentProperty("localDateList");
|
persistentProperty = persistentEntity.getRequiredPersistentProperty("localDateList");
|
||||||
assertThat(persistentProperty.hasPropertyConverter()).isTrue();
|
assertThat(persistentProperty.hasPropertyValueConverter()).isTrue();
|
||||||
assertThat(persistentProperty.getPropertyConverter()).isNotNull();
|
assertThat(persistentProperty.getPropertyValueConverter()).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAES-716
|
@Test // DATAES-716
|
||||||
@ -114,7 +116,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
||||||
LocalDate localDate = LocalDate.of(2019, 12, 27);
|
LocalDate localDate = LocalDate.of(2019, 12, 27);
|
||||||
|
|
||||||
String converted = persistentProperty.getPropertyConverter().write(localDate).toString();
|
String converted = persistentProperty.getPropertyValueConverter().write(localDate).toString();
|
||||||
|
|
||||||
assertThat(converted).isEqualTo("27.12.2019");
|
assertThat(converted).isEqualTo("27.12.2019");
|
||||||
}
|
}
|
||||||
@ -124,7 +126,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
||||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate");
|
||||||
|
|
||||||
Object converted = persistentProperty.getPropertyConverter().read("27.12.2019");
|
Object converted = persistentProperty.getPropertyValueConverter().read("27.12.2019");
|
||||||
|
|
||||||
assertThat(converted).isInstanceOf(LocalDate.class);
|
assertThat(converted).isInstanceOf(LocalDate.class);
|
||||||
assertThat(converted).isEqualTo(LocalDate.of(2019, 12, 27));
|
assertThat(converted).isEqualTo(LocalDate.of(2019, 12, 27));
|
||||||
@ -138,7 +140,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
.from(ZonedDateTime.of(LocalDateTime.of(2020, 4, 19, 19, 44), ZoneId.of("UTC")));
|
.from(ZonedDateTime.of(LocalDateTime.of(2020, 4, 19, 19, 44), ZoneId.of("UTC")));
|
||||||
Date legacyDate = calendar.getTime();
|
Date legacyDate = calendar.getTime();
|
||||||
|
|
||||||
String converted = persistentProperty.getPropertyConverter().write(legacyDate).toString();
|
String converted = persistentProperty.getPropertyValueConverter().write(legacyDate).toString();
|
||||||
|
|
||||||
assertThat(converted).isEqualTo("20200419T194400.000Z");
|
assertThat(converted).isEqualTo("20200419T194400.000Z");
|
||||||
}
|
}
|
||||||
@ -148,7 +150,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
SimpleElasticsearchPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class);
|
||||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate");
|
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate");
|
||||||
|
|
||||||
Object converted = persistentProperty.getPropertyConverter().read("20200419T194400.000Z");
|
Object converted = persistentProperty.getPropertyValueConverter().read("20200419T194400.000Z");
|
||||||
|
|
||||||
assertThat(converted).isInstanceOf(Date.class);
|
assertThat(converted).isInstanceOf(Date.class);
|
||||||
GregorianCalendar calendar = GregorianCalendar
|
GregorianCalendar calendar = GregorianCalendar
|
||||||
@ -246,6 +248,21 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
assertThat(property.getFieldName()).isEqualTo("CUStomFIEldnAME");
|
assertThat(property.getFieldName()).isEqualTo("CUStomFIEldnAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1945
|
||||||
|
@DisplayName("should use ValueConverter annotation")
|
||||||
|
void shouldUseValueConverterAnnotation() {
|
||||||
|
|
||||||
|
SimpleElasticsearchPersistentEntity<?> persistentEntity = context
|
||||||
|
.getRequiredPersistentEntity(EntityWithCustomValueConverters.class);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
persistentEntity.getRequiredPersistentProperty("fieldWithClassBasedConverter").getPropertyValueConverter())
|
||||||
|
.isInstanceOf(ClassBasedValueConverter.class);
|
||||||
|
assertThat(
|
||||||
|
persistentEntity.getRequiredPersistentProperty("fieldWithEnumBasedConverter").getPropertyValueConverter())
|
||||||
|
.isInstanceOf(EnumBasedValueConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
// region entities
|
// region entities
|
||||||
static class FieldNameProperty {
|
static class FieldNameProperty {
|
||||||
@Nullable @Field(name = "by-name") String fieldProperty;
|
@Nullable @Field(name = "by-name") String fieldProperty;
|
||||||
@ -324,5 +341,38 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
|||||||
this.withCustomFieldName = withCustomFieldName;
|
this.withCustomFieldName = withCustomFieldName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class EntityWithCustomValueConverters {
|
||||||
|
@Id private String id;
|
||||||
|
@Nullable @ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
|
||||||
|
@Nullable @ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassBasedValueConverter implements PropertyValueConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object write(Object value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Object value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum EnumBasedValueConverter implements PropertyValueConverter {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object write(Object value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Object value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user