1919import static org .springframework .data .domain .Sort .Direction .*;
2020import static org .springframework .data .mongodb .core .aggregation .Aggregation .*;
2121
22+ import java .time .ZonedDateTime ;
2223import java .util .List ;
24+ import java .util .Set ;
25+ import java .util .stream .Stream ;
2326
27+ import org .assertj .core .api .Assertions ;
28+ import org .bson .Document ;
2429import org .junit .jupiter .api .Test ;
30+ import org .junit .jupiter .params .ParameterizedTest ;
31+ import org .junit .jupiter .params .provider .Arguments ;
32+ import org .junit .jupiter .params .provider .MethodSource ;
2533
2634import org .springframework .data .annotation .Id ;
35+ import org .springframework .data .convert .ConverterBuilder ;
36+ import org .springframework .data .convert .CustomConversions ;
37+ import org .springframework .data .convert .CustomConversions .StoreConversions ;
38+ import org .springframework .data .domain .Sort .Direction ;
2739import org .springframework .data .mongodb .core .convert .MappingMongoConverter ;
2840import org .springframework .data .mongodb .core .convert .NoOpDbRefResolver ;
2941import org .springframework .data .mongodb .core .convert .QueryMapper ;
42+ import org .springframework .data .mongodb .core .mapping .Field ;
43+ import org .springframework .data .mongodb .core .query .Criteria ;
3044import org .springframework .data .mongodb .test .util .MongoTestMappingContext ;
3145
3246/**
@@ -47,32 +61,100 @@ void nonFieldsExposingAggregationOperationContinuesWithSameContextForNextStage()
4761 verify (stage2 ).toPipelineStages (eq (rootContext ));
4862 }
4963
64+ @ Test
65+ void contextShouldCarryOnRelaxedFieldMapping () {
66+
67+ MongoTestMappingContext ctx = new MongoTestMappingContext (cfg -> {
68+ cfg .initialEntitySet (TestRecord .class );
69+ });
70+
71+ MappingMongoConverter mongoConverter = new MappingMongoConverter (NoOpDbRefResolver .INSTANCE , ctx );
72+
73+ Aggregation agg = Aggregation .newAggregation (Aggregation .unwind ("layerOne.layerTwo" ),
74+ project ().and ("layerOne.layerTwo.layerThree" ).as ("layerOne.layerThree" ),
75+ sort (DESC , "layerOne.layerThree.fieldA" ));
76+
77+ AggregationOperationRenderer .toDocument (agg .getPipeline ().getOperations (),
78+ new RelaxedTypeBasedAggregationOperationContext (TestRecord .class , ctx , new QueryMapper (mongoConverter )));
79+ }
80+
81+ @ Test // GH-4722
82+ void appliesConversionToValuesUsedInAggregation () {
83+
84+ MongoTestMappingContext ctx = new MongoTestMappingContext (cfg -> {
85+ cfg .initialEntitySet (TestRecord .class );
86+ });
87+
88+ MappingMongoConverter mongoConverter = new MappingMongoConverter (NoOpDbRefResolver .INSTANCE , ctx );
89+ mongoConverter .setCustomConversions (new CustomConversions (StoreConversions .NONE ,
90+ Set .copyOf (ConverterBuilder .writing (ZonedDateTime .class , String .class , ZonedDateTime ::toString )
91+ .andReading (it -> ZonedDateTime .parse (it )).getConverters ())));
92+ mongoConverter .afterPropertiesSet ();
93+
94+ var agg = Aggregation .newAggregation (Aggregation .sort (Direction .DESC , "version" ),
95+ Aggregation .group ("entityId" ).first (Aggregation .ROOT ).as ("value" ), Aggregation .replaceRoot ("value" ),
96+ Aggregation .match (Criteria .where ("createdDate" ).lt (ZonedDateTime .now ())) // here is the problem
97+ );
98+
99+ List <Document > document = AggregationOperationRenderer .toDocument (agg .getPipeline ().getOperations (),
100+ new RelaxedTypeBasedAggregationOperationContext (TestRecord .class , ctx , new QueryMapper (mongoConverter )));
101+ Assertions .assertThat (document ).last ()
102+ .extracting (it -> it .getEmbedded (List .of ("$match" , "createdDate" , "$lt" ), Object .class ))
103+ .isInstanceOf (String .class );
104+ }
105+
106+ @ ParameterizedTest // GH-4722
107+ @ MethodSource ("studentAggregationContexts" )
108+ void mapsOperationThatDoesNotExposeDedicatedFieldsCorrectly (AggregationOperationContext aggregationContext ) {
109+
110+ var agg = newAggregation (Student .class , Aggregation .unwind ("grades" ), Aggregation .replaceRoot ("grades" ),
111+ Aggregation .project ("grades" ));
112+
113+ List <Document > mappedPipeline = AggregationOperationRenderer .toDocument (agg .getPipeline ().getOperations (),
114+ aggregationContext );
115+
116+ Assertions .assertThat (mappedPipeline ).last ().isEqualTo (Document .parse ("{\" $project\" : {\" grades\" : 1}}" ));
117+ }
118+
119+ private static Stream <Arguments > studentAggregationContexts () {
120+
121+ MongoTestMappingContext ctx = new MongoTestMappingContext (cfg -> {
122+ cfg .initialEntitySet (Student .class );
123+ });
124+
125+ MappingMongoConverter mongoConverter = new MappingMongoConverter (NoOpDbRefResolver .INSTANCE , ctx );
126+ mongoConverter .afterPropertiesSet ();
127+
128+ QueryMapper queryMapper = new QueryMapper (mongoConverter );
129+
130+ return Stream .of (
131+ Arguments
132+ .of (new TypeBasedAggregationOperationContext (Student .class , ctx , queryMapper , FieldLookupPolicy .strict ())),
133+ Arguments .of (
134+ new TypeBasedAggregationOperationContext (Student .class , ctx , queryMapper , FieldLookupPolicy .relaxed ())));
135+ }
136+
50137 record TestRecord (@ Id String field1 , String field2 , LayerOne layerOne ) {
51138 record LayerOne (List <LayerTwo > layerTwo ) {
52139 }
53140
54141 record LayerTwo (LayerThree layerThree ) {
55142 }
56143
57- record LayerThree (int fieldA , int fieldB )
58- { }
144+ record LayerThree (int fieldA , int fieldB ) {
145+ }
59146 }
60147
61- @ Test
62- void xxx () {
148+ static class Student {
63149
64- MongoTestMappingContext ctx = new MongoTestMappingContext (cfg -> {
65- cfg .initialEntitySet (TestRecord .class );
66- });
150+ @ Field ("mark" ) List <Grade > grades ;
67151
68- MappingMongoConverter mongoConverter = new MappingMongoConverter ( NoOpDbRefResolver . INSTANCE , ctx );
152+ }
69153
70- Aggregation agg = Aggregation .newAggregation (
71- Aggregation .unwind ("layerOne.layerTwo" ),
72- project ().and ("layerOne.layerTwo.layerThree" ).as ("layerOne.layerThree" ),
73- sort (DESC , "layerOne.layerThree.fieldA" )
74- );
154+ static class Grade {
75155
76- AggregationOperationRenderer .toDocument (agg .getPipeline ().getOperations (), new RelaxedTypeBasedAggregationOperationContext (TestRecord .class , ctx , new QueryMapper (mongoConverter )));
156+ int points ;
157+ String grades ;
77158 }
159+
78160}
0 commit comments