Skip to content

Commit c7e1ca5

Browse files
christophstroblmp911de
authored andcommitted
DATAMONGO-2635 - Enforce aggregation pipeline mapping.
Avoid using the Aggregation.DEFAULT_CONTEXT which does not map contained values to the according MongoDB representation. We now use a relaxed aggregation context, preserving given field names, where possible. Original pull request: #890.
1 parent 6ab43c2 commit c7e1ca5

File tree

6 files changed

+44
-13
lines changed

6 files changed

+44
-13
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ AggregationOperationContext prepareAggregationContext(Aggregation aggregation,
7777
}
7878

7979
if (!(aggregation instanceof TypedAggregation)) {
80-
return Aggregation.DEFAULT_CONTEXT;
80+
return new RelaxedTypeBasedAggregationOperationContext(Object.class, mappingContext, queryMapper);
8181
}
8282

8383
Class<?> inputType = ((TypedAggregation) aggregation).getInputType();
@@ -98,7 +98,7 @@ AggregationOperationContext prepareAggregationContext(Aggregation aggregation,
9898
*/
9999
List<Document> createPipeline(Aggregation aggregation, AggregationOperationContext context) {
100100

101-
if (!ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
101+
if (ObjectUtils.nullSafeEquals(context, Aggregation.DEFAULT_CONTEXT)) {
102102
return aggregation.toPipeline(context);
103103
}
104104

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,10 +707,9 @@ Document getMappedShardKey(MongoPersistentEntity<?> entity) {
707707
*/
708708
List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
709709

710-
AggregationOperationContext context = domainType != null
711-
? new RelaxedTypeBasedAggregationOperationContext(domainType, mappingContext, queryMapper)
712-
: Aggregation.DEFAULT_CONTEXT;
710+
Class<?> type = domainType != null ? domainType : Object.class;
713711

712+
AggregationOperationContext context = new RelaxedTypeBasedAggregationOperationContext(type, mappingContext, queryMapper);
714713
return aggregationUtil.createPipeline((AggregationUpdate) update, context);
715714
}
716715

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
1919

20+
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
2021
import reactor.core.publisher.Flux;
2122
import reactor.core.publisher.Mono;
2223
import reactor.util.function.Tuple2;
@@ -2112,7 +2113,7 @@ List<Document> prepareFilter(ChangeStreamOptions options) {
21122113
AggregationOperationContext context = agg instanceof TypedAggregation
21132114
? new TypeBasedAggregationOperationContext(((TypedAggregation<?>) agg).getInputType(),
21142115
getConverter().getMappingContext(), queryMapper)
2115-
: Aggregation.DEFAULT_CONTEXT;
2116+
: new RelaxedTypeBasedAggregationOperationContext(Object.class, mappingContext, queryMapper);
21162117

21172118
return agg.toPipeline(new PrefixingDelegatingAggregationOperationContext(context, "fullDocument",
21182119
Arrays.asList("operationType", "fullDocument", "documentKey", "updateDescription", "ns")));

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222

2323
import org.bson.Document;
24+
import org.springframework.data.mapping.PersistentEntity;
2425
import org.springframework.data.mapping.PersistentPropertyPath;
2526
import org.springframework.data.mapping.context.MappingContext;
2627
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
@@ -29,6 +30,7 @@
2930
import org.springframework.data.mongodb.core.convert.QueryMapper;
3031
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
3132
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
33+
import org.springframework.data.util.Lazy;
3234
import org.springframework.lang.Nullable;
3335
import org.springframework.util.Assert;
3436

@@ -46,6 +48,7 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
4648
private final Class<?> type;
4749
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
4850
private final QueryMapper mapper;
51+
private final Lazy<MongoPersistentEntity<?>> entity;
4952

5053
/**
5154
* Creates a new {@link TypeBasedAggregationOperationContext} for the given type, {@link MappingContext} and
@@ -65,6 +68,7 @@ public TypeBasedAggregationOperationContext(Class<?> type,
6568
this.type = type;
6669
this.mappingContext = mappingContext;
6770
this.mapper = mapper;
71+
this.entity = Lazy.of(() -> mappingContext.getPersistentEntity(type));
6872
}
6973

7074
/*
@@ -151,10 +155,14 @@ public AggregationOperationContext continueOnMissingFieldReference(Class<?> type
151155

152156
protected FieldReference getReferenceFor(Field field) {
153157

158+
if(entity.getNullable() == null) {
159+
return new DirectFieldReference(new ExposedField(field, true));
160+
}
161+
154162
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext
155-
.getPersistentPropertyPath(field.getTarget(), type);
163+
.getPersistentPropertyPath(field.getTarget(), type);
156164
Field mappedField = field(field.getName(),
157-
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
165+
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
158166

159167
return new DirectFieldReference(new ExposedField(mappedField, true));
160168
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,8 @@ public Document toDocument(AggregationOperationContext context) {
142142

143143
private AggregationOperationContext computeContext(AggregationOperationContext source) {
144144

145-
if (domainType == null) {
146-
return Aggregation.DEFAULT_CONTEXT;
147-
}
148-
149145
if (source instanceof TypeBasedAggregationOperationContext) {
150-
return ((TypeBasedAggregationOperationContext) source).continueOnMissingFieldReference(domainType);
146+
return ((TypeBasedAggregationOperationContext) source).continueOnMissingFieldReference(domainType != null ? domainType : Object.class);
151147
}
152148

153149
if (source instanceof ExposedFieldsAggregationOperationContext) {

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,22 @@ void skipOutputDoesNotReadBackAggregationResults() {
19281928
assertThat(results.getRawResults()).isEmpty();
19291929
}
19301930

1931+
@Test // DATAMONGO-2635
1932+
void mapsEnumsInMatchClauseUsingInCriteriaCorrectly() {
1933+
1934+
WithEnum source = new WithEnum();
1935+
source.enumValue = MyEnum.TWO;
1936+
source.id = "id-1";
1937+
1938+
mongoTemplate.save(source);
1939+
1940+
Aggregation agg = newAggregation(match(where("enumValue").in(Collections.singletonList(MyEnum.TWO))));
1941+
1942+
AggregationResults<Document> results = mongoTemplate.aggregate(agg, mongoTemplate.getCollectionName(WithEnum.class),
1943+
Document.class);
1944+
assertThat(results.getMappedResults()).hasSize(1);
1945+
}
1946+
19311947
private void createUsersWithReferencedPersons() {
19321948

19331949
mongoTemplate.dropCollection(User.class);
@@ -2240,4 +2256,15 @@ static class ComplexId {
22402256
String p1;
22412257
String p2;
22422258
}
2259+
2260+
static enum MyEnum {
2261+
ONE, TWO
2262+
}
2263+
2264+
@lombok.Data
2265+
static class WithEnum {
2266+
2267+
@Id String id;
2268+
MyEnum enumValue;
2269+
}
22432270
}

0 commit comments

Comments
 (0)