Skip to content

Commit 84ca79d

Browse files
christophstroblmp911de
authored andcommitted
#523 - Add example for declarative MongoDB aggregations.
Original pull request: #526. closes: #523
1 parent a44f93a commit 84ca79d

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

mongodb/aggregation/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Spring Data MongoDB - Aggregations
2+
3+
This project contains usage samples for using MongoDB [Aggregations](https://docs.mongodb.com/manual/aggregation/)
4+
showing both the programmatic and declarative approach for integrating an Aggregation Pipeline into the repository lay.
5+
6+
## Programmatic
7+
8+
The programmatic approach uses a [custom repository](https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.custom-implementations) implementation along with the [Aggregation Framework](https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation).
9+
10+
```java
11+
class OrderRepositoryImpl implements OrderRepositoryCustom {
12+
13+
private MongoOperations operations;
14+
15+
// ...
16+
17+
@Override
18+
public Invoice getInvoiceFor(Order order) {
19+
20+
AggregationResults<Invoice> results = operations.aggregate(newAggregation(Order.class,
21+
match(where("id").is(order.getId())),
22+
unwind("items"),
23+
project("id", "customerId", "items")
24+
.andExpression("'$items.price' * '$items.quantity'").as("lineTotal"),
25+
group("id")
26+
.sum("lineTotal").as("netAmount")
27+
.addToSet("items").as("items"),
28+
project("id", "items", "netAmount")
29+
.and("orderId").previousOperation()
30+
.andExpression("netAmount * [0]", taxRate).as("taxAmount")
31+
.andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount")
32+
), Invoice.class);
33+
34+
return results.getUniqueMappedResult();
35+
}
36+
}
37+
```
38+
39+
## Declarative
40+
41+
The [declarative approach](https://docs.spring.io/spring-data/mongodb/docs/2.2.0.RC2/reference/html/#mongodb.repositories.queries.aggregation) allows to define an Aggregation Pipeline via the `@Aggregation` annotation.
42+
43+
```java
44+
public interface OrderRepository extends CrudRepository<Order, String>, OrderRepositoryCustom {
45+
46+
@Aggregation("{ $group : { _id : $customerId, total : { $sum : 1 } } }")
47+
List<OrdersPerCustomer> totalOrdersPerCustomer(Sort sort);
48+
49+
@Aggregation(pipeline = { "{ $match : { customerId : ?0 } }", "{ $count : total }" })
50+
Long totalOrdersForCustomer(String customerId);
51+
}
52+
```
53+

mongodb/aggregation/src/main/java/example/springdata/mongodb/aggregation/OrderRepository.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,24 @@
1515
*/
1616
package example.springdata.mongodb.aggregation;
1717

18+
import java.util.List;
19+
20+
import org.springframework.data.domain.Sort;
21+
import org.springframework.data.mongodb.repository.Aggregation;
1822
import org.springframework.data.repository.CrudRepository;
1923

2024
/**
2125
* A repository interface assembling CRUD functionality as well as the API to invoke the methods implemented manually.
2226
*
2327
* @author Thomas Darimont
2428
* @author Oliver Gierke
29+
* @author Christoph Strobl
2530
*/
2631
public interface OrderRepository extends CrudRepository<Order, String>, OrderRepositoryCustom {
2732

33+
@Aggregation("{ $group : { _id : $customerId, total : { $sum : 1 } } }")
34+
List<OrdersPerCustomer> totalOrdersPerCustomer(Sort sort);
35+
36+
@Aggregation(pipeline = { "{ $match : { customerId : ?0 } }", "{ $count : total }" })
37+
Long totalOrdersForCustomer(String customerId);
2838
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2019 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+
* https://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 example.springdata.mongodb.aggregation;
17+
18+
import lombok.Value;
19+
20+
import org.springframework.data.annotation.Id;
21+
22+
/**
23+
* @author Christoph Strobl
24+
*/
25+
@Value
26+
public class OrdersPerCustomer {
27+
28+
@Id //
29+
private String customerId;
30+
private Long total;
31+
}

mongodb/aggregation/src/test/java/example/springdata/mongodb/aggregation/OrderRepositoryIntegrationTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@
2121

2222
import java.util.Date;
2323

24+
import org.assertj.core.api.Assertions;
2425
import org.junit.Before;
2526
import org.junit.Test;
2627
import org.junit.runner.RunWith;
2728
import org.springframework.beans.factory.annotation.Autowired;
2829
import org.springframework.boot.test.context.SpringBootTest;
30+
import org.springframework.data.domain.Sort;
2931
import org.springframework.test.context.junit4.SpringRunner;
3032

3133
/**
3234
* Integration tests for {@link OrderRepository}.
3335
*
3436
* @author Thomas Darimont
3537
* @author Oliver Gierke
38+
* @author Christoph Strobl
3639
*/
3740
@RunWith(SpringRunner.class)
3841
@SpringBootTest
@@ -64,4 +67,33 @@ public void createsInvoiceViaAggregation() {
6467
assertThat(invoice.getTaxAmount(), is(closeTo(1.577, 000001)));
6568
assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001)));
6669
}
70+
71+
@Test
72+
public void declarativeAggregationWithSort() {
73+
74+
repository.save(new Order("c42", new Date()).addItem(product1));
75+
repository.save(new Order("c42", new Date()).addItem(product2));
76+
repository.save(new Order("c42", new Date()).addItem(product3));
77+
78+
repository.save(new Order("b12", new Date()).addItem(product1));
79+
repository.save(new Order("b12", new Date()).addItem(product1));
80+
81+
Assertions.assertThat(repository.totalOrdersPerCustomer(Sort.by(Sort.Order.desc("total")))) //
82+
.containsExactly( //
83+
new OrdersPerCustomer("c42", 3L), new OrdersPerCustomer("b12", 2L) //
84+
);
85+
}
86+
87+
@Test
88+
public void multiStageDeclarativeAggregation() {
89+
90+
repository.save(new Order("c42", new Date()).addItem(product1));
91+
repository.save(new Order("c42", new Date()).addItem(product2));
92+
repository.save(new Order("c42", new Date()).addItem(product3));
93+
94+
repository.save(new Order("b12", new Date()).addItem(product1));
95+
repository.save(new Order("b12", new Date()).addItem(product1));
96+
97+
Assertions.assertThat(repository.totalOrdersForCustomer("c42")).isEqualTo(3);
98+
}
6799
}

0 commit comments

Comments
 (0)