2121import java .util .concurrent .ConcurrentHashMap ;
2222import java .util .stream .Collectors ;
2323
24+ import org .apache .commons .logging .Log ;
25+ import org .apache .commons .logging .LogFactory ;
26+ import java .time .temporal .TemporalAccessor ;
27+ import java .util .*;
28+ import java .util .Map .Entry ;
29+ import java .util .concurrent .ConcurrentHashMap ;
30+ import java .util .stream .Collectors ;
31+
2432import org .slf4j .Logger ;
2533import org .slf4j .LoggerFactory ;
2634import org .springframework .beans .BeansException ;
5159import org .springframework .data .mapping .PersistentPropertyAccessor ;
5260import org .springframework .data .mapping .PreferredConstructor ;
5361import org .springframework .data .mapping .context .MappingContext ;
54- import org .springframework .data .mapping .model .ConvertingPropertyAccessor ;
55- import org .springframework .data .mapping .model .DefaultSpELExpressionEvaluator ;
56- import org .springframework .data .mapping .model .EntityInstantiator ;
57- import org .springframework .data .mapping .model .EntityInstantiators ;
58- import org .springframework .data .mapping .model .ParameterValueProvider ;
59- import org .springframework .data .mapping .model .PersistentEntityParameterValueProvider ;
60- import org .springframework .data .mapping .model .PropertyValueProvider ;
61- import org .springframework .data .mapping .model .SpELContext ;
62- import org .springframework .data .mapping .model .SpELExpressionEvaluator ;
63- import org .springframework .data .mapping .model .SpELExpressionParameterValueProvider ;
62+ import org .springframework .data .mapping .model .*;
6463import org .springframework .data .util .ClassTypeInformation ;
6564import org .springframework .data .util .TypeInformation ;
6665import org .springframework .format .datetime .DateFormatterRegistrar ;
@@ -196,7 +195,7 @@ private Base(
196195 }
197196
198197 /**
199- * Class to do the actual writing . The methods originally were in the MappingElasticsearchConverter class, but are
198+ * Class to do the actual reading . The methods originally were in the MappingElasticsearchConverter class, but are
200199 * refactored to allow for keeping state during the conversion of an object.
201200 */
202201 private static class Reader extends Base {
@@ -215,10 +214,17 @@ public Reader(
215214 }
216215
217216 @ SuppressWarnings ("unchecked" )
217+ /**
218+ * Reads the given source into the given type.
219+ *
220+ * @param type they type to convert the given source to.
221+ * @param source the source to create an object of the given type from.
222+ * @return the object that was read
223+ */
218224 <R > R read (Class <R > type , Document source ) {
219225
220- TypeInformation <R > typeHint = ClassTypeInformation .from ((Class <R >) ClassUtils .getUserClass (type ));
221- R r = read (typeHint , source );
226+ TypeInformation <R > typeInformation = ClassTypeInformation .from ((Class <R >) ClassUtils .getUserClass (type ));
227+ R r = read (typeInformation , source );
222228
223229 if (r == null ) {
224230 throw new ConversionException ("could not convert into object of class " + type );
@@ -229,11 +235,11 @@ <R> R read(Class<R> type, Document source) {
229235
230236 @ Nullable
231237 @ SuppressWarnings ("unchecked" )
232- private <R > R read (TypeInformation <R > type , Map <String , Object > source ) {
238+ private <R > R read (TypeInformation <R > typeInformation , Map <String , Object > source ) {
233239
234240 Assert .notNull (source , "Source must not be null!" );
235241
236- TypeInformation <? extends R > typeToUse = typeMapper .readType (source , type );
242+ TypeInformation <? extends R > typeToUse = typeMapper .readType (source , typeInformation );
237243 Class <? extends R > rawType = typeToUse .getType ();
238244
239245 if (conversions .hasCustomReadTarget (source .getClass (), rawType )) {
@@ -251,8 +257,8 @@ private <R> R read(TypeInformation<R> type, Map<String, Object> source) {
251257 if (typeToUse .equals (ClassTypeInformation .OBJECT )) {
252258 return (R ) source ;
253259 }
254- // Retrieve persistent entity info
255260
261+ // Retrieve persistent entity info
256262 ElasticsearchPersistentEntity <?> entity = mappingContext .getPersistentEntity (typeToUse );
257263
258264 if (entity == null ) {
@@ -370,7 +376,6 @@ private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Ob
370376 }
371377
372378 return result ;
373-
374379 }
375380
376381 private ParameterValueProvider <ElasticsearchPersistentProperty > getParameterProvider (
@@ -460,12 +465,44 @@ private <T> T readValue(Object value, TypeInformation<?> type) {
460465 } else if (value .getClass ().isArray ()) {
461466 return (T ) readCollectionOrArray (type , Arrays .asList ((Object []) value ));
462467 } else if (value instanceof Map ) {
468+
469+ TypeInformation <?> collectionComponentType = getCollectionComponentType (type );
470+ if (collectionComponentType != null ) {
471+ Object o = read (collectionComponentType , (Map <String , Object >) value );
472+ return getCollectionWithSingleElement (type , collectionComponentType , o );
473+ }
463474 return (T ) read (type , (Map <String , Object >) value );
464475 } else {
476+
477+ TypeInformation <?> collectionComponentType = getCollectionComponentType (type );
478+ if (collectionComponentType != null
479+ && collectionComponentType .isAssignableFrom (ClassTypeInformation .from (value .getClass ()))) {
480+ Object o = getPotentiallyConvertedSimpleRead (value , collectionComponentType );
481+ return getCollectionWithSingleElement (type , collectionComponentType , o );
482+ }
483+
465484 return (T ) getPotentiallyConvertedSimpleRead (value , rawType );
466485 }
467486 }
468487
488+ @ SuppressWarnings ("unchecked" )
489+ private static <T > T getCollectionWithSingleElement (TypeInformation <?> collectionType ,
490+ TypeInformation <?> componentType , Object element ) {
491+ Collection <Object > collection = CollectionFactory .createCollection (collectionType .getType (),
492+ componentType .getType (), 1 );
493+ collection .add (element );
494+ return (T ) collection ;
495+ }
496+
497+ /**
498+ * @param type the type to check
499+ * @return true if type is a collectoin, null otherwise,
500+ */
501+ @ Nullable
502+ TypeInformation <?> getCollectionComponentType (TypeInformation <?> type ) {
503+ return type .isCollectionLike () ? type .getComponentType () : null ;
504+ }
505+
469506 private Object propertyConverterRead (ElasticsearchPersistentProperty property , Object source ) {
470507 PropertyValueConverter propertyValueConverter = Objects .requireNonNull (property .getPropertyValueConverter ());
471508
@@ -1131,17 +1168,18 @@ public void updateQuery(Query query, @Nullable Class<?> domainClass) {
11311168
11321169 Assert .notNull (query , "query must not be null" );
11331170
1134- if (domainClass != null ) {
1171+ if (domainClass == null ) {
1172+ return ;
1173+ }
11351174
1136- updateFieldsAndSourceFilter (query , domainClass );
1175+ updatePropertiesInFieldsAndSourceFilter (query , domainClass );
11371176
1138- if (query instanceof CriteriaQuery ) {
1139- updateCriteriaQuery ((CriteriaQuery ) query , domainClass );
1140- }
1177+ if (query instanceof CriteriaQuery ) {
1178+ updatePropertiesInCriteriaQuery ((CriteriaQuery ) query , domainClass );
11411179 }
11421180 }
11431181
1144- private void updateFieldsAndSourceFilter (Query query , Class <?> domainClass ) {
1182+ private void updatePropertiesInFieldsAndSourceFilter (Query query , Class <?> domainClass ) {
11451183
11461184 ElasticsearchPersistentEntity <?> persistentEntity = mappingContext .getPersistentEntity (domainClass );
11471185
@@ -1174,14 +1212,22 @@ private void updateFieldsAndSourceFilter(Query query, Class<?> domainClass) {
11741212 }
11751213 }
11761214
1177- private List <String > updateFieldNames (List <String > fields , ElasticsearchPersistentEntity <?> persistentEntity ) {
1178- return fields .stream ().map (fieldName -> {
1215+ /**
1216+ * relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
1217+ * fieldname. If no such property exists, the original fieldName is kept.
1218+ *
1219+ * @param fieldNames list of fieldnames
1220+ * @param persistentEntity the persistent entity to check
1221+ * @return an updated list of field names
1222+ */
1223+ private List <String > updateFieldNames (List <String > fieldNames , ElasticsearchPersistentEntity <?> persistentEntity ) {
1224+ return fieldNames .stream ().map (fieldName -> {
11791225 ElasticsearchPersistentProperty persistentProperty = persistentEntity .getPersistentProperty (fieldName );
11801226 return persistentProperty != null ? persistentProperty .getFieldName () : fieldName ;
11811227 }).collect (Collectors .toList ());
11821228 }
11831229
1184- private void updateCriteriaQuery (CriteriaQuery criteriaQuery , Class <?> domainClass ) {
1230+ private void updatePropertiesInCriteriaQuery (CriteriaQuery criteriaQuery , Class <?> domainClass ) {
11851231
11861232 Assert .notNull (criteriaQuery , "criteriaQuery must not be null" );
11871233 Assert .notNull (domainClass , "domainClass must not be null" );
@@ -1190,17 +1236,17 @@ private void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainCla
11901236
11911237 if (persistentEntity != null ) {
11921238 for (Criteria chainedCriteria : criteriaQuery .getCriteria ().getCriteriaChain ()) {
1193- updateCriteria (chainedCriteria , persistentEntity );
1239+ updatePropertiesInCriteria (chainedCriteria , persistentEntity );
11941240 }
11951241 for (Criteria subCriteria : criteriaQuery .getCriteria ().getSubCriteria ()) {
11961242 for (Criteria chainedCriteria : subCriteria .getCriteriaChain ()) {
1197- updateCriteria (chainedCriteria , persistentEntity );
1243+ updatePropertiesInCriteria (chainedCriteria , persistentEntity );
11981244 }
11991245 }
12001246 }
12011247 }
12021248
1203- private void updateCriteria (Criteria criteria , ElasticsearchPersistentEntity <?> persistentEntity ) {
1249+ private void updatePropertiesInCriteria (Criteria criteria , ElasticsearchPersistentEntity <?> persistentEntity ) {
12041250
12051251 Field field = criteria .getField ();
12061252
0 commit comments