Spring Data Reactive MongoDB — Aggregation Pipeline
In this article, I would like to show you the Mongo aggregation pipeline using Spring Data Reactive MongoRepository and Reactive MongoTemplate.
Project Setup:
Lets first create a simple spring boot project with these dependencies.
Sample Application:
Let’s consider an application in which freelancers register themselves with their skill sets. The information is stored in the mongoDB as shown below.
{
"name": "sam",
"age": 40,
"skills": [ "js", "react", "python"]
}
{
"name": "jack",
"age": 38,
"skills": [ "js", "angular", "postgres"]
}
{
"name": "james",
"age": 30,
"skills": [ "java", "reactor", "mongo"]
}
{
"name": "smith",
"age": 32,
"skills": [ "qa", "selenium"]
}
Users of the application would like to see for each skill set/technology, all the available freelancers. So the UI expectation is more or less like this.
{
"js":[
"sam",
"jack"
],
"java":[
"james"
],
"selenium":[
"smith"
]
...
...
}
Let’s see how to achieve this using Spring Data.
MongoDB Setup:
I use docker-compose to set up mongodb and insert above documents.
version: "3"
services:
mongo:
image: mongo
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
mongo-express:
image: mongo-express
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: password
Aggregation Pipeline:
To get the data in the format we want, we need to have following stages.
- unwind: Each freelancer has an array of skill sets. It is stored in the skills field. This stage reconstructs the document in a way that a person with 3 skills would be presented as 3 persons with 1 skill each.
{
"$unwind" : {
"path" : "$skills"
}
}
- group: Once you have unwinded (!), now we could group by skills field. For each skill, extract the names and push it to an array!
{
"$group":{
"_id":"$skills",
"names":{
"$push":"$name"
}
}
}
- project: Once we have grouped the records, then we need to look for the specific fields we are interested in. I exclude the _id and retrieve as skill, then I also need the names field.
{
"$project":{
"_id":0,
"skill":"$_id",
"names":1
}
}
Now let’s get this implemented.
Entity Class:
Our entity class is represented as shown below.
@Data
@Document
@ToString
public class Freelancer {
@Id
private String id;
private String name;
private int age;
private List<String> skills;
}
Projection Class:
Since we would like to present the data differently, Lets create a model class for that.
@Data
@ToString
public class SkilledPeople {
private String skill;
private List<String> names;
}
Approach-1:
Mongo Reactive Repository:
Once we have our stages ready, we include them as an array of stages as value for the @Aggregation. Do note that it returns the instances of projection class we have defined above.
Application Properties:
spring.data.mongodb.database=admin
spring.data.mongodb.username=admin
spring.data.mongodb.password=password
Demo:
At this point, everything seems to be ready! We can run and verify.
Output:
We get the list of skills and corresponding freelancers who can be hired.
SkilledPeople(skill=js, names=[sam, jack])
SkilledPeople(skill=angular, names=[jack])
SkilledPeople(skill=postgres, names=[jack])
SkilledPeople(skill=selenium, names=[smith])
SkilledPeople(skill=java, names=[james])
SkilledPeople(skill=mongo, names=[james])
SkilledPeople(skill=react, names=[sam])
SkilledPeople(skill=python, names=[sam])
SkilledPeople(skill=reactor, names=[james])
SkilledPeople(skill=qa, names=[smith])
Approach-2:
Reactive Mongo Template + Aggregation Operation:
Spring Data provides the support for the Mongo aggregation framework. We can create all the stages as shown here and get it executed via Mongo template.
Demo:
We would get the same output, If we modify the runner as shown below.
Summary:
We were able to successfully demonstrate the mongo aggregation pipeline using both Spring Reactive Mongo Repository and Mongo Template.
The source code is available here.
Happy learning 🙂