DATAMONGO-1585 - Expose synthetic fields in $project aggregation stage.
Field projections now expose their fields as synthetic simple fields. Projection aggregation stage redefines the available field set available for later aggregation stages entirely so projected fields are considered synthetic. A simple synthetic field has no target field which causes later aggregation stages to not pick up the underlying target but the exposed field name when rendering aggregation operations to Mongo documents. The change is motivated by a bug where previously an aggregation consisting of projection of an aliased field and sort caused the sort projection stage to render with the original field name instead of the aliased field. The sort did not apply any sorting since projection redefines the available field set entirely and the original field is no longer accessible. Original Pull Request: #433
This commit is contained in:
committed by
Christoph Strobl
parent
2c6bd6ecea
commit
1fe79f1194
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,6 +28,7 @@ import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.If
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
|
||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -1206,8 +1207,9 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
* @since 1.10
|
||||
*/
|
||||
public ProjectionOperationBuilder let(AggregationExpression valueExpression, String variableName,
|
||||
AggregationExpression in) {
|
||||
return this.operation.and(VariableOperators.Let.define(ExpressionVariable.newVariable(variableName).forExpression(valueExpression)).andApply(in));
|
||||
AggregationExpression in) {
|
||||
return this.operation.and(VariableOperators.Let
|
||||
.define(ExpressionVariable.newVariable(variableName).forExpression(valueExpression)).andApply(in));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1279,6 +1281,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class FieldProjection extends Projection {
|
||||
|
||||
@@ -1297,7 +1300,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private FieldProjection(Field field, Object value) {
|
||||
|
||||
super(field);
|
||||
super(new ExposedField(field.getName(), true));
|
||||
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,7 @@ import org.junit.rules.ExpectedException;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Aggregation}.
|
||||
@@ -279,6 +280,47 @@ public class AggregationUnitTests {
|
||||
assertThat(id.get("ruleType"), is((Object) "$rules.ruleType"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingBySyntheticAndExposedGroupFields() {
|
||||
|
||||
Document agg = newAggregation( //
|
||||
group("cmsParameterId").addToSet("title").as("titles"), //
|
||||
sort(Direction.ASC, "cmsParameterId", "titles") //
|
||||
).toDocument("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(notNullValue()));
|
||||
|
||||
Document sort = ((List<Document>) agg.get("pipeline")).get(1);
|
||||
|
||||
assertThat(getAsDocument(sort, "$sort"), is(Document.parse("{ \"_id.cmsParameterId\" : 1 , \"titles\" : 1}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportSortingByProjectedFields() {
|
||||
|
||||
Document agg = newAggregation( //
|
||||
project("cmsParameterId") //
|
||||
.and(SystemVariable.CURRENT + ".titles").as("titles") //
|
||||
.and("field").as("alias"), //
|
||||
sort(Direction.ASC, "cmsParameterId", "titles", "alias") //
|
||||
).toDocument("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(notNullValue()));
|
||||
|
||||
Document sort = ((List<Document>) agg.get("pipeline")).get(1);
|
||||
|
||||
assertThat(getAsDocument(sort, "$sort"),
|
||||
isBsonObject().containing("cmsParameterId", 1) //
|
||||
.containing("titles", 1) //
|
||||
.containing("alias", 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-924
|
||||
*/
|
||||
@@ -336,7 +378,7 @@ public class AggregationUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-954
|
||||
* @see DATAMONGO-954, DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void shouldSupportReferencingSystemVariables() {
|
||||
@@ -345,7 +387,7 @@ public class AggregationUnitTests {
|
||||
project("someKey") //
|
||||
.and("a").as("a1") //
|
||||
.and(Aggregation.CURRENT + ".a").as("a2") //
|
||||
, sort(Direction.DESC, "a") //
|
||||
, sort(Direction.DESC, "a1") //
|
||||
, group("someKey").first(Aggregation.ROOT).as("doc") //
|
||||
).toDocument("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@@ -353,7 +395,7 @@ public class AggregationUnitTests {
|
||||
assertThat(projection0, is((Document) new Document("someKey", 1).append("a1", "$a").append("a2", "$$CURRENT.a")));
|
||||
|
||||
Document sort = extractPipelineElement(agg, 1, "$sort");
|
||||
assertThat(sort, is((Document) new Document("a", -1)));
|
||||
assertThat(sort, is((Document) new Document("a1", -1)));
|
||||
|
||||
Document group = extractPipelineElement(agg, 2, "$group");
|
||||
assertThat(group, is((Document) new Document("_id", "$someKey").append("doc", new Document("$first", "$$ROOT"))));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,7 +103,8 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
public void aliasesIdFieldCorrectly() {
|
||||
|
||||
AggregationOperationContext context = getContext(Foo.class);
|
||||
assertThat(context.getReference("id"), is((FieldReference) new DirectFieldReference(new ExposedField(field("id", "_id"), true))));
|
||||
assertThat(context.getReference("id"),
|
||||
is((FieldReference) new DirectFieldReference(new ExposedField(field("id", "_id"), true))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,6 +174,23 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
assertThat(document.get("cursor"), is((Object) new org.bson.Document("foo", 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void rendersSortOfProjectedFieldCorrectly() {
|
||||
|
||||
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
|
||||
TypedAggregation<MeterData> agg = newAggregation(MeterData.class, project().and("counterName").as("counter"), //
|
||||
sort(Direction.ASC, "counter"));
|
||||
|
||||
Document dbo = agg.toDocument("meterData", context);
|
||||
Document sort = getPipelineElementFromAggregationAt(dbo, 1);
|
||||
|
||||
Document definition = (Document) sort.get("$sort");
|
||||
assertThat(definition.get("counter"), is(equalTo((Object) 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1133
|
||||
*/
|
||||
@@ -192,14 +210,15 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
* @see DATAMONGO-1326, DATAMONGO-1585
|
||||
*/
|
||||
@Test
|
||||
public void lookupShouldInheritFieldsFromInheritingAggregationOperation() {
|
||||
|
||||
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
|
||||
TypedAggregation<MeterData> agg = newAggregation(MeterData.class,
|
||||
lookup("OtherCollection", "resourceId", "otherId", "lookup"), sort(Direction.ASC, "resourceId"));
|
||||
lookup("OtherCollection", "resourceId", "otherId", "lookup"), //
|
||||
sort(Direction.ASC, "resourceId", "counterName"));
|
||||
|
||||
org.bson.Document document = agg.toDocument("meterData", context);
|
||||
org.bson.Document sort = getPipelineElementFromAggregationAt(document, 1);
|
||||
@@ -207,6 +226,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
org.bson.Document definition = (org.bson.Document) sort.get("$sort");
|
||||
|
||||
assertThat(definition.get("resourceId"), is(equalTo((Object) 1)));
|
||||
assertThat(definition.get("counter_name"), is(equalTo((Object) 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user