1919import static org .mockito .Mockito .mock ;
2020import static org .mockito .Mockito .verify ;
2121import static org .springframework .data .domain .Sort .Direction .DESC ;
22+ import static org .springframework .data .mongodb .core .aggregation .Aggregation .newAggregation ;
2223import static org .springframework .data .mongodb .core .aggregation .Aggregation .project ;
2324import static org .springframework .data .mongodb .core .aggregation .Aggregation .sort ;
2425
2526import java .time .ZonedDateTime ;
2627import java .util .List ;
2728import java .util .Set ;
29+ import java .util .stream .Stream ;
2830
2931import org .assertj .core .api .Assertions ;
3032import org .bson .Document ;
3133import org .junit .jupiter .api .Test ;
34+ import org .junit .jupiter .params .ParameterizedTest ;
35+ import org .junit .jupiter .params .provider .Arguments ;
36+ import org .junit .jupiter .params .provider .MethodSource ;
3237import org .springframework .data .annotation .Id ;
3338import org .springframework .data .convert .ConverterBuilder ;
3439import org .springframework .data .convert .CustomConversions ;
3742import org .springframework .data .mongodb .core .convert .MappingMongoConverter ;
3843import org .springframework .data .mongodb .core .convert .NoOpDbRefResolver ;
3944import org .springframework .data .mongodb .core .convert .QueryMapper ;
45+ import org .springframework .data .mongodb .core .mapping .Field ;
4046import org .springframework .data .mongodb .core .query .Criteria ;
4147import org .springframework .data .mongodb .test .util .MongoTestMappingContext ;
4248
@@ -100,6 +106,37 @@ void appliesConversionToValuesUsedInAggregation() {
100106 .isInstanceOf (String .class );
101107 }
102108
109+ @ ParameterizedTest // GH-4722
110+ @ MethodSource ("studentAggregationContexts" )
111+ void mapsOperationThatDoesNotExposeDedicatedFieldsCorrectly (AggregationOperationContext aggregationContext ) {
112+
113+ var agg = newAggregation (Student .class , Aggregation .unwind ("grades" ), Aggregation .replaceRoot ("grades" ),
114+ Aggregation .project ("grades" ));
115+
116+ List <Document > mappedPipeline = AggregationOperationRenderer .toDocument (agg .getPipeline ().getOperations (),
117+ aggregationContext );
118+
119+ Assertions .assertThat (mappedPipeline ).last ().isEqualTo (Document .parse ("{\" $project\" : {\" grades\" : 1}}" ));
120+ }
121+
122+ private static Stream <Arguments > studentAggregationContexts () {
123+
124+ MongoTestMappingContext ctx = new MongoTestMappingContext (cfg -> {
125+ cfg .initialEntitySet (Student .class );
126+ });
127+
128+ MappingMongoConverter mongoConverter = new MappingMongoConverter (NoOpDbRefResolver .INSTANCE , ctx );
129+ mongoConverter .afterPropertiesSet ();
130+
131+ QueryMapper queryMapper = new QueryMapper (mongoConverter );
132+
133+ return Stream .of (
134+ Arguments
135+ .of (new TypeBasedAggregationOperationContext (Student .class , ctx , queryMapper , FieldLookupPolicy .strict ())),
136+ Arguments .of (
137+ new TypeBasedAggregationOperationContext (Student .class , ctx , queryMapper , FieldLookupPolicy .relaxed ())));
138+ }
139+
103140 record TestRecord (@ Id String field1 , String field2 , LayerOne layerOne ) {
104141 record LayerOne (List <LayerTwo > layerTwo ) {
105142 }
@@ -110,4 +147,17 @@ record LayerTwo(LayerThree layerThree) {
110147 record LayerThree (int fieldA , int fieldB ) {
111148 }
112149 }
150+
151+ static class Student {
152+
153+ @ Field ("mark" ) List <Grade > grades ;
154+
155+ }
156+
157+ static class Grade {
158+
159+ int points ;
160+ String grades ;
161+ }
162+
113163}
0 commit comments