@@ -168,8 +168,15 @@ public void afterPropertiesSet() {
168168 @ SuppressWarnings ("unchecked" )
169169 @ Override
170170 public <R > R read (Class <R > type , Document source ) {
171+
171172 TypeInformation <R > typeHint = ClassTypeInformation .from ((Class <R >) ClassUtils .getUserClass (type ));
172- return read (typeHint , source );
173+ R r = read (typeHint , source );
174+
175+ if (r == null ) {
176+ throw new ConversionException ("could not convert into object of class " + type );
177+ }
178+
179+ return r ;
173180 }
174181
175182 protected <R > R readEntity (ElasticsearchPersistentEntity <?> entity , Map <String , Object > source ) {
@@ -188,7 +195,7 @@ protected <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String,
188195
189196 EntityInstantiator instantiator = instantiators .getInstantiatorFor (targetEntity );
190197
191- @ SuppressWarnings ({ "unchecked" , "ConstantConditions" })
198+ @ SuppressWarnings ({ "unchecked" })
192199 R instance = (R ) instantiator .createInstance (targetEntity , propertyValueProvider );
193200
194201 if (!targetEntity .requiresPropertyPopulation ()) {
@@ -246,6 +253,7 @@ private ParameterValueProvider<ElasticsearchPersistentProperty> getParameterProv
246253 ElasticsearchPropertyValueProvider provider = new ElasticsearchPropertyValueProvider (source , evaluator );
247254
248255 // TODO: Support for non-static inner classes via ObjectPath
256+ // noinspection ConstantConditions
249257 PersistentEntityParameterValueProvider <ElasticsearchPersistentProperty > parameterProvider = new PersistentEntityParameterValueProvider <>(
250258 entity , provider , null );
251259
@@ -281,7 +289,6 @@ protected <R> R readProperties(ElasticsearchPersistentEntity<?> entity, R instan
281289 return accessor .getBean ();
282290 }
283291
284- @ SuppressWarnings ("unchecked" )
285292 @ Nullable
286293 protected <R > R readValue (@ Nullable Object value , ElasticsearchPersistentProperty property , TypeInformation <?> type ) {
287294
@@ -349,7 +356,7 @@ private <R> R read(TypeInformation<R> type, Map<String, Object> source) {
349356 }
350357
351358 if (typeToUse .isMap ()) {
352- return ( R ) readMap (typeToUse , source );
359+ return readMap (typeToUse , source );
353360 }
354361
355362 if (typeToUse .equals (ClassTypeInformation .OBJECT )) {
@@ -549,7 +556,7 @@ public void write(Object source, Document sink) {
549556 }
550557
551558 Class <?> entityType = ClassUtils .getUserClass (source .getClass ());
552- TypeInformation <? extends Object > typeInformation = ClassTypeInformation .from (entityType );
559+ TypeInformation <?> typeInformation = ClassTypeInformation .from (entityType );
553560
554561 if (requiresTypeHint (entityType )) {
555562 typeMapper .writeType (typeInformation , sink );
@@ -561,9 +568,9 @@ public void write(Object source, Document sink) {
561568 /**
562569 * Internal write conversion method which should be used for nested invocations.
563570 *
564- * @param source
565- * @param sink
566- * @param typeInformation
571+ * @param source the object to write
572+ * @param sink the write destination
573+ * @param typeInformation type information for the source
567574 */
568575 @ SuppressWarnings ("unchecked" )
569576 protected void writeInternal (@ Nullable Object source , Map <String , Object > sink ,
@@ -578,7 +585,10 @@ protected void writeInternal(@Nullable Object source, Map<String, Object> sink,
578585
579586 if (customTarget .isPresent ()) {
580587 Map <String , Object > result = conversionService .convert (source , Map .class );
581- sink .putAll (result );
588+
589+ if (result != null ) {
590+ sink .putAll (result );
591+ }
582592 return ;
583593 }
584594
@@ -600,9 +610,9 @@ protected void writeInternal(@Nullable Object source, Map<String, Object> sink,
600610 /**
601611 * Internal write conversion method which should be used for nested invocations.
602612 *
603- * @param source
604- * @param sink
605- * @param entity
613+ * @param source the object to write
614+ * @param sink the write destination
615+ * @param entity entity for the source
606616 */
607617 protected void writeInternal (@ Nullable Object source , Map <String , Object > sink ,
608618 @ Nullable ElasticsearchPersistentEntity <?> entity ) {
@@ -734,7 +744,6 @@ protected void writeProperty(ElasticsearchPersistentProperty property, Object va
734744 *
735745 * @param collection must not be {@literal null}.
736746 * @param property must not be {@literal null}.
737- * @return
738747 */
739748 protected List <Object > createCollection (Collection <?> collection , ElasticsearchPersistentProperty property ) {
740749 return writeCollectionInternal (collection , property .getTypeInformation (), new ArrayList <>(collection .size ()));
@@ -745,7 +754,6 @@ protected List<Object> createCollection(Collection<?> collection, ElasticsearchP
745754 *
746755 * @param map must not {@literal null}.
747756 * @param property must not be {@literal null}.
748- * @return
749757 */
750758 protected Map <String , Object > createMap (Map <?, ?> map , ElasticsearchPersistentProperty property ) {
751759
@@ -761,7 +769,6 @@ protected Map<String, Object> createMap(Map<?, ?> map, ElasticsearchPersistentPr
761769 * @param source must not be {@literal null}.
762770 * @param sink must not be {@literal null}.
763771 * @param propertyType must not be {@literal null}.
764- * @return
765772 */
766773 protected Map <String , Object > writeMapInternal (Map <?, ?> source , Map <String , Object > sink ,
767774 TypeInformation <?> propertyType ) {
@@ -801,7 +808,6 @@ protected Map<String, Object> writeMapInternal(Map<?, ?> source, Map<String, Obj
801808 * @param source the collection to create a {@link Collection} for, must not be {@literal null}.
802809 * @param type the {@link TypeInformation} to consider or {@literal null} if unknown.
803810 * @param sink the {@link Collection} to write to.
804- * @return
805811 */
806812 @ SuppressWarnings ("unchecked" )
807813 private List <Object > writeCollectionInternal (Collection <?> source , @ Nullable TypeInformation <?> type ,
@@ -837,26 +843,30 @@ private List<Object> writeCollectionInternal(Collection<?> source, @Nullable Typ
837843 /**
838844 * Returns a {@link String} representation of the given {@link Map} key
839845 *
840- * @param key
841- * @return
846+ * @param key the key to convert
842847 */
843848 private String potentiallyConvertMapKey (Object key ) {
844849
845850 if (key instanceof String ) {
846851 return (String ) key ;
847852 }
848853
849- return conversions .hasCustomWriteTarget (key .getClass (), String .class )
850- ? (String ) getPotentiallyConvertedSimpleWrite (key , Object .class )
851- : key .toString ();
854+ if (conversions .hasCustomWriteTarget (key .getClass (), String .class )) {
855+ Object potentiallyConvertedSimpleWrite = getPotentiallyConvertedSimpleWrite (key , Object .class );
856+
857+ if (potentiallyConvertedSimpleWrite == null ) {
858+ return key .toString ();
859+ }
860+ return (String ) potentiallyConvertedSimpleWrite ;
861+ }
862+ return key .toString ();
852863 }
853864
854865 /**
855866 * Checks whether we have a custom conversion registered for the given value into an arbitrary simple Elasticsearch
856867 * type. Returns the converted value if so. If not, we perform special enum handling or simply return the value as is.
857868 *
858- * @param value
859- * @return
869+ * @param value value to convert
860870 */
861871 @ Nullable
862872 private Object getPotentiallyConvertedSimpleWrite (@ Nullable Object value , @ Nullable Class <?> typeHint ) {
@@ -869,6 +879,10 @@ private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nulla
869879
870880 if (conversionService .canConvert (value .getClass (), typeHint )) {
871881 value = conversionService .convert (value , typeHint );
882+
883+ if (value == null ) {
884+ return null ;
885+ }
872886 }
873887 }
874888
@@ -908,8 +922,8 @@ protected Object getWriteSimpleValue(Object value) {
908922 * @deprecated since 4.2, use {@link #writeInternal(Object, Map, TypeInformation)} instead.
909923 */
910924 @ Deprecated
911- protected Object getWriteComplexValue (ElasticsearchPersistentProperty property , TypeInformation <?> typeHint ,
912- Object value ) {
925+ protected Object getWriteComplexValue (ElasticsearchPersistentProperty property ,
926+ @ SuppressWarnings ( "unused" ) TypeInformation <?> typeHint , Object value ) {
913927
914928 Document document = Document .create ();
915929 writeInternal (value , document , property .getTypeInformation ());
@@ -928,11 +942,19 @@ protected Object getWriteComplexValue(ElasticsearchPersistentProperty property,
928942 *
929943 * @param source must not be {@literal null}.
930944 * @param sink must not be {@literal null}.
931- * @param type
945+ * @param type type to compare to
932946 */
933- protected void addCustomTypeKeyIfNecessary (Object source , Map <String , Object > sink , @ Nullable TypeInformation <?> type ) {
947+ protected void addCustomTypeKeyIfNecessary (Object source , Map <String , Object > sink ,
948+ @ Nullable TypeInformation <?> type ) {
934949
935- Class <?> reference = type != null ? type .getActualType ().getType () : Object .class ;
950+ Class <?> reference ;
951+
952+ if (type == null ) {
953+ reference = Object .class ;
954+ } else {
955+ TypeInformation <?> actualType = type .getActualType ();
956+ reference = actualType == null ? Object .class : actualType .getType ();
957+ }
936958 Class <?> valueType = ClassUtils .getUserClass (source .getClass ());
937959
938960 boolean notTheSameClass = !valueType .equals (reference );
@@ -987,8 +1009,7 @@ private boolean isSimpleType(Class<?> type) {
9871009 * {@link Collection} already, will convert an array into a {@link Collection} or simply create a single element
9881010 * collection for everything else.
9891011 *
990- * @param source
991- * @return
1012+ * @param source object to convert
9921013 */
9931014 private static Collection <?> asCollection (Object source ) {
9941015
@@ -1019,21 +1040,42 @@ public void updateCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClas
10191040 }
10201041
10211042 private void updateCriteria (Criteria criteria , ElasticsearchPersistentEntity <?> persistentEntity ) {
1043+
10221044 Field field = criteria .getField ();
10231045
10241046 if (field == null ) {
10251047 return ;
10261048 }
10271049
1028- String name = field .getName ();
1029- ElasticsearchPersistentProperty property = persistentEntity .getPersistentProperty (name );
1050+ String [] fieldNames = field .getName ().split ("\\ ." );
1051+ ElasticsearchPersistentEntity <?> currentEntity = persistentEntity ;
1052+ ElasticsearchPersistentProperty persistentProperty = null ;
1053+ for (int i = 0 ; i < fieldNames .length ; i ++) {
1054+ persistentProperty = currentEntity .getPersistentProperty (fieldNames [i ]);
1055+
1056+ if (persistentProperty != null ) {
1057+ fieldNames [i ] = persistentProperty .getFieldName ();
1058+ try {
1059+ currentEntity = mappingContext .getPersistentEntity (persistentProperty .getActualType ());
1060+ } catch (Exception e ) {
1061+ // using system types like UUIDs will lead to java.lang.reflect.InaccessibleObjectException in JDK 16
1062+ // so if we cannot get an entity here, bail out.
1063+ currentEntity = null ;
1064+ }
1065+ }
1066+
1067+ if (currentEntity == null ) {
1068+ break ;
1069+ }
1070+ }
1071+
1072+ field .setName (String .join ("." , fieldNames ));
10301073
1031- if (property != null && property .getName ().equals (name )) {
1032- field .setName (property .getFieldName ());
1074+ if (persistentProperty != null ) {
10331075
1034- if (property .hasPropertyConverter ()) {
1076+ if (persistentProperty .hasPropertyConverter ()) {
10351077 ElasticsearchPersistentPropertyConverter propertyConverter = Objects
1036- .requireNonNull (property .getPropertyConverter ());
1078+ .requireNonNull (persistentProperty .getPropertyConverter ());
10371079 criteria .getQueryCriteriaEntries ().forEach (criteriaEntry -> {
10381080 Object value = criteriaEntry .getValue ();
10391081 if (value .getClass ().isArray ()) {
@@ -1047,14 +1089,15 @@ private void updateCriteria(Criteria criteria, ElasticsearchPersistentEntity<?>
10471089 });
10481090 }
10491091
1050- org .springframework .data .elasticsearch .annotations .Field fieldAnnotation = property
1092+ org .springframework .data .elasticsearch .annotations .Field fieldAnnotation = persistentProperty
10511093 .findAnnotation (org .springframework .data .elasticsearch .annotations .Field .class );
10521094
10531095 if (fieldAnnotation != null ) {
10541096 field .setFieldType (fieldAnnotation .type ());
10551097 }
10561098 }
10571099 }
1100+
10581101 // endregion
10591102
10601103 static class MapValueAccessor {
@@ -1148,7 +1191,6 @@ class ElasticsearchPropertyValueProvider implements PropertyValueProvider<Elasti
11481191 this .evaluator = evaluator ;
11491192 }
11501193
1151- @ SuppressWarnings ("unchecked" )
11521194 @ Override
11531195 public <T > T getPropertyValue (ElasticsearchPersistentProperty property ) {
11541196
0 commit comments