@@ -395,81 +395,6 @@ private Object doReadOrProject(ConversionContext context, Bson source, TypeInfor
395
395
return readDocument (context , source , typeHint );
396
396
}
397
397
398
- static class AssociationConversionContext implements ConversionContext {
399
-
400
- private final ConversionContext delegate ;
401
-
402
- public AssociationConversionContext (ConversionContext delegate ) {
403
- this .delegate = delegate ;
404
- }
405
-
406
- @ Override
407
- public <S > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context ) {
408
- return delegate .convert (source , typeHint , context );
409
- }
410
-
411
- @ Override
412
- public ConversionContext withPath (ObjectPath currentPath ) {
413
- return new AssociationConversionContext (delegate .withPath (currentPath ));
414
- }
415
-
416
- @ Override
417
- public ObjectPath getPath () {
418
- return delegate .getPath ();
419
- }
420
-
421
- @ Override
422
- public CustomConversions getCustomConversions () {
423
- return delegate .getCustomConversions ();
424
- }
425
-
426
- @ Override
427
- public MongoConverter getSourceConverter () {
428
- return delegate .getSourceConverter ();
429
- }
430
-
431
- @ Override
432
- public boolean resolveIdsInContext () {
433
- return true ;
434
- }
435
- }
436
-
437
- class ProjectingConversionContext extends DefaultConversionContext {
438
-
439
- private final EntityProjection <?, ?> returnedTypeDescriptor ;
440
-
441
- ProjectingConversionContext (MongoConverter sourceConverter , CustomConversions customConversions , ObjectPath path ,
442
- ContainerValueConverter <Collection <?>> collectionConverter , ContainerValueConverter <Bson > mapConverter ,
443
- ContainerValueConverter <DBRef > dbRefConverter , ValueConverter <Object > elementConverter ,
444
- EntityProjection <?, ?> projection ) {
445
- super (sourceConverter , customConversions , path ,
446
- (context , source , typeHint ) -> doReadOrProject (context , source , typeHint , projection ),
447
-
448
- collectionConverter , mapConverter , dbRefConverter , elementConverter );
449
- this .returnedTypeDescriptor = projection ;
450
- }
451
-
452
- @ Override
453
- public DefaultConversionContext forProperty (String name ) {
454
-
455
- EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
456
- if (property == null ) {
457
- return new DefaultConversionContext (sourceConverter , conversions , path ,
458
- MappingMongoConverter .this ::readDocument , collectionConverter , mapConverter , dbRefConverter ,
459
- elementConverter );
460
- }
461
-
462
- return new ProjectingConversionContext (sourceConverter , conversions , path , collectionConverter , mapConverter ,
463
- dbRefConverter , elementConverter , property );
464
- }
465
-
466
- @ Override
467
- public DefaultConversionContext withPath (ObjectPath currentPath ) {
468
- return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
469
- mapConverter , dbRefConverter , elementConverter , returnedTypeDescriptor );
470
- }
471
- }
472
-
473
398
static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor <Map <String , Object >> {
474
399
475
400
Map <String , Object > map = new LinkedHashMap <>();
@@ -580,16 +505,14 @@ private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(Con
580
505
581
506
private <S > S read (ConversionContext context , MongoPersistentEntity <S > entity , Document bson ) {
582
507
508
+ S existing = context .findContextualEntity (entity , bson );
509
+ if (existing != null ) {
510
+ return existing ;
511
+ }
512
+
583
513
SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator (bson , spELContext );
584
514
DocumentAccessor documentAccessor = new DocumentAccessor (bson );
585
515
586
- if (context .resolveIdsInContext () && hasIdentifier (bson )) {
587
- S existing = findContextualEntity (context , entity , bson );
588
- if (existing != null ) {
589
- return existing ;
590
- }
591
- }
592
-
593
516
PreferredConstructor <S , MongoPersistentProperty > persistenceConstructor = entity .getPersistenceConstructor ();
594
517
595
518
ParameterValueProvider <MongoPersistentProperty > provider = persistenceConstructor != null
@@ -607,16 +530,6 @@ private <S> S read(ConversionContext context, MongoPersistentEntity<S> entity, D
607
530
return instance ;
608
531
}
609
532
610
- private boolean hasIdentifier (Document bson ) {
611
- return bson .get (BasicMongoPersistentProperty .ID_FIELD_NAME ) != null ;
612
- }
613
-
614
- @ Nullable
615
- private <S > S findContextualEntity (ConversionContext context , MongoPersistentEntity <S > entity , Document bson ) {
616
- return context .getPath ().getPathItem (bson .get (BasicMongoPersistentProperty .ID_FIELD_NAME ), entity .getCollection (),
617
- entity .getType ());
618
- }
619
-
620
533
private <S > S populateProperties (ConversionContext context , MongoPersistentEntity <S > entity ,
621
534
DocumentAccessor documentAccessor , SpELExpressionEvaluator evaluator , S instance ) {
622
535
@@ -2240,40 +2153,130 @@ public TypeDescriptor toTypeDescriptor() {
2240
2153
}
2241
2154
}
2242
2155
2156
+ /**
2157
+ * Conversion context defining an interface for graph-traversal-based conversion of documents. Entrypoint for
2158
+ * recursive conversion of {@link Document} and other types.
2159
+ *
2160
+ * @since 3.4.3
2161
+ */
2243
2162
interface ConversionContext {
2244
2163
2164
+ /**
2165
+ * Converts a source object into {@link TypeInformation target}.
2166
+ *
2167
+ * @param source must not be {@literal null}.
2168
+ * @param typeHint must not be {@literal null}.
2169
+ * @return the converted object.
2170
+ */
2245
2171
default <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ) {
2246
2172
return convert (source , typeHint , this );
2247
2173
}
2248
2174
2175
+ /**
2176
+ * Converts a source object into {@link TypeInformation target}.
2177
+ *
2178
+ * @param source must not be {@literal null}.
2179
+ * @param typeHint must not be {@literal null}.
2180
+ * @param context must not be {@literal null}.
2181
+ * @return the converted object.
2182
+ */
2249
2183
<S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context );
2250
2184
2185
+ /**
2186
+ * Create a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2187
+ *
2188
+ * @param currentPath must not be {@literal null}.
2189
+ * @return a new {@link ConversionContext} with {@link ObjectPath currentPath} applied.
2190
+ */
2251
2191
ConversionContext withPath (ObjectPath currentPath );
2252
2192
2253
- ObjectPath getPath ();
2254
-
2193
+ /**
2194
+ * Obtain a {@link ConversionContext} for the given property {@code name}.
2195
+ *
2196
+ * @param name must not be {@literal null}.
2197
+ * @return the {@link ConversionContext} to be used for conversion of the given property.
2198
+ */
2255
2199
default ConversionContext forProperty (String name ) {
2256
2200
return this ;
2257
2201
}
2258
2202
2259
- default ConversionContext forProperty (@ Nullable PersistentProperty property ) {
2203
+ /**
2204
+ * Obtain a {@link ConversionContext} for the given {@link MongoPersistentProperty}.
2205
+ *
2206
+ * @param property must not be {@literal null}.
2207
+ * @return the {@link ConversionContext} to be used for conversion of the given property.
2208
+ */
2209
+ default ConversionContext forProperty (MongoPersistentProperty property ) {
2260
2210
2261
- if (property != null ) {
2262
- if (property .isAssociation ()) {
2263
- return new AssociationConversionContext (forProperty (property .getName ()));
2264
- }
2265
- return forProperty (property .getName ());
2266
- }
2267
- return this ;
2211
+ return property .isAssociation () ? new AssociationConversionContext (forProperty (property .getName ()))
2212
+ : forProperty (property .getName ());
2268
2213
}
2269
2214
2270
- default boolean resolveIdsInContext () {
2271
- return false ;
2215
+ /**
2216
+ * Lookup a potentially existing entity instance of the given {@link MongoPersistentEntity} and {@link Document}
2217
+ *
2218
+ * @param entity
2219
+ * @param document
2220
+ * @return
2221
+ * @param <S>
2222
+ */
2223
+ @ Nullable
2224
+ default <S > S findContextualEntity (MongoPersistentEntity <S > entity , Document document ) {
2225
+ return null ;
2272
2226
}
2273
2227
2228
+ ObjectPath getPath ();
2229
+
2274
2230
CustomConversions getCustomConversions ();
2275
2231
2276
2232
MongoConverter getSourceConverter ();
2233
+
2234
+ }
2235
+
2236
+ /**
2237
+ * @since 3.4.3
2238
+ */
2239
+ static class AssociationConversionContext implements ConversionContext {
2240
+
2241
+ private final ConversionContext delegate ;
2242
+
2243
+ public AssociationConversionContext (ConversionContext delegate ) {
2244
+ this .delegate = delegate ;
2245
+ }
2246
+
2247
+ @ Override
2248
+ public <S > S convert (Object source , TypeInformation <? extends S > typeHint , ConversionContext context ) {
2249
+ return delegate .convert (source , typeHint , context );
2250
+ }
2251
+
2252
+ @ Override
2253
+ public ConversionContext withPath (ObjectPath currentPath ) {
2254
+ return new AssociationConversionContext (delegate .withPath (currentPath ));
2255
+ }
2256
+
2257
+ @ Override
2258
+ public <S > S findContextualEntity (MongoPersistentEntity <S > entity , Document document ) {
2259
+
2260
+ Object identifier = document .get (BasicMongoPersistentProperty .ID_FIELD_NAME );
2261
+
2262
+ return identifier != null ? getPath ().getPathItem (identifier , entity .getCollection (), entity .getType ()) : null ;
2263
+ }
2264
+
2265
+ @ Override
2266
+ public ObjectPath getPath () {
2267
+ return delegate .getPath ();
2268
+ }
2269
+
2270
+ @ Override
2271
+ public CustomConversions getCustomConversions () {
2272
+ return delegate .getCustomConversions ();
2273
+ }
2274
+
2275
+ @ Override
2276
+ public MongoConverter getSourceConverter () {
2277
+ return delegate .getSourceConverter ();
2278
+ }
2279
+
2277
2280
}
2278
2281
2279
2282
/**
@@ -2309,14 +2312,8 @@ protected static class DefaultConversionContext implements ConversionContext {
2309
2312
this .elementConverter = elementConverter ;
2310
2313
}
2311
2314
2312
- /**
2313
- * Converts a source object into {@link TypeInformation target}.
2314
- *
2315
- * @param source must not be {@literal null}.
2316
- * @param typeHint must not be {@literal null}.
2317
- * @return the converted object.
2318
- */
2319
2315
@ SuppressWarnings ("unchecked" )
2316
+ @ Override
2320
2317
public <S extends Object > S convert (Object source , TypeInformation <? extends S > typeHint ,
2321
2318
ConversionContext context ) {
2322
2319
@@ -2382,28 +2379,20 @@ public MongoConverter getSourceConverter() {
2382
2379
return sourceConverter ;
2383
2380
}
2384
2381
2385
- /**
2386
- * Create a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2387
- *
2388
- * @param currentPath must not be {@literal null}.
2389
- * @return a new {@link DefaultConversionContext} with {@link ObjectPath currentPath} applied.
2390
- */
2391
- public DefaultConversionContext withPath (ObjectPath currentPath ) {
2382
+ @ Override
2383
+ public ConversionContext withPath (ObjectPath currentPath ) {
2392
2384
2393
2385
Assert .notNull (currentPath , "ObjectPath must not be null" );
2394
2386
2395
2387
return new DefaultConversionContext (sourceConverter , conversions , currentPath , documentConverter ,
2396
2388
collectionConverter , mapConverter , dbRefConverter , elementConverter );
2397
2389
}
2398
2390
2391
+ @ Override
2399
2392
public ObjectPath getPath () {
2400
2393
return path ;
2401
2394
}
2402
2395
2403
- public DefaultConversionContext forProperty (String name ) {
2404
- return this ;
2405
- }
2406
-
2407
2396
/**
2408
2397
* Converts a simple {@code source} value into {@link TypeInformation the target type}.
2409
2398
*
@@ -2429,6 +2418,45 @@ interface ContainerValueConverter<T> {
2429
2418
2430
2419
}
2431
2420
2421
+ /**
2422
+ * @since 3.4.3
2423
+ */
2424
+ class ProjectingConversionContext extends DefaultConversionContext {
2425
+
2426
+ private final EntityProjection <?, ?> returnedTypeDescriptor ;
2427
+
2428
+ ProjectingConversionContext (MongoConverter sourceConverter , CustomConversions customConversions , ObjectPath path ,
2429
+ ContainerValueConverter <Collection <?>> collectionConverter , ContainerValueConverter <Bson > mapConverter ,
2430
+ ContainerValueConverter <DBRef > dbRefConverter , ValueConverter <Object > elementConverter ,
2431
+ EntityProjection <?, ?> projection ) {
2432
+ super (sourceConverter , customConversions , path ,
2433
+ (context , source , typeHint ) -> doReadOrProject (context , source , typeHint , projection ),
2434
+
2435
+ collectionConverter , mapConverter , dbRefConverter , elementConverter );
2436
+ this .returnedTypeDescriptor = projection ;
2437
+ }
2438
+
2439
+ @ Override
2440
+ public ConversionContext forProperty (String name ) {
2441
+
2442
+ EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
2443
+ if (property == null ) {
2444
+ return new DefaultConversionContext (sourceConverter , conversions , path ,
2445
+ MappingMongoConverter .this ::readDocument , collectionConverter , mapConverter , dbRefConverter ,
2446
+ elementConverter );
2447
+ }
2448
+
2449
+ return new ProjectingConversionContext (sourceConverter , conversions , path , collectionConverter , mapConverter ,
2450
+ dbRefConverter , elementConverter , property );
2451
+ }
2452
+
2453
+ @ Override
2454
+ public ConversionContext withPath (ObjectPath currentPath ) {
2455
+ return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
2456
+ mapConverter , dbRefConverter , elementConverter , returnedTypeDescriptor );
2457
+ }
2458
+ }
2459
+
2432
2460
private static class PropertyTranslatingPropertyAccessor <T > implements PersistentPropertyPathAccessor <T > {
2433
2461
2434
2462
private final PersistentPropertyAccessor <T > delegate ;
0 commit comments