# MongoDB Advanced Aggregations With Spring Boot and Amazon Corretto

# Introduction

In this tutorial, we'll get into the understanding of aggregations and explore how to construct aggregation pipelines within your Spring Boot applications.

If you're new to Spring Boot, it's advisable to understand the fundamentals by acquainting yourself with the example template provided for performing Create, Read, Update, Delete (CRUD) operations with Spring Boot and MongoDB before delving into advanced aggregation concepts.

This tutorial serves as a complement to the example code template accessible in the GitHub repository. The code utilises sample data, which will be introduced later in the tutorial.

As indicated in the tutorial title, we'll compile the Java code using Amazon Corretto.

We recommend following the tutorial meticulously, progressing through each stage of the aggregation pipeline creation process.

Let's dive in!

# Prerequisites

This tutorial follows a few specifications mentioned below. Before you start practicing it, please make sure you have all the necessary downloads and uploads in your environment.

1. Amazon Corretto 21 JDK.
2. A free Atlas tier, also known as an M0 cluster.
3. Sample Data loaded in the cluster.
4. Spring Data Version 4.2.2.
5. MongoDB version 6.0.3.
6. MongoDB Java Driver version 4.11.1.

Let’s understand each of these in detail.

# Understanding and installing Corretto

Corretto comes with the ability to be a no-cost, multiplatform, production-ready open JDK. It also provides the ability to work across multiple distributions of Linux, Windows, and macOS.

You can read more about Amazon Corretto in Introduction to Amazon Corretto: A No-Cost Distribution of OpenJDK.

We will begin the tutorial with the first step of installing the Amazon Corretto 21 JDK and setting up your IDE with the correct JDK.

Step 1: Install Amazon Corretto 21 from the official website based on the operating system specifications.

Step 2: If you are on macOS, you will need to set the JAVA_HOME variable with the path for the Corretto. To do this, go to the system terminal and set the variable JAVA_HOME as:

```
export JAVA_HOME=/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk/Contents/Home 
```

Once the variable is set, you should check if the installation is done correctly using:

```
java --version
openjdk 21.0.2 2024-01-16 LTS
OpenJDK Runtime Environment Corretto-21.0.2.13.1 (build 21.0.2+13-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.2.13.1 (build 21.0.2+13-LTS, mixed mode, sharing)
```

For any other operating system, you will need to follow the steps mentioned in the official documentation from Java on how to set or change the PATH system variable and check if the version has been set.

Once the JDK is installed on the system, you can set up your IDE of choice to use Amazon Corretto to compile the code.

At this point, you have all the necessary environment components ready to kickstart your application.

# Creating the Spring Boot application

In this part of the tutorial, we're going to explore how to write aggregation queries for a Spring Boot application.

Aggregations in MongoDB are like super-powered tools for doing complex calculations on your data and getting meaningful results back. They work by applying different operations to your data and then giving you the results in a structured way.

But before we get into the details, let's first understand what an aggregation pipeline is and how it operates in MongoDB.

Think of an aggregation pipeline as a series of steps or stages that MongoDB follows to process your data. Each stage in the pipeline performs a specific task, like filtering or grouping your data in a certain way. And just like a real pipeline, data flows through each stage, with the output of one stage becoming the input for the next. This allows you to build up complex operations step by step to get the results you need.

By now, you should have the sample data loaded in your Atlas cluster. In this tutorial, we will be using the `sample_supplies.sales` collection for our aggregation queries.

The next step is cloning the repository from the link to test the aggregations. You can start by cloning the repository using the below command:

```
git clone https://github.com/mongodb-developer/mongodb-springboot-aggregations
```

Once the above step is complete, upon forking and cloning the repository to your local environment, it's essential to update the connection string in the designated placeholder within the `application.properties` file. This modification enables seamless connectivity to your cluster during project execution.

# README

After cloning the repository and changing the URI in the environment variables, you can try running the REST APIs in your Postman application.

All the extra information and commands you need to get this project going are in the README.md file which you can read on GitHub.

# Writing aggregation queries in Spring

The Aggregation Framework support in Spring Data MongoDB is based on the following key abstractions:

- Aggregation
- AggregationDefinition
- AggregationResults

The Aggregation Framework support in Spring Data MongoDB is based on the following key abstractions: Aggregation, AggregationDefinition, and AggregationResults.

While writing the aggregation queries, the first step is to generate the pipelines to perform the computations using the operations supported.

The documentation on spring.io explains each step clearly and gives simple examples to help you understand.

For the tutorial, we have the REST APIs defined in the SalesController.java class, and the methods have been mentioned in the SalesRepository.java class.

The first aggregation makes use of a simple $match operation to find all the documents where the `storeLocation` has been specified as the match value.

