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: #422
This commit is contained in:
Mark Paluch
2016-12-07 11:15:37 +01:00
committed by Christoph Strobl
parent 7b49b120e3
commit cab35759db
4 changed files with 183 additions and 0 deletions

View File

@@ -23,6 +23,7 @@ import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.CountOperation.CountOperationBuilder;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
@@ -396,6 +397,16 @@ public class Aggregation {
return new LookupOperation(from, localField, foreignField, as);
}
/**
* Creates a new {@link CountOperationBuilder}.
*
* @return never {@literal null}.
* @since 1.10
*/
public static CountOperationBuilder count() {
return new CountOperationBuilder();
}
/**
* Creates a new {@link Fields} instance for the given field names.
*

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $count}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#count()} instead of creating instances of this
* class directly.
*
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/count/#pipe._S_count
* @author Mark Paluch
* @since 1.10
*/
public class CountOperation implements FieldsExposingAggregationOperation {
private final String fieldName;
/**
* Creates a new {@link CountOperation} given the {@link fieldName} field name.
*
* @param asFieldName must not be {@literal null} or empty.
*/
public CountOperation(String fieldName) {
Assert.hasText(fieldName, "Field name must not be null or empty!");
this.fieldName = fieldName;
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$count", fieldName);
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
*/
@Override
public ExposedFields getFields() {
return ExposedFields.from(new ExposedField(fieldName, true));
}
/**
* Builder for {@link CountOperation}.
*
* @author Mark Paluch
*/
public static class CountOperationBuilder {
/**
* Returns the finally to be applied {@link CountOperation} with the given alias.
*
* @param fieldName must not be {@literal null} or empty.
* @return
*/
public CountOperation as(String fieldName) {
return new CountOperation(fieldName);
}
}
}

View File

@@ -100,6 +100,7 @@ public class AggregationTests {
private static final Version TWO_DOT_FOUR = new Version(2, 4);
private static final Version TWO_DOT_SIX = new Version(2, 6);
private static final Version THREE_DOT_TWO = new Version(3, 2);
private static final Version THREE_DOT_FOUR = new Version(3, 4);
private static boolean initialized = false;
@@ -1298,6 +1299,30 @@ public class AggregationTests {
assertThat(result.getMappedResults(), hasSize(2));
}
/**
* @see DATAMONGO-1549
*/
@Test
public void shouldApplyCountCorrectly() {
assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR));
mongoTemplate.save(new Reservation("0123", "42", 100));
mongoTemplate.save(new Reservation("0360", "43", 200));
mongoTemplate.save(new Reservation("0360", "44", 300));
Aggregation agg = newAggregation( //
count().as("documents"), //
project("documents") //
.andExpression("documents * 2").as("twice"));
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, Reservation.class, DBObject.class);
assertThat(result.getMappedResults(), hasSize(1));
DBObject dbObject = result.getMappedResults().get(0);
assertThat(dbObject, isBsonObject().containing("documents", 3).containing("twice", 6));
}
/**
* @see DATAMONGO-975
*/

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Unit tests for {@link CountOperation}.
*
* @author Mark Paluch
*/
public class CountOperationUnitTests {
/**
* @see DATAMONGO-1549
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsEmptyFieldName() {
new CountOperation("");
}
/**
* @see DATAMONGO-1549
*/
@Test
public void shouldRenderCorrectly() {
CountOperation countOperation = new CountOperation("field");
DBObject dbObject = countOperation.toDBObject(Aggregation.DEFAULT_CONTEXT);
assertThat(dbObject, is(JSON.parse("{$count : \"field\" }")));
}
/**
* @see DATAMONGO-1549
*/
@Test
public void countExposesFields() {
CountOperation countOperation = new CountOperation("field");
assertThat(countOperation.getFields().exposesNoFields(), is(false));
assertThat(countOperation.getFields().exposesSingleFieldOnly(), is(true));
assertThat(countOperation.getFields().getField("field"), notNullValue());
}
}