mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-21 19:42:10 +00:00
DATAES-530 - Polishing.
Fix generics in ElasticsearchEntityMapper. Extract methods. Move enum conversion handling to simple type handling for symmetric implementation. Swap comparison of nullable types to avoid potential null dereference. Rename ElasticsearchDefaultTypeMapper to DefaultElasticsearchTypeMapper. Move MapTypeAliasAccessor to DefaultElasticsearchTypeMapper. Introduce SearchResultMapperAdapter to avoid empty method implementations in anonymous classes. Javadoc, reference docs, formatting. Original Pull Request: #237
This commit is contained in:
parent
a64af54e26
commit
c3e7056d7b
@ -66,13 +66,26 @@ for `Converter` registration.
|
||||
<3> Optionally set `CustomConversions` if applicable.
|
||||
====
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations]]
|
||||
=== Mapping Annotation Overview
|
||||
|
||||
The `ElasticsearchEntityMapper` can use metadata to drive the mapping of objects to documents. The following annotations are available:
|
||||
|
||||
* `@Id`: Applied at the field level to mark the field used for identity purpose.
|
||||
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the index name and index type where the document will be stored.
|
||||
* `@Transient`: By default all private fields are mapped to the document, this annotation excludes the field where it is applied from being stored in the database
|
||||
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved Document.
|
||||
* `@Field`: Applied at the field level and described the name of the field as it will be represented in the Elasticsearch document thus allowing the name to be different than the fieldname of the class.
|
||||
|
||||
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.rules]]
|
||||
=== Mapping Rules
|
||||
|
||||
==== Type Hints
|
||||
|
||||
Mapping uses _type hints_ embedded in the document sent to the server to allow generic type mapping. Those type hints are
|
||||
represented as `_class` attributes within the document on the server and will be written for each aggregate root.
|
||||
Mapping uses _type hints_ embedded in the document sent to the server to allow generic type mapping.
|
||||
Those type hints are represented as `_class` attributes within the document and are written for each aggregate root.
|
||||
|
||||
.Type Hints
|
||||
====
|
||||
@ -123,8 +136,7 @@ public class Person {
|
||||
<1> The configured alias is used when writing the entity.
|
||||
====
|
||||
|
||||
NOTE: Type hints will not be written for nested Objects unless the properties type is `Object`, an interface or the
|
||||
actual value type does not match the properties declaration.
|
||||
NOTE: Type hints will not be written for nested Objects unless the properties type is `Object`, an interface or the actual value type does not match the properties declaration.
|
||||
|
||||
==== Geospatial Types
|
||||
|
||||
@ -152,8 +164,7 @@ public class Address {
|
||||
|
||||
==== Collections
|
||||
|
||||
For values inside Collections apply the same mapping rules as for aggregate roots when it comes to _type hints_
|
||||
and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
For values inside Collections apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
|
||||
.Collections
|
||||
====
|
||||
@ -179,8 +190,7 @@ public class Person {
|
||||
|
||||
==== Maps
|
||||
|
||||
For values inside Maps apply the same mapping rules as for aggregate roots when it comes to _type hints_
|
||||
and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
For values inside Maps apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
However the Map key needs to a String to be processed by Elasticsearch.
|
||||
|
||||
.Collections
|
||||
@ -214,8 +224,7 @@ public class Person {
|
||||
[[elasticsearch.mapping.meta-model.conversions]]
|
||||
=== Custom Conversions
|
||||
|
||||
Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, previous section>> `ElasticsearchCustomConversions`
|
||||
allows to register specific rules for mapping domain and simple types.
|
||||
Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, previous section>> `ElasticsearchCustomConversions` allows registering specific rules for mapping domain and simple types.
|
||||
|
||||
.Meta Model Object Mapping Configuration
|
||||
====
|
||||
|
@ -72,15 +72,14 @@ public class ElasticsearchConfigurationSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EntityMapper} used for mapping source <> DomainType. <br />
|
||||
* Returns the {@link EntityMapper} used for mapping between the source and domain type. <br />
|
||||
* <strong>Hint</strong>: you can use {@link org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper} as
|
||||
* an alternative to the {@link DefaultEntityMapper}.
|
||||
*
|
||||
* <pre>{@code
|
||||
* <pre class="code">
|
||||
* ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
|
||||
* new DefaultConversionService());
|
||||
* new DefaultConversionService());
|
||||
* entityMapper.setConversions(elasticsearchCustomConversions());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
|
@ -28,14 +28,20 @@ public abstract class AbstractResultMapper implements ResultsMapper {
|
||||
private final EntityMapper entityMapper;
|
||||
private final ProjectionFactory projectionFactory;
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractResultMapper}.
|
||||
*
|
||||
* @param entityMapper must not be {@literal null}.
|
||||
*/
|
||||
public AbstractResultMapper(EntityMapper entityMapper) {
|
||||
this(entityMapper, new SpelAwareProxyProjectionFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractResultMapper}.
|
||||
*
|
||||
* @param entityMapper
|
||||
* @param projectionFactory
|
||||
* @param entityMapper must not be {@literal null}.
|
||||
* @param projectionFactory must not be {@literal null}.
|
||||
* @since 3.2
|
||||
*/
|
||||
public AbstractResultMapper(EntityMapper entityMapper, ProjectionFactory projectionFactory) {
|
||||
@ -47,6 +53,10 @@ public abstract class AbstractResultMapper implements ResultsMapper {
|
||||
this.projectionFactory = projectionFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.ResultsMapper#getEntityMapper()
|
||||
*/
|
||||
@Override
|
||||
public EntityMapper getEntityMapper() {
|
||||
return this.entityMapper;
|
||||
@ -58,6 +68,6 @@ public abstract class AbstractResultMapper implements ResultsMapper {
|
||||
*/
|
||||
@Override
|
||||
public ProjectionFactory getProjectionFactory() {
|
||||
return projectionFactory;
|
||||
return this.projectionFactory;
|
||||
}
|
||||
}
|
||||
|
@ -88,12 +88,11 @@ public class DefaultResultMapper extends AbstractResultMapper {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
static EntityMapper initEntityMapper(
|
||||
private static EntityMapper initEntityMapper(
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
|
||||
Assert.notNull(mappingContext, "MappingContext must not be null!");
|
||||
return new DefaultEntityMapper(mappingContext);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
@ -34,7 +36,6 @@ import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomCo
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchTypeMapper;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
@ -76,7 +77,49 @@ public class ElasticsearchEntityMapper implements
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
this.conversionService = conversionService != null ? conversionService : new DefaultConversionService();
|
||||
this.typeMapper = ElasticsearchTypeMapper.defaultTypeMapper(mappingContext);
|
||||
this.typeMapper = ElasticsearchTypeMapper.create(mappingContext);
|
||||
}
|
||||
|
||||
// --> GETTERS / SETTERS
|
||||
|
||||
@Override
|
||||
public MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CustomConversions} to be applied during the mapping process. <br />
|
||||
* Conversions are registered after {@link #afterPropertiesSet() bean initialization}.
|
||||
*
|
||||
* @param conversions must not be {@literal null}.
|
||||
*/
|
||||
public void setConversions(CustomConversions conversions) {
|
||||
this.conversions = conversions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ElasticsearchTypeMapper} to use for reading / writing type hints.
|
||||
*
|
||||
* @param typeMapper must not be {@literal null}.
|
||||
*/
|
||||
public void setTypeMapper(ElasticsearchTypeMapper typeMapper) {
|
||||
this.typeMapper = typeMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
DateFormatterRegistrar.addDateConverters(conversionService);
|
||||
conversions.registerConvertersIn(conversionService);
|
||||
}
|
||||
|
||||
// --> READ
|
||||
@ -86,12 +129,14 @@ public class ElasticsearchEntityMapper implements
|
||||
return read(targetType, source);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <R> R read(Class<R> type, Map<String, Object> source) {
|
||||
return doRead(source, ClassTypeInformation.from((Class<R>) ClassUtils.getUserClass(type)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
protected <R> R doRead(Map<String, Object> source, TypeInformation<R> typeHint) {
|
||||
|
||||
@ -113,6 +158,7 @@ public class ElasticsearchEntityMapper implements
|
||||
return readEntity(entity, source);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Object> source) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> targetEntity = computeClosestEntity(entity, source);
|
||||
@ -125,11 +171,8 @@ public class ElasticsearchEntityMapper implements
|
||||
R instance = (R) instantiator.createInstance(targetEntity,
|
||||
new PersistentEntityParameterValueProvider<>(targetEntity, propertyValueProvider, null));
|
||||
|
||||
if (targetEntity.requiresPropertyPopulation()) {
|
||||
return readProperties(targetEntity, instance, propertyValueProvider);
|
||||
}
|
||||
|
||||
return instance;
|
||||
return targetEntity.requiresPropertyPopulation() ? readProperties(targetEntity, instance, propertyValueProvider)
|
||||
: instance;
|
||||
}
|
||||
|
||||
protected <R> R readProperties(ElasticsearchPersistentEntity<?> entity, R instance,
|
||||
@ -153,6 +196,7 @@ public class ElasticsearchEntityMapper implements
|
||||
return accessor.getBean();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <R> R readValue(@Nullable Object source, ElasticsearchPersistentProperty property,
|
||||
TypeInformation<R> targetType) {
|
||||
|
||||
@ -160,20 +204,19 @@ public class ElasticsearchEntityMapper implements
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> rawType = targetType.getType();
|
||||
Class<R> rawType = targetType.getType();
|
||||
if (conversions.hasCustomReadTarget(source.getClass(), rawType)) {
|
||||
return (R) conversionService.convert(source, rawType);
|
||||
return rawType.cast(conversionService.convert(source, rawType));
|
||||
} else if (source instanceof List) {
|
||||
return readCollectionValue((List) source, property, targetType);
|
||||
} else if (source instanceof Map) {
|
||||
return readMapValue((Map<String, Object>) source, property, targetType);
|
||||
} else if (Enum.class.isAssignableFrom(rawType)) {
|
||||
return (R) Enum.valueOf((Class<Enum>) rawType, source.toString());
|
||||
}
|
||||
|
||||
return (R) readSimpleValue(source, targetType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R readMapValue(@Nullable Map<String, Object> source, ElasticsearchPersistentProperty property,
|
||||
TypeInformation<R> targetType) {
|
||||
|
||||
@ -186,10 +229,10 @@ public class ElasticsearchEntityMapper implements
|
||||
return readEntity(targetEntity, source);
|
||||
}
|
||||
|
||||
Map<String, Object> target = new LinkedHashMap();
|
||||
Map<String, Object> target = new LinkedHashMap<>();
|
||||
for (Entry<String, Object> entry : source.entrySet()) {
|
||||
|
||||
if (conversions.isSimpleType(entry.getValue().getClass())) {
|
||||
if (isSimpleType(entry.getValue())) {
|
||||
target.put(entry.getKey(),
|
||||
readSimpleValue(entry.getValue(), targetType.isMap() ? targetType.getComponentType() : targetType));
|
||||
} else {
|
||||
@ -211,13 +254,13 @@ public class ElasticsearchEntityMapper implements
|
||||
} else {
|
||||
target.put(entry.getKey(), readEntity(targetEntity, (Map) entry.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return (R) target;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R> R readCollectionValue(@Nullable List<?> source, ElasticsearchPersistentProperty property,
|
||||
TypeInformation<R> targetType) {
|
||||
|
||||
@ -225,12 +268,11 @@ public class ElasticsearchEntityMapper implements
|
||||
return null;
|
||||
}
|
||||
|
||||
List<?> sourceList = source;
|
||||
Collection target = createCollectionForValue(targetType, sourceList.size());
|
||||
Collection<Object> target = createCollectionForValue(targetType, source.size());
|
||||
|
||||
for (Object value : sourceList) {
|
||||
for (Object value : source) {
|
||||
|
||||
if (conversions.isSimpleType(value.getClass())) {
|
||||
if (isSimpleType(value)) {
|
||||
target.add(
|
||||
readSimpleValue(value, targetType.getComponentType() != null ? targetType.getComponentType() : targetType));
|
||||
} else {
|
||||
@ -246,23 +288,24 @@ public class ElasticsearchEntityMapper implements
|
||||
return (R) target;
|
||||
}
|
||||
|
||||
protected Object readSimpleValue(@Nullable Object value, TypeInformation<?> targetType) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object readSimpleValue(@Nullable Object value, TypeInformation<?> targetType) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> target = targetType.getType();
|
||||
|
||||
if (ClassTypeInformation.OBJECT.equals(targetType)
|
||||
|| (targetType != null && value.getClass().equals(targetType.getActualType()))) {
|
||||
if (value == null || target == null || ClassUtils.isAssignableValue(target, value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (conversionService.canConvert(value.getClass(), targetType.getType())) {
|
||||
return conversionService.convert(value, targetType.getType());
|
||||
if (conversions.hasCustomReadTarget(value.getClass(), target)) {
|
||||
return conversionService.convert(value, target);
|
||||
}
|
||||
|
||||
throw new MappingException(
|
||||
String.format("Unable to map %s of type %s to %s", value, value.getClass(), targetType.getType()));
|
||||
if (Enum.class.isAssignableFrom(target)) {
|
||||
return Enum.valueOf((Class<Enum>) target, value.toString());
|
||||
}
|
||||
|
||||
return conversionService.convert(value, target);
|
||||
}
|
||||
|
||||
// --> WRITE
|
||||
@ -275,6 +318,7 @@ public class ElasticsearchEntityMapper implements
|
||||
return target;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void write(@Nullable Object source, Map<String, Object> sink) {
|
||||
|
||||
@ -284,12 +328,12 @@ public class ElasticsearchEntityMapper implements
|
||||
|
||||
if (source instanceof Map) {
|
||||
|
||||
sink.putAll((Map) source);
|
||||
sink.putAll((Map<String, Object>) source);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> entityType = ClassUtils.getUserClass(source.getClass());
|
||||
TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType);
|
||||
TypeInformation<?> type = ClassTypeInformation.from(entityType);
|
||||
|
||||
if (requiresTypeHint(type, source.getClass(), null)) {
|
||||
typeMapper.writeType(source.getClass(), sink);
|
||||
@ -337,6 +381,7 @@ public class ElasticsearchEntityMapper implements
|
||||
if (requiresTypeHint(entity.getTypeInformation(), source.getClass(), containingStructure)) {
|
||||
typeMapper.writeType(source.getClass(), sink);
|
||||
}
|
||||
|
||||
writeProperties(entity, accessor, sink);
|
||||
}
|
||||
|
||||
@ -355,7 +400,7 @@ public class ElasticsearchEntityMapper implements
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!conversions.isSimpleType(value.getClass())) {
|
||||
if (!isSimpleType(value)) {
|
||||
writeProperty(property, value, sink);
|
||||
} else {
|
||||
sink.put(property.getFieldName(), getWriteSimpleValue(value));
|
||||
@ -393,15 +438,20 @@ public class ElasticsearchEntityMapper implements
|
||||
|
||||
protected Object getWriteSimpleValue(Object value) {
|
||||
|
||||
Optional<Class<?>> customWriteTarget = conversions.getCustomWriteTarget(value.getClass());
|
||||
|
||||
if (!customWriteTarget.isPresent()) {
|
||||
return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value;
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return conversionService.convert(value, customWriteTarget.get());
|
||||
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(value.getClass());
|
||||
|
||||
if (customTarget.isPresent()) {
|
||||
return conversionService.convert(value, customTarget.get());
|
||||
}
|
||||
|
||||
return Enum.class.isAssignableFrom(value.getClass()) ? ((Enum<?>) value).name() : value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object getWriteComplexValue(ElasticsearchPersistentProperty property, TypeInformation<?> typeHint,
|
||||
Object value) {
|
||||
|
||||
@ -412,7 +462,7 @@ public class ElasticsearchEntityMapper implements
|
||||
return writeMapValue((Map<String, Object>) value, property, typeHint);
|
||||
}
|
||||
|
||||
if (property.isEntity() || !conversions.isSimpleType(value.getClass())) {
|
||||
if (property.isEntity() || !isSimpleType(value)) {
|
||||
return writeEntity(value, property, typeHint);
|
||||
}
|
||||
|
||||
@ -433,7 +483,7 @@ public class ElasticsearchEntityMapper implements
|
||||
Streamable<Entry<String, Object>> mapSource = Streamable.of(value.entrySet());
|
||||
|
||||
if (!typeHint.getActualType().getType().equals(Object.class)
|
||||
&& conversions.isSimpleType(typeHint.getMapValueType().getType())) {
|
||||
&& isSimpleType(typeHint.getMapValueType().getType())) {
|
||||
mapSource.forEach(it -> target.put(it.getKey(), getWriteSimpleValue(it.getValue())));
|
||||
} else {
|
||||
|
||||
@ -442,7 +492,7 @@ public class ElasticsearchEntityMapper implements
|
||||
Object converted = null;
|
||||
if (it.getValue() != null) {
|
||||
|
||||
if (conversions.isSimpleType(it.getValue().getClass())) {
|
||||
if (isSimpleType(it.getValue())) {
|
||||
converted = getWriteSimpleValue(it.getValue());
|
||||
} else {
|
||||
converted = getWriteComplexValue(property, ClassTypeInformation.from(it.getValue().getClass()),
|
||||
@ -451,7 +501,6 @@ public class ElasticsearchEntityMapper implements
|
||||
}
|
||||
|
||||
target.put(it.getKey(), converted);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -461,23 +510,21 @@ public class ElasticsearchEntityMapper implements
|
||||
private Object writeCollectionValue(Object value, ElasticsearchPersistentProperty property,
|
||||
TypeInformation<?> typeHint) {
|
||||
|
||||
Streamable collectionSource = value instanceof Iterable ? Streamable.of((Iterable) value)
|
||||
Streamable<?> collectionSource = value instanceof Iterable ? Streamable.of((Iterable<?>) value)
|
||||
: Streamable.of(ObjectUtils.toObjectArray(value));
|
||||
|
||||
List<Object> target = new ArrayList<>();
|
||||
if (!typeHint.getActualType().getType().equals(Object.class)
|
||||
&& conversions.isSimpleType(typeHint.getActualType().getType())) {
|
||||
|
||||
if (!typeHint.getActualType().getType().equals(Object.class) && isSimpleType(typeHint.getActualType().getType())) {
|
||||
collectionSource.map(this::getWriteSimpleValue).forEach(target::add);
|
||||
} else {
|
||||
|
||||
Streamable.of((Iterable) value).map(it -> {
|
||||
collectionSource.map(it -> {
|
||||
|
||||
if (it == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (conversions.isSimpleType(it.getClass())) {
|
||||
if (isSimpleType(it)) {
|
||||
return getWriteSimpleValue(it);
|
||||
}
|
||||
|
||||
@ -488,48 +535,6 @@ public class ElasticsearchEntityMapper implements
|
||||
return target;
|
||||
}
|
||||
|
||||
// --> GETTERS / SETTERS
|
||||
|
||||
@Override
|
||||
public MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CustomConversions} to be applied during the mapping process. <br />
|
||||
* Conversions are registered after {@link #afterPropertiesSet() bean initialization}.
|
||||
*
|
||||
* @param conversions must not be {@literal null}.
|
||||
*/
|
||||
public void setConversions(CustomConversions conversions) {
|
||||
this.conversions = conversions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ElasticsearchTypeMapper} to use for reading / writing type hints.
|
||||
*
|
||||
* @param typeMapper must not be {@literal null}.
|
||||
*/
|
||||
public void setTypeMapper(ElasticsearchTypeMapper typeMapper) {
|
||||
this.typeMapper = typeMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
DateFormatterRegistrar.addDateConverters(conversionService);
|
||||
conversions.registerConvertersIn(conversionService);
|
||||
}
|
||||
|
||||
// --> LEGACY
|
||||
|
||||
@Override
|
||||
@ -549,21 +554,23 @@ public class ElasticsearchEntityMapper implements
|
||||
// --> PRIVATE HELPERS
|
||||
|
||||
private boolean requiresTypeHint(TypeInformation<?> type, Class<?> actualType,
|
||||
@Nullable TypeInformation<?> containingStructure) {
|
||||
@Nullable TypeInformation<?> container) {
|
||||
|
||||
if (containingStructure != null) {
|
||||
if (container != null) {
|
||||
|
||||
if (containingStructure.isCollectionLike()) {
|
||||
if (containingStructure.getActualType().equals(type) && type.getType().equals(actualType)) {
|
||||
if (container.isCollectionLike()) {
|
||||
if (type.equals(container.getActualType()) && type.getType().equals(actualType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (containingStructure.isMap()) {
|
||||
if (containingStructure.getMapValueType().equals(type) && type.getType().equals(actualType)) {
|
||||
|
||||
if (container.isMap()) {
|
||||
if (type.equals(container.getMapValueType()) && type.getType().equals(actualType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (containingStructure.equals(type) && type.getType().equals(actualType)) {
|
||||
|
||||
if (container.equals(type) && type.getType().equals(actualType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -574,7 +581,7 @@ public class ElasticsearchEntityMapper implements
|
||||
|
||||
/**
|
||||
* Compute the type to use by checking the given entity against the store type;
|
||||
*
|
||||
*
|
||||
* @param entity
|
||||
* @param source
|
||||
* @return
|
||||
@ -600,12 +607,12 @@ public class ElasticsearchEntityMapper implements
|
||||
private ElasticsearchPersistentEntity<?> computeGenericValueTypeForRead(ElasticsearchPersistentProperty property,
|
||||
Object value) {
|
||||
|
||||
return property.getTypeInformation().getActualType().equals(ClassTypeInformation.OBJECT)
|
||||
return ClassTypeInformation.OBJECT.equals(property.getTypeInformation().getActualType())
|
||||
? mappingContext.getRequiredPersistentEntity(value.getClass())
|
||||
: mappingContext.getRequiredPersistentEntity(property.getTypeInformation().getActualType());
|
||||
}
|
||||
|
||||
private Collection<?> createCollectionForValue(TypeInformation<?> collectionTypeInformation, int size) {
|
||||
private Collection<Object> createCollectionForValue(TypeInformation<?> collectionTypeInformation, int size) {
|
||||
|
||||
Class<?> collectionType = collectionTypeInformation.isSubTypeOf(Collection.class) //
|
||||
? collectionTypeInformation.getType() //
|
||||
@ -618,19 +625,24 @@ public class ElasticsearchEntityMapper implements
|
||||
return collectionTypeInformation.getType().isArray() //
|
||||
? new ArrayList<>(size) //
|
||||
: CollectionFactory.createCollection(collectionType, componentType.getType(), size);
|
||||
}
|
||||
|
||||
private boolean isSimpleType(Object value) {
|
||||
return isSimpleType(value.getClass());
|
||||
}
|
||||
|
||||
private boolean isSimpleType(Class<?> type) {
|
||||
return conversions.isSimpleType(type);
|
||||
}
|
||||
|
||||
// --> OHTER STUFF
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class ElasticsearchPropertyValueProvider implements PropertyValueProvider<ElasticsearchPersistentProperty> {
|
||||
|
||||
final MapValueAccessor mapValueAccessor;
|
||||
|
||||
public ElasticsearchPropertyValueProvider(MapValueAccessor mapValueAccessor) {
|
||||
this.mapValueAccessor = mapValueAccessor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getPropertyValue(ElasticsearchPersistentProperty property) {
|
||||
return (T) readValue(mapValueAccessor.get(property), property, property.getTypeInformation());
|
||||
@ -670,13 +682,14 @@ public class ElasticsearchEntityMapper implements
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> getAsMap(Object result) {
|
||||
|
||||
if (result instanceof Map) {
|
||||
return (Map) result;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("%s is no Map.", result));
|
||||
throw new IllegalArgumentException(String.format("%s is not a Map.", result));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,16 +24,7 @@ import static org.springframework.util.StringUtils.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
@ -877,7 +868,7 @@ public class ElasticsearchRestTemplate
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery()).withIndices(indexName)
|
||||
.withTypes(typeName).withPageable(PageRequest.of(0, pageSize)).build();
|
||||
|
||||
SearchResultMapper onlyIdResultMapper = new SearchResultMapper() {
|
||||
SearchResultMapper onlyIdResultMapper = new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
@ -886,14 +877,9 @@ public class ElasticsearchRestTemplate
|
||||
result.add(id);
|
||||
}
|
||||
if (result.size() > 0) {
|
||||
return new AggregatedPageImpl<T>((List<T>) result, response.getScrollId());
|
||||
return new AggregatedPageImpl<>((List<T>) result, response.getScrollId());
|
||||
}
|
||||
return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
return new AggregatedPageImpl<>(Collections.emptyList(), response.getScrollId());
|
||||
}
|
||||
};
|
||||
|
||||
@ -1458,7 +1444,7 @@ public class ElasticsearchRestTemplate
|
||||
/**
|
||||
* It takes two steps to create a List<AliasMetadata> from the elasticsearch http response because the aliases field
|
||||
* is actually a Map by alias name, but the alias name is on the AliasMetadata.
|
||||
*
|
||||
*
|
||||
* @param aliasResponse
|
||||
* @return
|
||||
*/
|
||||
|
@ -755,7 +755,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery()).withIndices(indexName)
|
||||
.withTypes(typeName).withPageable(PageRequest.of(0, pageSize)).build();
|
||||
|
||||
SearchResultMapper onlyIdResultMapper = new SearchResultMapper() {
|
||||
SearchResultMapper onlyIdResultMapper = new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
@ -766,12 +766,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
|
||||
if (result.size() > 0) {
|
||||
return new AggregatedPageImpl<T>((List<T>) result, response.getScrollId());
|
||||
}
|
||||
return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
return new AggregatedPageImpl<T>(Collections.emptyList(), response.getScrollId());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -86,14 +86,14 @@ public interface ResultsMapper extends SearchResultMapper, GetResultMapper, Mult
|
||||
Object mappedResult = getEntityMapper().readObject(source, type);
|
||||
|
||||
if (mappedResult == null) {
|
||||
return (T) mappedResult;
|
||||
return (T) null;
|
||||
}
|
||||
|
||||
if (type.isInterface() || !ClassUtils.isAssignableValue(type, mappedResult)) {
|
||||
return getProjectionFactory().createProjection(type, mappedResult);
|
||||
}
|
||||
|
||||
return (T) mappedResult;
|
||||
return type.cast(mappedResult);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,13 +120,13 @@ public interface ResultsMapper extends SearchResultMapper, GetResultMapper, Mult
|
||||
Object mappedResult = getEntityMapper().readObject(source, type);
|
||||
|
||||
if (mappedResult == null) {
|
||||
return (T) mappedResult;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.isInterface()) {
|
||||
return getProjectionFactory().createProjection(type, mappedResult);
|
||||
}
|
||||
|
||||
return (T) mappedResult;
|
||||
return type.cast(mappedResult);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
|
||||
/**
|
||||
* Adapter utility for {@link SearchResultMapper} that wish to implement a subset of mapping methods. Default
|
||||
* implementations throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
abstract class SearchResultMapperAdapter implements SearchResultMapper {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.SearchResultMapper#mapResults(org.elasticsearch.action.search.SearchResponse, java.lang.Class, org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.SearchResultMapper#mapSearchHit(org.elasticsearch.search.SearchHit, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ import org.springframework.core.convert.converter.Converter;
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
public final class DateTimeConverters {
|
||||
|
||||
private static DateTimeFormatter formatter = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);
|
||||
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* http://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.core.convert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.convert.DefaultTypeMapper;
|
||||
import org.springframework.data.convert.SimpleTypeInformationMapper;
|
||||
import org.springframework.data.convert.TypeAliasAccessor;
|
||||
import org.springframework.data.convert.TypeInformationMapper;
|
||||
import org.springframework.data.mapping.Alias;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Elasticsearch specific {@link org.springframework.data.convert.TypeMapper} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class DefaultElasticsearchTypeMapper extends DefaultTypeMapper<Map<String, Object>>
|
||||
implements ElasticsearchTypeMapper {
|
||||
|
||||
private final @Nullable String typeKey;
|
||||
|
||||
public DefaultElasticsearchTypeMapper(@Nullable String typeKey) {
|
||||
this(typeKey, Collections.singletonList(new SimpleTypeInformationMapper()));
|
||||
}
|
||||
|
||||
public DefaultElasticsearchTypeMapper(@Nullable String typeKey,
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext) {
|
||||
this(typeKey, new MapTypeAliasAccessor(typeKey), mappingContext,
|
||||
Collections.singletonList(new SimpleTypeInformationMapper()));
|
||||
}
|
||||
|
||||
public DefaultElasticsearchTypeMapper(@Nullable String typeKey, List<? extends TypeInformationMapper> mappers) {
|
||||
this(typeKey, new MapTypeAliasAccessor(typeKey), null, mappers);
|
||||
}
|
||||
|
||||
public DefaultElasticsearchTypeMapper(@Nullable String typeKey, TypeAliasAccessor<Map<String, Object>> accessor,
|
||||
@Nullable MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
|
||||
List<? extends TypeInformationMapper> mappers) {
|
||||
|
||||
super(accessor, mappingContext, mappers);
|
||||
this.typeKey = typeKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeKey(String key) {
|
||||
return typeKey != null && typeKey.equals(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TypeAliasAccessor} to store aliases in a {@link Map}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public static class MapTypeAliasAccessor implements TypeAliasAccessor<Map<String, Object>> {
|
||||
|
||||
private final @Nullable String typeKey;
|
||||
|
||||
public MapTypeAliasAccessor(@Nullable String typeKey) {
|
||||
this.typeKey = typeKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.TypeAliasAccessor#readAliasFrom(java.lang.Object)
|
||||
*/
|
||||
public Alias readAliasFrom(Map<String, Object> source) {
|
||||
return Alias.ofNullable(source.get(typeKey));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.TypeAliasAccessor#writeTypeTo(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public void writeTypeTo(Map<String, Object> sink, Object alias) {
|
||||
|
||||
if (typeKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sink.put(typeKey, alias);
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@ public class ElasticsearchCustomConversions extends CustomConversions {
|
||||
static {
|
||||
|
||||
List<Object> converters = new ArrayList<>();
|
||||
|
||||
converters.addAll(GeoConverters.getConvertersToRegister());
|
||||
converters.add(StringToUUIDConverter.INSTANCE);
|
||||
converters.add(UUIDToStringConverter.INSTANCE);
|
||||
@ -62,6 +63,9 @@ public class ElasticsearchCustomConversions extends CustomConversions {
|
||||
super(STORE_CONVERSIONS, converters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to read a {@link UUID} from its {@link String} representation.
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum StringToUUIDConverter implements Converter<String, UUID> {
|
||||
|
||||
@ -73,6 +77,9 @@ public class ElasticsearchCustomConversions extends CustomConversions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to write a {@link UUID} to its {@link String} representation.
|
||||
*/
|
||||
@WritingConverter
|
||||
enum UUIDToStringConverter implements Converter<UUID, String> {
|
||||
|
||||
@ -84,6 +91,9 @@ public class ElasticsearchCustomConversions extends CustomConversions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to read a {@link BigDecimal} from a {@link Double} value.
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum DoubleToBigDecimalConverter implements Converter<Double, BigDecimal> {
|
||||
|
||||
@ -95,6 +105,9 @@ public class ElasticsearchCustomConversions extends CustomConversions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to write a {@link BigDecimal} to a {@link Double} value.
|
||||
*/
|
||||
@WritingConverter
|
||||
enum BigDecimalToDoubleConverter implements Converter<BigDecimal, Double> {
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* http://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.core.convert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.convert.DefaultTypeMapper;
|
||||
import org.springframework.data.convert.TypeAliasAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Elasticsearch specific {@link org.springframework.data.convert.TypeMapper} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
class ElasticsearchDefaultTypeMapper extends DefaultTypeMapper<Map<String, Object>> implements ElasticsearchTypeMapper {
|
||||
|
||||
private final @Nullable String typeKey;
|
||||
|
||||
ElasticsearchDefaultTypeMapper(@Nullable String typeKey, TypeAliasAccessor accessor,
|
||||
@Nullable MappingContext mappingContext, List additionalMappers) {
|
||||
|
||||
super(accessor, mappingContext, additionalMappers);
|
||||
this.typeKey = typeKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeKey(String key) {
|
||||
return typeKey != null && typeKey.equals(key);
|
||||
}
|
||||
}
|
@ -15,17 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.convert.SimpleTypeInformationMapper;
|
||||
import org.springframework.data.convert.TypeAliasAccessor;
|
||||
import org.springframework.data.convert.TypeMapper;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.mapping.Alias;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Elasticsearch specific {@link TypeMapper} definition.
|
||||
@ -48,45 +43,15 @@ public interface ElasticsearchTypeMapper extends TypeMapper<Map<String, Object>>
|
||||
return readType(source) != null;
|
||||
}
|
||||
|
||||
static ElasticsearchTypeMapper defaultTypeMapper(
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
return new ElasticsearchDefaultTypeMapper(DEFAULT_TYPE_KEY, new MapTypeAliasAccessor(DEFAULT_TYPE_KEY),
|
||||
mappingContext, Collections.singletonList(new SimpleTypeInformationMapper()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TypeAliasAccessor} to store aliases in a {@link Map}.
|
||||
* Creates a new default {@link ElasticsearchTypeMapper}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @param mappingContext the mapping context.
|
||||
* @return a new default {@link ElasticsearchTypeMapper}.
|
||||
* @see DefaultElasticsearchTypeMapper
|
||||
*/
|
||||
final class MapTypeAliasAccessor implements TypeAliasAccessor<Map<String, Object>> {
|
||||
|
||||
private final @Nullable String typeKey;
|
||||
|
||||
public MapTypeAliasAccessor(@Nullable String typeKey) {
|
||||
this.typeKey = typeKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.TypeAliasAccessor#readAliasFrom(java.lang.Object)
|
||||
*/
|
||||
public Alias readAliasFrom(Map<String, Object> source) {
|
||||
return Alias.ofNullable(source.get(typeKey));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.TypeAliasAccessor#writeTypeTo(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public void writeTypeTo(Map<String, Object> sink, Object alias) {
|
||||
|
||||
if (typeKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sink.put(typeKey, alias);
|
||||
}
|
||||
static ElasticsearchTypeMapper create(
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
return new DefaultElasticsearchTypeMapper(DEFAULT_TYPE_KEY, mappingContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,14 +35,17 @@ import org.springframework.util.NumberUtils;
|
||||
*/
|
||||
class GeoConverters {
|
||||
|
||||
static Collection<? extends Object> getConvertersToRegister() {
|
||||
static Collection<Object> getConvertersToRegister() {
|
||||
|
||||
return Arrays.asList(PointToMapConverter.INSTANCE, MapToPointConverter.INSTANCE, GeoPointToMapConverter.INSTANCE,
|
||||
MapToGeoPointConverter.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to write a {@link Point} to {@link Map} using {@code lat/long} properties.
|
||||
*/
|
||||
@WritingConverter
|
||||
enum PointToMapConverter implements Converter<Point, Map> {
|
||||
enum PointToMapConverter implements Converter<Point, Map<String, Object>> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@ -56,8 +59,11 @@ class GeoConverters {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to write a {@link GeoPoint} to {@link Map} using {@code lat/long} properties.
|
||||
*/
|
||||
@WritingConverter
|
||||
enum GeoPointToMapConverter implements Converter<GeoPoint, Map> {
|
||||
enum GeoPointToMapConverter implements Converter<GeoPoint, Map<String, Object>> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@ -70,8 +76,11 @@ class GeoConverters {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to read a {@link Point} from {@link Map} using {@code lat/long} properties.
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum MapToPointConverter implements Converter<Map, Point> {
|
||||
enum MapToPointConverter implements Converter<Map<String, Object>, Point> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@ -84,8 +93,11 @@ class GeoConverters {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Converter} to read a {@link GeoPoint} from {@link Map} using {@code lat/long} properties.
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum MapToGeoPointConverter implements Converter<Map, GeoPoint> {
|
||||
enum MapToGeoPointConverter implements Converter<Map<String, Object>, GeoPoint> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
|
@ -15,6 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -62,11 +68,6 @@ import org.springframework.data.elasticsearch.entities.SampleEntity;
|
||||
import org.springframework.data.elasticsearch.entities.SampleMappingEntity;
|
||||
import org.springframework.data.elasticsearch.entities.UseServerConfigurationEntity;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import static org.apache.commons.lang.RandomStringUtils.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.elasticsearch.utils.IndexBuilder.*;
|
||||
|
||||
/**
|
||||
* Base for testing rest/transport templates
|
||||
@ -808,7 +809,7 @@ public class ElasticsearchTemplateTests {
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME).withFields("message").build();
|
||||
// when
|
||||
Page<String> page = elasticsearchTemplate.queryForPage(searchQuery, String.class, new SearchResultMapper() {
|
||||
Page<String> page = elasticsearchTemplate.queryForPage(searchQuery, String.class, new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<String> values = new ArrayList<>();
|
||||
@ -817,11 +818,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return new AggregatedPageImpl<>((List<T>) values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
// then
|
||||
assertThat(page, is(notNullValue()));
|
||||
@ -941,10 +937,10 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
|
||||
|
||||
final SearchResultMapper searchResultMapper = new SearchResultMapper() {
|
||||
final SearchResultMapper searchResultMapper = new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<SampleEntity> result = new ArrayList<SampleEntity>();
|
||||
List<SampleEntity> result = new ArrayList<>();
|
||||
for (SearchHit searchHit : response.getHits()) {
|
||||
if (response.getHits().getHits().length <= 0) {
|
||||
return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
|
||||
@ -959,12 +955,7 @@ public class ElasticsearchTemplateTests {
|
||||
if (result.size() > 0) {
|
||||
return new AggregatedPageImpl<T>((List<T>) result, response.getScrollId());
|
||||
}
|
||||
return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
return new AggregatedPageImpl<T>(Collections.emptyList(), response.getScrollId());
|
||||
}
|
||||
};
|
||||
|
||||
@ -1347,7 +1338,8 @@ public class ElasticsearchTemplateTests {
|
||||
.withHighlightFields(message.toArray(new HighlightBuilder.Field[message.size()]))
|
||||
.build();
|
||||
|
||||
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
|
||||
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class,
|
||||
new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<SampleEntity> chunk = new ArrayList<>();
|
||||
@ -1366,11 +1358,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(sampleEntities.getContent().get(0).getHighlightedMessage(), is(highlightedMessage));
|
||||
@ -1409,7 +1396,7 @@ public class ElasticsearchTemplateTests {
|
||||
.build();
|
||||
|
||||
// when
|
||||
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
|
||||
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
for (SearchHit searchHit : response.getHits()) {
|
||||
@ -1425,11 +1412,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1458,7 +1440,7 @@ public class ElasticsearchTemplateTests {
|
||||
.withHighlightFields(new HighlightBuilder.Field("message"))
|
||||
.build();
|
||||
// when
|
||||
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
|
||||
elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
for (SearchHit searchHit : response.getHits()) {
|
||||
@ -1471,11 +1453,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1621,7 +1598,8 @@ public class ElasticsearchTemplateTests {
|
||||
.withTypes(TYPE_NAME)
|
||||
.build();
|
||||
// then
|
||||
Page<SampleEntity> page = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class, new SearchResultMapper() {
|
||||
Page<SampleEntity> page = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class,
|
||||
new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<SampleEntity> values = new ArrayList<>();
|
||||
@ -1633,11 +1611,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return new AggregatedPageImpl<>((List<T>) values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertThat(page, is(notNullValue()));
|
||||
assertThat(page.getContent().size(), is(1));
|
||||
@ -1826,7 +1799,8 @@ public class ElasticsearchTemplateTests {
|
||||
// then
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME).withQuery(matchAllQuery()).build();
|
||||
Page<Map> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, Map.class, new SearchResultMapper() {
|
||||
Page<Map> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, Map.class,
|
||||
new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<Map> chunk = new ArrayList<>();
|
||||
@ -1847,11 +1821,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertThat(sampleEntities.getTotalElements(), is(equalTo(2L)));
|
||||
assertThat(sampleEntities.getContent().get(0).get("userId"), is(person1.get("userId")));
|
||||
@ -2400,7 +2369,8 @@ public class ElasticsearchTemplateTests {
|
||||
// When
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withTypes("hetro").withIndices(INDEX_1_NAME, INDEX_2_NAME).build();
|
||||
Page<ResultAggregator> page = elasticsearchTemplate.queryForPage(searchQuery, ResultAggregator.class, new SearchResultMapper() {
|
||||
Page<ResultAggregator> page = elasticsearchTemplate.queryForPage(searchQuery, ResultAggregator.class,
|
||||
new SearchResultMapperAdapter() {
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
List<ResultAggregator> values = new ArrayList<>();
|
||||
@ -2412,11 +2382,6 @@ public class ElasticsearchTemplateTests {
|
||||
}
|
||||
return new AggregatedPageImpl<>((List<T>) values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(page.getTotalElements(), is(2l));
|
||||
|
Loading…
x
Reference in New Issue
Block a user