DATAMONGO-1418 - Add support for $out operator in aggregations.

We now support the $out operator via Aggregation.out(…) to store aggregation results in a collection. Using the $out operator returns an empty list in AggregationResults.

Original pull request: #361.
CLA: 172720160413124705 (Nikolai Bogdanov)
This commit is contained in:
Nikolai Bogdanov
2016-04-13 11:50:15 +03:00
committed by Mark Paluch
parent ba8ece334a
commit 0db36aff8f
4 changed files with 150 additions and 0 deletions

View File

@@ -45,6 +45,7 @@ import com.mongodb.DBObject;
* @author Mark Paluch
* @author Alessio Fachechi
* @author Christoph Strobl
* @author Nikolay Bogdanov
* @since 1.3
*/
public class Aggregation {
@@ -160,6 +161,13 @@ public class Aggregation {
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
Assert.notNull(options, "AggregationOptions must not be null!");
//check $out is the last operation if exist
for (int i = 0; i < aggregationOperations.size(); i++) {
if (aggregationOperations.get(i) instanceof OutOperation && i != aggregationOperations.size() - 1) {
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
}
}
this.operations = aggregationOperations;
this.options = options;
}
@@ -318,6 +326,20 @@ public class Aggregation {
return new MatchOperation(criteria);
}
/**
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation
* in the pipeline.
*
* @param outCollectionName collection name to export aggregation results. The {@link OutOperation} creates a new
* collection in the current database if one does not already exist. The collection is
* not visible until the aggregation completes. If the aggregation fails, MongoDB does
* not create the collection. Must not be {@literal null}.
* @return
*/
public static OutOperation out(String outCollectionName) {
return new OutOperation(outCollectionName);
}
/**
* Creates a new {@link LookupOperation}.
*

View File

@@ -0,0 +1,51 @@
/*
* 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 com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.springframework.util.Assert;
/**
* Encapsulates the {@code $out}-operation.
* <p>
* We recommend to use the static factory method {@link Aggregation#out(String)} instead of creating instances of this
* class directly.
*
* @see http://docs.mongodb.org/manual/reference/aggregation/out/
* @author Nikolay Bogdanov
*/
public class OutOperation implements AggregationOperation {
private final String collectionName;
/**
* @param outCollectionName Collection name to export the results. Must not be {@literal null}.
*/
public OutOperation(String outCollectionName) {
Assert.notNull(outCollectionName, "Collection name must not be null!");
this.collectionName = outCollectionName;
}
/*
* (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("$out", collectionName);
}
}

View File

@@ -55,6 +55,7 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.Person;
@@ -78,6 +79,7 @@ import com.mongodb.util.JSON;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @author Nikolay Bogdanov
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -1182,6 +1184,50 @@ public class AggregationTests {
assertThat(firstItem, isBsonObject().containing("linkedPerson.[0].firstname", "u1"));
}
/**
* @see DATAMONGO-1418
*/
@Test
public void shouldCreateOutputCollection() {
assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX));
mongoTemplate.save(new Person("Anna", "Ivanova", 21, Person.Sex.FEMALE));
mongoTemplate.save(new Person("Pavel", "Sidorov", 36, Person.Sex.MALE));
mongoTemplate.save(new Person("Anastasia", "Volochkova", 29, Person.Sex.FEMALE));
mongoTemplate.save(new Person("Igor", "Stepanov", 31, Person.Sex.MALE));
mongoTemplate.save(new Person("Leoniv", "Yakubov", 55, Person.Sex.MALE));
String tempOutCollection = "personQueryTemp";
TypedAggregation<Person> agg = newAggregation(Person.class, //
group("sex").count().as("count"), //
sort(DESC, "count"), //
out(tempOutCollection));
AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, DBObject.class);
assertThat(results.getMappedResults(), is(empty()));
List<DBObject> list = mongoTemplate.findAll(DBObject.class, tempOutCollection);
assertThat(list, hasSize(2));
assertThat(list.get(0), isBsonObject().containing("_id", "MALE").containing("count", 3));
assertThat(list.get(1), isBsonObject().containing("_id", "FEMALE").containing("count", 2));
mongoTemplate.dropCollection(tempOutCollection);
}
/**
* @see DATAMONGO-1418
*/
@Test(expected = IllegalArgumentException.class)
public void outShouldOutBeTheLastOperation() {
newAggregation(match(new Criteria()), //
group("field1").count().as("totalCount"), //
out("collection1"), //
skip(100));
}
private void createUsersWithReferencedPersons() {
mongoTemplate.dropCollection(User.class);

View File

@@ -0,0 +1,31 @@
/*
* 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.junit.Test;
/**
* Unit tests for {@link OutOperation}.
*
* @author Nikolay Bogdanov
*/
public class OutOperationUnitTest {
@Test(expected = IllegalArgumentException.class)
public void shouldCheckNPEInCreation() {
new OutOperation(null);
}
}