Skip to content

Commit f5b7a38

Browse files
christophstroblmp911de
authored andcommitted
Configure relaxed/strict field lookup when creating AggregationOperationContext.
Closes #4714 Original pull request: #4720
1 parent 17d8a42 commit f5b7a38

File tree

7 files changed

+68
-16
lines changed

7 files changed

+68
-16
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ static List<Document> toDocument(List<AggregationOperation> operations, Aggregat
5050
List<Document> operationDocuments = new ArrayList<Document>(operations.size());
5151

5252
AggregationOperationContext contextToUse = rootContext;
53+
boolean relaxed = rootContext instanceof RelaxedTypeBasedAggregationOperationContext;
5354

5455
for (AggregationOperation operation : operations) {
5556

@@ -60,12 +61,13 @@ static List<Document> toDocument(List<AggregationOperation> operations, Aggregat
6061
ExposedFields fields = exposedFieldsOperation.getFields();
6162

6263
if (operation instanceof InheritsFieldsAggregationOperation || exposedFieldsOperation.inheritsFields()) {
63-
contextToUse = new InheritingExposedFieldsAggregationOperationContext(fields, contextToUse);
64+
contextToUse = new InheritingExposedFieldsAggregationOperationContext(fields, contextToUse, relaxed);
6465
} else {
6566
contextToUse = fields.exposesNoFields() ? DEFAULT_CONTEXT
66-
: new ExposedFieldsAggregationOperationContext(fields, contextToUse);
67+
: new ExposedFieldsAggregationOperationContext(fields, contextToUse, relaxed);
6768
}
6869
}
70+
6971
}
7072

7173
return operationDocuments;

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ private Document toFilter(ExposedFields exposedFields, AggregationOperationConte
688688

689689
Document filterExpression = new Document();
690690
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
691-
exposedFields, context);
691+
exposedFields, context, false);
692692

693693
filterExpression.putAll(context.getMappedObject(new Document("input", getMappedInput(context))));
694694
filterExpression.put("as", as.getTarget());

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected DocumentEnhancingOperation(Map<Object, Object> source) {
5050
public Document toDocument(AggregationOperationContext context) {
5151

5252
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
53-
exposedFields, context);
53+
exposedFields, context, false);
5454

