Skip to content

Commit aceec20

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: #361. CLA: 172720160413124705 (Nikolai Bogdanov)
1 parent 4f031b7 commit aceec20

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-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
@@ -43,6 +43,7 @@
4343
* @author Mark Paluch
4444
* @author Alessio Fachechi
4545
* @author Christoph Strobl
46+
* @author Nikolay Bogdanov
4647
* @since 1.3
4748
*/
4849
public class Aggregation {
@@ -158,6 +159,13 @@ protected Aggregation(List<AggregationOperation> aggregationOperations, Aggregat
158159
Assert.isTrue(!aggregationOperations.isEmpty(), "At least one AggregationOperation has to be provided");
159160
Assert.notNull(options, "AggregationOptions must not be null!");
160161

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

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

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@
5656
import org.springframework.data.mongodb.core.Venue;
5757
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
5858
import org.springframework.data.mongodb.core.index.GeospatialIndex;
59+
import org.springframework.data.mongodb.core.query.Criteria;
5960
import org.springframework.data.mongodb.core.query.NearQuery;
6061
import org.springframework.data.mongodb.core.query.Query;
6162
import org.springframework.data.mongodb.repository.Person;
6263
import org.springframework.data.util.Version;
6364
import org.springframework.test.context.ContextConfiguration;
6465
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
6566

67+
import com.mongodb.DBObject;
6668
import com.mongodb.MongoException;
6769
import com.mongodb.client.MongoCollection;
6870

@@ -75,6 +77,7 @@
7577
* @author Oliver Gierke
7678
* @author Christoph Strobl
7779
* @author Mark Paluch
80+
* @author Nikolay Bogdanov
7881
*/
7982
@RunWith(SpringJUnit4ClassRunner.class)
8083
@ContextConfiguration("classpath:infrastructure.xml")
@@ -1182,6 +1185,50 @@ public void shouldGroupByAndLookupPeopleCorectly() {
11821185
assertThat(firstItem, isBsonObject().containing("linkedPerson.[0].firstname", "u1"));
11831186
}
11841187

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

11871234
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)