4747import org .jspecify .annotations .Nullable ;
4848import org .reactivestreams .Publisher ;
4949import org .reactivestreams .Subscriber ;
50-
5150import org .springframework .beans .BeansException ;
5251import org .springframework .context .ApplicationContext ;
5352import org .springframework .context .ApplicationContextAware ;
114113import org .springframework .data .mongodb .core .mapreduce .MapReduceOptions ;
115114import org .springframework .data .mongodb .core .query .BasicQuery ;
116115import org .springframework .data .mongodb .core .query .Collation ;
116+ import org .springframework .data .mongodb .core .query .Criteria ;
117117import org .springframework .data .mongodb .core .query .Meta ;
118118import org .springframework .data .mongodb .core .query .NearQuery ;
119119import org .springframework .data .mongodb .core .query .Query ;
@@ -1137,9 +1137,8 @@ public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndMo
11371137 return findAndModify (query , update , options , entityClass , getCollectionName (entityClass ));
11381138 }
11391139
1140- @ Override
1141- public <T > Mono <T > findAndModify (Query query , UpdateDefinition update , FindAndModifyOptions options ,
1142- Class <T > entityClass , String collectionName ) {
1140+ public <S , T > Mono <T > findAndModify (Query query , UpdateDefinition update , FindAndModifyOptions options ,
1141+ Class <S > entityClass , String collectionName , QueryResultConverter <? super S , ? extends T > resultConverter ) {
11431142
11441143 Assert .notNull (options , "Options must not be null " );
11451144 Assert .notNull (entityClass , "Entity class must not be null" );
@@ -1156,13 +1155,27 @@ public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndMo
11561155 }
11571156
11581157 return doFindAndModify (collectionName , ReactiveCollectionPreparerDelegate .of (query ), query .getQueryObject (),
1159- query .getFieldsObject (), getMappedSortObject (query , entityClass ), entityClass , update , optionsToUse );
1158+ query .getFieldsObject (), getMappedSortObject (query , entityClass ), entityClass , update , optionsToUse ,
1159+ resultConverter );
1160+ }
1161+
1162+ @ Override
1163+ public <T > Mono <T > findAndModify (Query query , UpdateDefinition update , FindAndModifyOptions options ,
1164+ Class <T > entityClass , String collectionName ) {
1165+ return findAndModify (query , update , options , entityClass , collectionName , QueryResultConverter .entity ());
11601166 }
11611167
11621168 @ Override
1163- @ SuppressWarnings ("NullAway" )
11641169 public <S , T > Mono <T > findAndReplace (Query query , S replacement , FindAndReplaceOptions options , Class <S > entityType ,
11651170 String collectionName , Class <T > resultType ) {
1171+ return findAndReplace (query , replacement , options , entityType , collectionName , resultType ,
1172+ QueryResultConverter .entity ());
1173+ }
1174+
1175+ @ SuppressWarnings ("NullAway" )
1176+ public <S , T , R > Mono <R > findAndReplace (Query query , S replacement , FindAndReplaceOptions options ,
1177+ Class <S > entityType , String collectionName , Class <T > resultType ,
1178+ QueryResultConverter <? super T , ? extends R > resultConverter ) {
11661179
11671180 Assert .notNull (query , "Query must not be null" );
11681181 Assert .notNull (replacement , "Replacement must not be null" );
@@ -1199,9 +1212,9 @@ public <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceO
11991212 mapped .getCollection ()));
12001213 }).flatMap (it -> {
12011214
1202- Mono <T > afterFindAndReplace = doFindAndReplace (it .getCollection (), collectionPreparer , mappedQuery ,
1215+ Mono <R > afterFindAndReplace = doFindAndReplace (it .getCollection (), collectionPreparer , mappedQuery ,
12031216 mappedFields , mappedSort , queryContext .getCollation (entityType ).orElse (null ), entityType , it .getTarget (),
1204- options , projection );
1217+ options , projection , resultConverter );
12051218 return afterFindAndReplace .flatMap (saved -> {
12061219 maybeEmitEvent (new AfterSaveEvent <>(saved , it .getTarget (), it .getCollection ()));
12071220 return maybeCallAfterSave (saved , it .getTarget (), it .getCollection ());
@@ -2280,6 +2293,43 @@ protected <T> Flux<T> doFindAndDelete(String collectionName, Query query, Class<
22802293 .flatMapSequential (deleteResult -> Flux .fromIterable (list )));
22812294 }
22822295
2296+ <S , T > Flux <T > doFindAndDelete (String collectionName , Query query , Class <S > entityClass ,
2297+ QueryResultConverter <? super S , ? extends T > resultConverter ) {
2298+
2299+ List <Object > ids = new ArrayList <>();
2300+ ProjectingReadCallback readCallback = new ProjectingReadCallback (getConverter (),
2301+ EntityProjection .nonProjecting (entityClass ), collectionName );
2302+
2303+ QueryResultConverterCallback <S , T > callback = new QueryResultConverterCallback <>(resultConverter , readCallback ) {
2304+
2305+ @ Override
2306+ public Mono <T > doWith (Document object ) {
2307+ ids .add (object .get ("_id" ));
2308+ return super .doWith (object );
2309+ }
2310+ };
2311+
2312+ Flux <T > flux = doFind (collectionName , ReactiveCollectionPreparerDelegate .of (query ), query .getQueryObject (),
2313+ query .getFieldsObject (), entityClass ,
2314+ new QueryFindPublisherPreparer (query , query .getSortObject (), query .getLimit (), query .getSkip (), entityClass ),
2315+ callback );
2316+
2317+ return Flux .from (flux ).collectList ().filter (it -> !it .isEmpty ()).flatMapMany (list -> {
2318+
2319+ Criteria [] criterias = ids .stream () //
2320+ .map (it -> Criteria .where ("_id" ).is (it )) //
2321+ .toArray (Criteria []::new );
2322+
2323+ Query removeQuery = new Query (criterias .length == 1 ? criterias [0 ] : new Criteria ().orOperator (criterias ));
2324+ if (query .hasReadPreference ()) {
2325+ removeQuery .withReadPreference (query .getReadPreference ());
2326+ }
2327+
2328+ return Flux .from (remove (removeQuery , entityClass , collectionName ))
2329+ .flatMapSequential (deleteResult -> Flux .fromIterable (list ));
2330+ });
2331+ }
2332+
22832333 /**
22842334 * Create the specified collection using the provided options
22852335 *
@@ -2487,9 +2537,10 @@ protected <T> Mono<T> doFindAndRemove(String collectionName,
24872537 new ReadDocumentCallback <>(this .mongoConverter , entityClass , collectionName ), collectionName );
24882538 }
24892539
2490- protected < T > Mono <T > doFindAndModify (String collectionName ,
2540+ < S , T > Mono <T > doFindAndModify (String collectionName ,
24912541 CollectionPreparer <MongoCollection <Document >> collectionPreparer , Document query , Document fields ,
2492- @ Nullable Document sort , Class <T > entityClass , UpdateDefinition update , FindAndModifyOptions options ) {
2542+ @ Nullable Document sort , Class <S > entityClass , UpdateDefinition update , FindAndModifyOptions options ,
2543+ QueryResultConverter <? super S , ? extends T > resultConverter ) {
24932544
24942545 MongoPersistentEntity <?> entity = mappingContext .getPersistentEntity (entityClass );
24952546 UpdateContext updateContext = queryOperations .updateSingleContext (update , query , false );
@@ -2508,10 +2559,13 @@ protected <T> Mono<T> doFindAndModify(String collectionName,
25082559 serializeToJsonSafely (mappedUpdate ), collectionName ));
25092560 }
25102561
2562+ EntityProjection <S , ?> projection = EntityProjection .nonProjecting (entityClass );
2563+ DocumentCallback <T > callback = getResultReader (projection , collectionName , resultConverter );
2564+
25112565 return executeFindOneInternal (
25122566 new FindAndModifyCallback (collectionPreparer , mappedQuery , fields , sort , mappedUpdate ,
25132567 update .getArrayFilters ().stream ().map (ArrayFilter ::asDocument ).collect (Collectors .toList ()), options ),
2514- new ReadDocumentCallback <>( this . mongoConverter , entityClass , collectionName ) , collectionName );
2568+ callback , collectionName );
25152569 });
25162570 }
25172571
@@ -2540,7 +2594,7 @@ protected <T> Mono<T> doFindAndReplace(String collectionName,
25402594 EntityProjection <T , ?> projection = operations .introspectProjection (resultType , entityType );
25412595
25422596 return doFindAndReplace (collectionName , collectionPreparer , mappedQuery , mappedFields , mappedSort , collation ,
2543- entityType , replacement , options , projection );
2597+ entityType , replacement , options , projection , QueryResultConverter . entity () );
25442598 }
25452599
25462600 /**
@@ -2560,10 +2614,11 @@ protected <T> Mono<T> doFindAndReplace(String collectionName,
25602614 * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
25612615 * @since 3.4
25622616 */
2563- private <T > Mono <T > doFindAndReplace (String collectionName ,
2617+ private <S , T > Mono <T > doFindAndReplace (String collectionName ,
25642618 CollectionPreparer <MongoCollection <Document >> collectionPreparer , Document mappedQuery , Document mappedFields ,
25652619 Document mappedSort , com .mongodb .client .model .Collation collation , Class <?> entityType , Document replacement ,
2566- FindAndReplaceOptions options , EntityProjection <T , ?> projection ) {
2620+ FindAndReplaceOptions options , EntityProjection <S , ?> projection ,
2621+ QueryResultConverter <? super S , ? extends T > resultConverter ) {
25672622
25682623 return Mono .defer (() -> {
25692624
@@ -2575,9 +2630,10 @@ private <T> Mono<T> doFindAndReplace(String collectionName,
25752630 serializeToJsonSafely (replacement ), collectionName ));
25762631 }
25772632
2633+ DocumentCallback <T > resultReader = getResultReader (projection , collectionName , resultConverter );
2634+
25782635 return executeFindOneInternal (new FindAndReplaceCallback (collectionPreparer , mappedQuery , mappedFields ,
2579- mappedSort , replacement , collation , options ),
2580- new ProjectingReadCallback <>(this .mongoConverter , projection , collectionName ), collectionName );
2636+ mappedSort , replacement , collation , options ), resultReader , collectionName );
25812637
25822638 });
25832639 }
@@ -3124,7 +3180,7 @@ interface ReactiveCollectionQueryCallback<T> extends ReactiveCollectionCallback<
31243180 FindPublisher <T > doInCollection (MongoCollection <Document > collection ) throws MongoException , DataAccessException ;
31253181 }
31263182
3127- static final class QueryResultConverterCallback <T , R > implements DocumentCallback <R > {
3183+ static class QueryResultConverterCallback <T , R > implements DocumentCallback <R > {
31283184
31293185 private final QueryResultConverter <? super T , ? extends R > converter ;
31303186 private final DocumentCallback <T > delegate ;
0 commit comments