5555
if (valueMap.size() == 1) {
5656
return context.getMappedObject(

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
3737

3838
private final ExposedFields exposedFields;
3939
private final AggregationOperationContext rootContext;
40+
private final boolean relaxedFieldLookup;
4041

4142
/**
4243
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
@@ -46,13 +47,14 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
4647
* @param rootContext must not be {@literal null}.
4748
*/
4849
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields,
49-
AggregationOperationContext rootContext) {
50+
AggregationOperationContext rootContext, boolean relaxedFieldLookup) {
5051

5152
Assert.notNull(exposedFields, "ExposedFields must not be null");
5253
Assert.notNull(rootContext, "RootContext must not be null");
5354

5455
this.exposedFields = exposedFields;
5556
this.rootContext = rootContext;
57+
this.relaxedFieldLookup = relaxedFieldLookup;
5658
}
5759

5860
@Override
@@ -87,7 +89,7 @@ public Fields getFields(Class<?> type) {
8789
* @param name must not be {@literal null}.
8890
* @return
8991
*/
90-
private FieldReference getReference(@Nullable Field field, String name) {
92+
protected FieldReference getReference(@Nullable Field field, String name) {
9193

9294
Assert.notNull(name, "Name must not be null");
9395

@@ -96,12 +98,10 @@ private FieldReference getReference(@Nullable Field field, String name) {
9698
return exposedField;
9799
}
98100

99-
if (rootContext instanceof RelaxedTypeBasedAggregationOperationContext) {
100-
101+
if(relaxedFieldLookup) {
101102
if (field != null) {
102103
return new DirectFieldReference(new ExposedField(field, true));
103104
}
104-
105105
return new DirectFieldReference(new ExposedField(name, true));
106106
}
107107

@@ -156,4 +156,12 @@ AggregationOperationContext getRootContext() {
156156
public CodecRegistry getCodecRegistry() {
157157
return getRootContext().getCodecRegistry();
158158
}
159+
160+
@Override
161+
public AggregationOperationContext continueOnMissingFieldReference() {
162+
if(relaxedFieldLookup) {
163+
return this;
164+
}
165+
return new ExposedFieldsAggregationOperationContext(exposedFields, rootContext, true);
166+
}
159167
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ class InheritingExposedFieldsAggregationOperationContext extends ExposedFieldsAg
3838
* @param previousContext must not be {@literal null}.
3939
*/
4040
public InheritingExposedFieldsAggregationOperationContext(ExposedFields exposedFields,
41-
AggregationOperationContext previousContext) {
41+
AggregationOperationContext previousContext, boolean continueOnMissingFieldReference) {
4242

43-
super(exposedFields, previousContext);
43+
super(exposedFields, previousContext, continueOnMissingFieldReference);
4444

4545
this.previousContext = previousContext;
4646
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ private Document toMap(ExposedFields exposedFields, AggregationOperationContext
171171

172172
Document map = new Document();
173173
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
174-
exposedFields, context);
174+
exposedFields, context, false);
175175

176176
Document input;
177177
if (sourceArray instanceof Field field) {
@@ -308,15 +308,16 @@ private Document toLet(ExposedFields exposedFields, AggregationOperationContext
308308

309309
Document letExpression = new Document();
310310
Document mappedVars = new Document();
311-
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
312-
exposedFields, context);
313311

314312
for (ExpressionVariable var : this.vars) {
315313
mappedVars.putAll(getMappedVariable(var, context));
316314
}
317315

318316
letExpression.put("vars", mappedVars);
319317
if (expression != null) {
318+
319+
InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext(
320+
exposedFields, context, false);
320321
letExpression.put("in", getMappedIn(operationContext));
321322
}
322323

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRendererUnitTests.java

+43-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,26 @@
1515
*/
1616
package org.springframework.data.mongodb.core.aggregation;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.Mockito.*;
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.Mockito.eq;
20+
import static org.mockito.Mockito.mock;
21+
import static org.mockito.Mockito.verify;
22+
import static org.mockito.Mockito.when;
23+
import static org.springframework.data.domain.Sort.Direction.DESC;
24+
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
25+
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
2026

2127
import java.util.List;
2228

2329
import org.assertj.core.api.InstanceOfAssertFactories;
2430
import org.junit.jupiter.api.Test;
2531
import org.mockito.ArgumentCaptor;
32+
import org.springframework.data.annotation.Id;
2633
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
34+
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
35+
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
36+
import org.springframework.data.mongodb.core.convert.QueryMapper;
37+
import org.springframework.data.mongodb.test.util.MongoTestMappingContext;
2738

2839
/**
2940
* @author Christoph Strobl
@@ -115,4 +126,34 @@ void inheritingFieldsExposingAggregationOperationForcesNewContextForNextStageKee
115126
.extracting("previousContext").isSameAs(captor.getAllValues().get(1));
116127
}
117128

129+
130+
131+
record TestRecord(@Id String field1, String field2, LayerOne layerOne) {
132+
record LayerOne(List<LayerTwo> layerTwo) {
133+
}
134+
135+
record LayerTwo(LayerThree layerThree) {
136+
}
137+
138+
record LayerThree(int fieldA, int fieldB)
139+
{}
140+
}
141+
142+
@Test
143+
void xxx() {
144+
145+
MongoTestMappingContext ctx = new MongoTestMappingContext(cfg -> {
146+
cfg.initialEntitySet(TestRecord.class);
147+
});
148+
149+
MappingMongoConverter mongoConverter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, ctx);
150+
151+
Aggregation agg = Aggregation.newAggregation(
152+
Aggregation.unwind("layerOne.layerTwo"),
153+
project().and("layerOne.layerTwo.layerThree").as("layerOne.layerThree"),
154+
sort(DESC, "layerOne.layerThree.fieldA")
155+
);
156+
157+
AggregationOperationRenderer.toDocument(agg.getPipeline().getOperations(), new RelaxedTypeBasedAggregationOperationContext(TestRecord.class, ctx, new QueryMapper(mongoConverter)));
158+
}
118159
}

0 commit comments

Comments
 (0)