Skip to content

Commit 0db36af

Browse files
Nikolai Bogdanovmp911de
Nikolai Bogdanov
authored andcommitted
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: spring-projects#361. CLA: 172720160413124705 (Nikolai Bogdanov)
1 parent ba8ece3 commit 0db36af

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* @author Mark Paluch
4646
* @author Alessio Fachechi
4747
* @author Christoph Strobl
48+
* @author Nikolay Bogdanov
4849
* @since 1.3
4950
*/
5051
public class Aggregation {
@@ -160,6 +161,13 @@ protected Aggregation(List<AggregationOperation> aggregationOperations, Aggregat
160161
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
161162
Assert.notNull(options, "AggregationOptions must not be null!");
162163

164+
//check $out is the last operation if exist
165+
for (int i = 0; i < aggregationOperations.size(); i++) {
166+
if (aggregationOperations.get(i) instanceof OutOperation && i != aggregationOperations.size() - 1) {
167+
throw new IllegalArgumentException("The $out operator must be the last stage in the pipeline.");
168+
}
169+
}
170+
163171
this.operations = aggregationOperations;
164172
this.options = options;
165173
}
@@ -318,6 +326,20 @@ public static MatchOperation match(Criteria criteria) {
318326
return new MatchOperation(criteria);
319327
}
320328

329+
/**
330+
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation
331+
* in the pipeline.
332+
*
333+
* @param outCollectionName collection name to export aggregation results. The {@link OutOperation} creates a new
334+
* collection in the current database if one does not already exist. The collection is
335+
* not visible until the aggregation completes. If the aggregation fails, MongoDB does
336+
* not create the collection. Must not be {@literal null}.
337+
* @return
338+
*/
339+
public static OutOperation out(String outCollectionName) {
340+
return new OutOperation(outCollectionName);
341+
}
342+
321343
/**
322344
* Creates a new {@link LookupOperation}.
323345
*
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 com.mongodb.BasicDBObject;
19+
import com.mongodb.DBObject;
20+
import org.springframework.util.Assert;
21+
22+
/**
23+
* Encapsulates the {@code $out}-operation.
24+
* <p>
25+
* We recommend to use the static factory method {@link Aggregation#out(String)} instead of creating instances of this
26+
* class directly.
27+
*
28+
* @see http://docs.mongodb.org/manual/reference/aggregation/out/
29+
* @author Nikolay Bogdanov
30+
*/
31+
public class OutOperation implements AggregationOperation {
32+
33+
private final String collectionName;
34+
35+
/**
36+
* @param outCollectionName Collection name to export the results. Must not be {@literal null}.
37+
*/
38+
public OutOperation(String outCollectionName) {
39+
Assert.notNull(outCollectionName, "Collection name must not be null!");
40+
this.collectionName = outCollectionName;
41+
}
42+
43+
/*
44+
* (non-Javadoc)
45+
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
46+
*/
47+
@Override
48+
public DBObject toDBObject(AggregationOperationContext context) {
49+
return new BasicDBObject("$out", collectionName);
50+
}
51+
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.springframework.data.mongodb.core.Venue;
5656
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
5757
import org.springframework.data.mongodb.core.index.GeospatialIndex;
58+
import org.springframework.data.mongodb.core.query.Criteria;
5859
import org.springframework.data.mongodb.core.query.NearQuery;
5960
import org.springframework.data.mongodb.core.query.Query;
6061
import org.springframework.data.mongodb.repository.Person;
@@ -78,6 +79,7 @@
7879
* @author Oliver Gierke
7980
* @author Christoph Strobl
8081
* @author Mark Paluch
82+
* @author Nikolay Bogdanov
8183
*/
8284
@RunWith(SpringJUnit4ClassRunner.class)
8385
@ContextConfiguration("classpath:infrastructure.xml")
@@ -1182,6 +1184,50 @@ public void shouldGroupByAndLookupPeopleCorectly() {
11821184
assertThat(firstItem, isBsonObject().containing("linkedPerson.[0].firstname", "u1"));
11831185
}
11841186

1187+
/**
1188+
* @see DATAMONGO-1418
1189+
*/
1190+
@Test
1191+
public void shouldCreateOutputCollection() {
1192+
1193+
assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX));
1194+
1195+
mongoTemplate.save(new Person("Anna", "Ivanova", 21, Person.Sex.FEMALE));
1196+
mongoTemplate.save(new Person("Pavel", "Sidorov", 36, Person.Sex.MALE));
1197+
mongoTemplate.save(new Person("Anastasia", "Volochkova", 29, Person.Sex.FEMALE));
1198+
mongoTemplate.save(new Person("Igor", "Stepanov", 31, Person.Sex.MALE));
1199+
mongoTemplate.save(new Person("Leoniv", "Yakubov", 55, Person.Sex.MALE));
1200+
1201+
String tempOutCollection = "personQueryTemp";
1202+
TypedAggregation<Person> agg = newAggregation(Person.class, //
1203+
group("sex").count().as("count"), //
1204+
sort(DESC, "count"), //
1205+
out(tempOutCollection));
1206+
1207+
AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, DBObject.class);
1208+
assertThat(results.getMappedResults(), is(empty()));
1209+
1210+
List<DBObject> list = mongoTemplate.findAll(DBObject.class, tempOutCollection);
1211+
1212+
assertThat(list, hasSize(2));
1213+
assertThat(list.get(0), isBsonObject().containing("_id", "MALE").containing("count", 3));
1214+
assertThat(list.get(1), isBsonObject().containing("_id", "FEMALE").containing("count", 2));
1215+
1216+
mongoTemplate.dropCollection(tempOutCollection);
1217+
}
1218+
1219+
/**
1220+
* @see DATAMONGO-1418
1221+
*/
1222+
@Test(expected = IllegalArgumentException.class)
1223+
public void outShouldOutBeTheLastOperation() {
1224+
1225+
newAggregation(match(new Criteria()), //
1226+
group("field1").count().as("totalCount"), //
1227+
out("collection1"), //
1228+
skip(100));
1229+
}
1230+
11851231
private void createUsersWithReferencedPersons() {
11861232

11871233
mongoTemplate.dropCollection(User.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.junit.Test;
19+
20+
/**
21+
* Unit tests for {@link OutOperation}.
22+
*
23+
* @author Nikolay Bogdanov
24+
*/
25+
public class OutOperationUnitTest {
26+
27+
@Test(expected = IllegalArgumentException.class)
28+
public void shouldCheckNPEInCreation() {
29+
new OutOperation(null);
30+
}
31+
}

0 commit comments

Comments
 (0)