```
db.sales.aggregate({ $match: { "storeLocation": "London"}}])
```

And now when we convert the aggregation to the spring boot function, it would look like this:

```
@Override
public List matchOp(String matchValue) {
MatchOperation matchStage = match(new Criteria("storeLocation").is(matchValue));
Aggregation aggregation = newAggregation(matchStage);
AggregationResults results = mongoTemplate.aggregate(aggregation, "sales", SalesDTO.class);
return results.getMappedResults();
}
```
In this Spring Boot method, we utilise the `MatchOperation` to filter documents based on the specified criteria, which in this case is the `storeLocation` matching the provided value. The aggregation is then executed using the `mongoTemplate` to aggregate data from the `sales` collection into `SalesDTO` objects, returning the mapped results.

The REST API can be tested using the curl command in the terminal which shows all documents where `storeLocation` is `London`.

The next aggregation pipeline that we have defined with the rest API is to group all documents according to `storeLocation` and then calculate the total sales and the average satisfaction based on the `matchValue`. This stage makes use of the `GroupOperation` to perform the evaluation.

```
@Override
public List groupOp(String matchValue) {
MatchOperation matchStage = match(new Criteria("storeLocation").is(matchValue));
GroupOperation groupStage = group("storeLocation").count()
                                                          .as("totalSales")
                                                          .avg("customer.satisfaction")
                                                          .as("averageSatisfaction");
ProjectionOperation projectStage = project("storeLocation", "totalSales", "averageSatisfaction");
Aggregation aggregation = newAggregation(matchStage, groupStage, projectStage);
AggregationResults results = mongoTemplate.aggregate(aggregation, "sales", GroupDTO.class);
return results.getMappedResults();
}
```

The REST API call would look like below:

```bash
curl http://localhost:8080/api/sales/aggregation/groupStage/Denver | jq
```

![Total sales and the average satisfaction for storeLocation as "Denver"

The next REST API is an extension that will streamline the above aggregation. In this case, we will be calculating the total sales for each store location. Therefore, you do not need to specify the store location and directly get the value for all the locations.
```
@Override
public List TotalSales() {
GroupOperation groupStage = group("storeLocation").count().as("totalSales");
SkipOperation skipStage = skip(0);
LimitOperation limitStage = limit(10);
Aggregation aggregation = newAggregation(groupStage, skipStage, limitStage);
AggregationResults results = mongoTemplate.aggregate(aggregation, "sales", TotalSalesDTO.class);
return results.getMappedResults();
}
```

And the REST API calls look like below:

```bash
curl http://localhost:8080/api/sales/aggregation/TotalSales | jq
```

The next API makes use of $sort and $limit operations to calculate the top 5 items sold in each category.

```
@Override
public List findPopularItems() {
UnwindOperation unwindStage = unwind("items");
GroupOperation groupStage = group("$items.name").sum("items.quantity").as("totalQuantity");
SortOperation sortStage = sort(Sort.Direction.DESC, "totalQuantity");
LimitOperation limitStage = limit(5);
Aggregation aggregation = newAggregation(unwindStage,groupStage, sortStage, limitStage);
return mongoTemplate.aggregate(aggregation, "sales", PopularDTO.class).getMappedResults();
}
```

```bash
curl http://localhost:8080/api/sales/aggregation/PopularItem | jq
```

The last API mentioned makes use of the $bucket to create buckets and then calculates the count and total amount spent within each bucket.

```
@Override
public List findTotalSpend(){
ProjectionOperation projectStage = project()
                .and(ArrayOperators.Size.lengthOfArray("items")).as("numItems")
                .and(ArithmeticOperators.Multiply.valueOf("price")
                        .multiplyBy("quantity")).as("totalAmount");

BucketOperation bucketStage = bucket("numItems")
                .withBoundaries(0, 3, 6, 9)
                .withDefaultBucket("Other")
                .andOutputCount().as("count")
                .andOutput("totalAmount").sum().as("totalAmount");

Aggregation aggregation = newAggregation(projectStage, bucketStage);
return mongoTemplate.aggregate(aggregation, "sales", BucketsDTO.class).getMappedResults();
}
```

```bash
curl http://localhost:8080/api/sales/aggregation/buckets | jq
```

# Conclusion

This tutorial provides a comprehensive overview of aggregations in MongoDB and how to implement them in a Spring Boot application. We have learned about the significance of aggregation queries for performing complex calculations on data sets, leveraging MongoDB's aggregation pipeline to streamline this process effectively.

As you continue to experiment and apply these concepts in your applications, feel free to reach out on our MongoDB community forums. Remember to explore further resources in the MongoDB Developer Center and documentation to deepen your understanding and refine your skills in working with MongoDB aggregations.