Skip to content

Commit 51ece43

Browse files
Thomas Darimontodrotbohm
Thomas Darimont
authored andcommitted
DATAMONGO-759 - Improved rendering of GroupOperation.
GroupOperation gets the _id field now rendered as null if no group fields were added to the operation. Previously it was rendered as empty document (i.e. { }). While this was technically correct as well, we're now closer to what the MongoDB reference documentation describes. Original pull request: #73.
1 parent 51bab83 commit 51ece43

File tree

4 files changed

+86
-11
lines changed

4 files changed

+86
-11
lines changed

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* Value object to capture the fields exposed by an {@link AggregationOperation}.
3030
*
3131
* @author Oliver Gierke
32+
* @author Thomas Darimont
3233
* @since 1.3
3334
*/
3435
public class ExposedFields implements Iterable<ExposedField> {
@@ -151,13 +152,47 @@ public ExposedField getField(String name) {
151152
return null;
152153
}
153154

155+
/**
156+
* Returns whether the {@link ExposedFields} exposes no non-synthetic fields at all.
157+
*
158+
* @return
159+
*/
160+
boolean exposesNoNonSyntheticFields() {
161+
return originalFields.isEmpty();
162+
}
163+
164+
/**
165+
* Returns whether the {@link ExposedFields} exposes a single non-synthetic field only.
166+
*
167+
* @return
168+
*/
169+
boolean exposesSingleNonSyntheticFieldOnly() {
170+
return originalFields.size() == 1;
171+
}
172+
173+
/**
174+
* Returns whether the {@link ExposedFields} exposes no fields at all.
175+
*
176+
* @return
177+
*/
178+
boolean exposesNoFields() {
179+
return exposedFieldsCount() == 0;
180+
}
181+
154182
/**
155183
* Returns whether the {@link ExposedFields} exposes a single field only.
156184
*
157185
* @return
158186
*/
159-
public boolean exposesSingleFieldOnly() {
160-
return originalFields.size() + syntheticFields.size() == 1;
187+
boolean exposesSingleFieldOnly() {
188+
return exposedFieldsCount() == 1;
189+
}
190+
191+
/**
192+
* @return
193+
*/
194+
private int exposedFieldsCount() {
195+
return originalFields.size() + syntheticFields.size();
161196
}
162197

163198
/*

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@
4040
*/
4141
public class GroupOperation implements FieldsExposingAggregationOperation {
4242

43-
private final ExposedFields nonSynthecticFields;
43+
/**
44+
* Holds the non-synthetic fields which are the fields of the group-id structure.
45+
*/
46+
private final ExposedFields idFields;
47+
4448
private final List<Operation> operations;
4549

4650
/**
@@ -50,7 +54,7 @@ public class GroupOperation implements FieldsExposingAggregationOperation {
5054
*/
5155
public GroupOperation(Fields fields) {
5256

53-
this.nonSynthecticFields = ExposedFields.nonSynthetic(fields);
57+
this.idFields = ExposedFields.nonSynthetic(fields);
5458
this.operations = new ArrayList<Operation>();
5559
}
5660

@@ -74,7 +78,7 @@ private GroupOperation(GroupOperation groupOperation, List<Operation> nextOperat
7478
Assert.notNull(groupOperation, "GroupOperation must not be null!");
7579
Assert.notNull(nextOperations, "NextOperations must not be null!");
7680

77-
this.nonSynthecticFields = groupOperation.nonSynthecticFields;
81+
this.idFields = groupOperation.idFields;
7882
this.operations = new ArrayList<Operation>(nextOperations.size() + 1);
7983
this.operations.addAll(groupOperation.operations);
8084
this.operations.addAll(nextOperations);
@@ -261,7 +265,7 @@ private GroupOperationBuilder newBuilder(Keyword keyword, String reference, Obje
261265
@Override
262266
public ExposedFields getFields() {
263267

264-
ExposedFields fields = this.nonSynthecticFields.and(new ExposedField(Fields.UNDERSCORE_ID, true));
268+
ExposedFields fields = this.idFields.and(new ExposedField(Fields.UNDERSCORE_ID, true));
265269

266270
for (Operation operation : operations) {
267271
fields = fields.and(operation.asField());
@@ -279,16 +283,20 @@ public com.mongodb.DBObject toDBObject(AggregationOperationContext context) {
279283

280284
BasicDBObject operationObject = new BasicDBObject();
281285

282-
if (nonSynthecticFields.exposesSingleFieldOnly()) {
286+
if (idFields.exposesNoNonSyntheticFields()) {
287+
288+
operationObject.put(Fields.UNDERSCORE_ID, null);
289+
290+
} else if (idFields.exposesSingleNonSyntheticFieldOnly()) {
283291

284-
FieldReference reference = context.getReference(nonSynthecticFields.iterator().next());
292+
FieldReference reference = context.getReference(idFields.iterator().next());
285293
operationObject.put(Fields.UNDERSCORE_ID, reference.toString());
286294

287295
} else {
288296

289297
BasicDBObject inner = new BasicDBObject();
290298

291-
for (ExposedField field : nonSynthecticFields) {
299+
for (ExposedField field : idFields) {
292300
FieldReference reference = context.getReference(field);
293301
inner.put(field.getName(), reference.toString());
294302
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ public ProjectionOperation andInclude(Fields fields) {
155155
return new ProjectionOperation(this.projections, FieldProjection.from(fields, true));
156156
}
157157

158-
/*
158+
/*
159159
* (non-Javadoc)
160-
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContextSupport#getFields()
160+
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
161161
*/
162162
@Override
163163
public ExposedFields getFields() {

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,38 @@ public void rejectsNullFields() {
3737
new GroupOperation((Fields) null);
3838
}
3939

40+
/**
41+
* @see DATAMONGO-759
42+
*/
43+
@Test
44+
public void groupOperationWithNoGroupIdFieldsShouldGenerateNullAsGroupId() {
45+
46+
GroupOperation operation = new GroupOperation(Fields.from());
47+
ExposedFields fields = operation.getFields();
48+
DBObject groupClause = extractDbObjectFromGroupOperation(operation);
49+
50+
assertThat(fields.exposesSingleFieldOnly(), is(true));
51+
assertThat(fields.exposesNoFields(), is(false));
52+
assertThat(groupClause.get(UNDERSCORE_ID), is(nullValue()));
53+
}
54+
55+
/**
56+
* @see DATAMONGO-759
57+
*/
58+
@Test
59+
public void groupOperationWithNoGroupIdFieldsButAdditionalFieldsShouldGenerateNullAsGroupId() {
60+
61+
GroupOperation operation = new GroupOperation(Fields.from()).count().as("cnt").last("foo").as("foo");
62+
ExposedFields fields = operation.getFields();
63+
DBObject groupClause = extractDbObjectFromGroupOperation(operation);
64+
65+
assertThat(fields.exposesSingleFieldOnly(), is(false));
66+
assertThat(fields.exposesNoFields(), is(false));
67+
assertThat(groupClause.get(UNDERSCORE_ID), is(nullValue()));
68+
assertThat((BasicDBObject) groupClause.get("cnt"), is(new BasicDBObject("$sum", 1)));
69+
assertThat((BasicDBObject) groupClause.get("foo"), is(new BasicDBObject("$last", "$foo")));
70+
}
71+
4072
@Test
4173
public void createsGroupOperationWithSingleField() {
4274

0 commit comments

Comments
 (0)