Skip to content

Commit 4c07235

Browse files
mp911dechristophstrobl
authored andcommitted
DATAMONGO-1549 - Add $count aggregation stage.
We now support the $count stage in aggregation pipelines. newAggregation( match(where("hotelCode").is("0360")), count().as("documents")); Original Pull Request: spring-projects#422
1 parent 5ebbf93 commit 4c07235

File tree

4 files changed

+179
-0
lines changed

4 files changed

+179
-0
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.bson.Document;
2525
import org.springframework.data.domain.Sort;
2626
import org.springframework.data.domain.Sort.Direction;
27+
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
2728
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
2829
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
2930
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
@@ -394,6 +395,16 @@ public static LookupOperation lookup(Field from, Field localField, Field foreign
394395
return new LookupOperation(from, localField, foreignField, as);
395396
}
396397

398+
/**
399+
* Creates a new {@link CountOperationBuilder}.
400+
*
401+
* @return never {@literal null}.
402+
* @since 1.10
403+
*/
404+
public static CountOperationBuilder count() {
405+
return new CountOperationBuilder();
406+
}
407+
397408
/**
398409
* Creates a new {@link Fields} instance for the given field names.
399410
*
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.aggregation;
17+
18+
import org.bson.Document;
19+
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
20+
import org.springframework.util.Assert;
21+
22+
/**
23+
* Encapsulates the aggregation framework {@code $count}-operation.
24+
* <p>
25+
* We recommend to use the static factory method {@link Aggregation#count()} instead of creating instances of this
26+
* class directly.
27+
*
28+
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/count/#pipe._S_count
29+
* @author Mark Paluch
30+
* @since 1.10
31+
*/
32+
public class CountOperation implements FieldsExposingAggregationOperation {
33+
34+
private final String fieldName;
35+
36+
/**
37+
* Creates a new {@link CountOperation} given the {@link fieldName} field name.
38+
*
39+
* @param asFieldName must not be {@literal null} or empty.
40+
*/
41+
public CountOperation(String fieldName) {
42+
43+
Assert.hasText(fieldName, "Field name must not be null or empty!");
44+
this.fieldName = fieldName;
45+
}
46+
47+
/* (non-Javadoc)
48+
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
49+
*/
50+
@Override
51+
public Document toDocument(AggregationOperationContext context) {
52+
return new Document("$count", fieldName);
53+
}
54+
55+
/* (non-Javadoc)
56+
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
57+
*/
58+
@Override
59+
public ExposedFields getFields() {
60+
return ExposedFields.from(new ExposedField(fieldName, true));
61+
}
62+
63+
/**
64+
* Builder for {@link CountOperation}.
65+
*
66+
* @author Mark Paluch
67+
*/
68+
public static class CountOperationBuilder {
69+
70+
/**
71+
* Returns the finally to be applied {@link CountOperation} with the given alias.
72+
*
73+
* @param fieldName must not be {@literal null} or empty.
74+
* @return
75+
*/
76+
public CountOperation as(String fieldName) {
77+
return new CountOperation(fieldName);
78+
}
79+
}
80+
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public class AggregationTests {
9595
private static final Version TWO_DOT_FOUR = new Version(2, 4);
9696
private static final Version TWO_DOT_SIX = new Version(2, 6);
9797
private static final Version THREE_DOT_TWO = new Version(3, 2);
98+
private static final Version THREE_DOT_FOUR = new Version(3, 4);
9899

99100
private static boolean initialized = false;
100101

@@ -1293,6 +1294,30 @@ public void shouldSupportReturningCurrentAggregationRootInReference() {
12931294
assertThat(result.getMappedResults(), hasSize(2));
12941295
}
12951296

1297+
/**
1298+
* @see DATAMONGO-1549
1299+
*/
1300+
@Test
1301+
public void shouldApplyCountCorrectly() {
1302+
1303+
assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR));
1304+
1305+
mongoTemplate.save(new Reservation("0123", "42", 100));
1306+
mongoTemplate.save(new Reservation("0360", "43", 200));
1307+
mongoTemplate.save(new Reservation("0360", "44", 300));
1308+
1309+
Aggregation agg = newAggregation( //
1310+
count().as("documents"), //
1311+
project("documents") //
1312+
.andExpression("documents * 2").as("twice"));
1313+
AggregationResults<Document> result = mongoTemplate.aggregate(agg, Reservation.class, Document.class);
1314+
1315+
assertThat(result.getMappedResults(), hasSize(1));
1316+
1317+
Document document = result.getMappedResults().get(0);
1318+
assertThat(document, isBsonObject().containing("documents", 3).containing("twice", 6));
1319+
}
1320+
12961321
/**
12971322
* @see DATAMONGO-975
12981323
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.aggregation;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
20+
21+
import org.bson.Document;
22+
import org.junit.Test;
23+
24+
/**
25+
* Unit tests for {@link CountOperation}.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
public class CountOperationUnitTests {
30+
31+
/**
32+
* @see DATAMONGO-1549
33+
*/
34+
@Test(expected = IllegalArgumentException.class)
35+
public void rejectsEmptyFieldName() {
36+
new CountOperation("");
37+
}
38+
39+
/**
40+
* @see DATAMONGO-1549
41+
*/
42+
@Test
43+
public void shouldRenderCorrectly() {
44+
45+
CountOperation countOperation = new CountOperation("field");
46+
Document dbObject = countOperation.toDocument(Aggregation.DEFAULT_CONTEXT);
47+
48+
assertThat(dbObject, is(Document.parse("{$count : \"field\" }")));
49+
}
50+
51+
/**
52+
* @see DATAMONGO-1549
53+
*/
54+
@Test
55+
public void countExposesFields() {
56+
57+
CountOperation countOperation = new CountOperation("field");
58+
59+
assertThat(countOperation.getFields().exposesNoFields(), is(false));
60+
assertThat(countOperation.getFields().exposesSingleFieldOnly(), is(true));
61+
assertThat(countOperation.getFields().getField("field"), notNullValue());
62+
}
63+
}

0 commit comments

Comments
 (0)