Skip to content

Commit 6827983

Browse files
Thomas Darimontodrotbohm
Thomas Darimont
authored andcommitted
DATAMONGO-788 - Projection operations do not render synthetic fields properly.
Introduced boolean isSynthetic() attribute to FieldReference to determine if the given reference points to a synthetic field, as this controls whether we render a simple include (1) or a concrete reference ($fieldName) E.g. isSynthetic() would be true for a field reference to _id. Original pull request: spring-projects#90.
1 parent 0e69021 commit 6827983

File tree

4 files changed

+83
-1
lines changed

4 files changed

+83
-1
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,13 @@ public FieldReference(ExposedField field) {
334334
this.field = field;
335335
}
336336

337+
/**
338+
* @return
339+
*/
340+
public boolean isSynthetic() {
341+
return field.synthetic;
342+
}
343+
337344
/**
338345
* Returns the raw, unqualified reference, i.e. the field reference without a {@literal $} prefix.
339346
*

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ private Object renderFieldValue(AggregationOperationContext context) {
628628
// check whether referenced field exists in the context
629629
FieldReference reference = context.getReference(field.getTarget());
630630

631-
if (field.getName().equals(field.getTarget())) {
631+
if (field.getName().equals(field.getTarget()) && reference.isSynthetic()) {
632632

633633
// render field as included
634634
return 1;

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

+49
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ private void cleanDb() {
106106
mongoTemplate.dropCollection(UserWithLikes.class);
107107
mongoTemplate.dropCollection(DATAMONGO753.class);
108108
mongoTemplate.dropCollection(Data.class);
109+
mongoTemplate.dropCollection(DATAMONGO788.class);
109110
}
110111

111112
/**
@@ -713,6 +714,36 @@ public void shouldPerformStringProjectionOperatorsCorrectly() throws ParseExcept
713714
assertThat((Integer) dbo.get("millisecond"), is(789));
714715
}
715716

717+
/**
718+
* @see DATAMONGO-788
719+
*/
720+
@Test
721+
public void referencesToGroupIdsShouldBeRenderedProperly() {
722+
723+
mongoTemplate.insert(new DATAMONGO788(1, 1));
724+
mongoTemplate.insert(new DATAMONGO788(1, 1));
725+
mongoTemplate.insert(new DATAMONGO788(1, 1));
726+
mongoTemplate.insert(new DATAMONGO788(2, 1));
727+
mongoTemplate.insert(new DATAMONGO788(2, 1));
728+
729+
AggregationOperation projectFirst = Aggregation.project("x", "y").and("xField").as("x").and("yField").as("y");
730+
AggregationOperation group = Aggregation.group("x", "y").count().as("xPerY");
731+
AggregationOperation project = Aggregation.project("xPerY", "x", "y").andExclude("_id");
732+
733+
TypedAggregation<DATAMONGO788> aggregation = Aggregation.newAggregation(DATAMONGO788.class, projectFirst, group,
734+
project);
735+
AggregationResults<DBObject> aggResults = mongoTemplate.aggregate(aggregation, DBObject.class);
736+
List<DBObject> items = aggResults.getMappedResults();
737+
738+
assertThat(items.size(), is(2));
739+
assertThat((Integer) items.get(0).get("xPerY"), is(2));
740+
assertThat((Integer) items.get(0).get("x"), is(2));
741+
assertThat((Integer) items.get(0).get("y"), is(1));
742+
assertThat((Integer) items.get(1).get("xPerY"), is(3));
743+
assertThat((Integer) items.get(1).get("x"), is(1));
744+
assertThat((Integer) items.get(1).get("y"), is(1));
745+
}
746+
716747
private void assertLikeStats(LikeStats like, String id, long count) {
717748

718749
assertThat(like, is(notNullValue()));
@@ -778,4 +809,22 @@ public PD(String pDch, int up) {
778809
this.up = up;
779810
}
780811
}
812+
813+
static class DATAMONGO788 {
814+
815+
int x;
816+
int y;
817+
int xField;
818+
int yField;
819+
820+
public DATAMONGO788() {}
821+
822+
public DATAMONGO788(int x, int y) {
823+
this.x = x;
824+
this.xField = x;
825+
this.y = y;
826+
this.yField = y;
827+
}
828+
}
829+
781830
}

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

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

18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
1820
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
1921
import static org.springframework.data.mongodb.core.query.Criteria.*;
2022

23+
import java.util.List;
24+
2125
import org.junit.Rule;
2226
import org.junit.Test;
2327
import org.junit.rules.ExpectedException;
28+
import org.springframework.data.mongodb.core.DBObjectTestUtils;
29+
30+
import com.mongodb.DBObject;
2431

2532
/**
2633
* Unit tests for {@link Aggregation}.
@@ -94,4 +101,23 @@ public void matchOperationShouldNotChangeAvailableFields() {
94101
project("a", "b") // b should still be available
95102
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
96103
}
104+
105+
/**
106+
* @see DATAMONGO-788
107+
*/
108+
@Test
109+
public void referencesToGroupIdsShouldBeRenderedAsReferences() {
110+
111+
DBObject agg = newAggregation( //
112+
project("a"), //
113+
group("a").count().as("aCnt"), //
114+
project("aCnt", "a") //
115+
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
116+
117+
@SuppressWarnings("unchecked")
118+
DBObject secondProjection = ((List<DBObject>) agg.get("pipeline")).get(2);
119+
DBObject fields = DBObjectTestUtils.getAsDBObject(secondProjection, "$project");
120+
assertThat((Integer) fields.get("aCnt"), is(1));
121+
assertThat((String) fields.get("a"), is("$_id.a"));
122+
}
97123
}

0 commit comments

Comments
 (0)