Spring Boot + MongoDB Quick Guide

Godwin Pinto
5 min readMar 24, 2023

--

A cheat sheet on how to quickly query MongoDB with Spring Boot.

Recommended Readers: Aware of MongoDB concepts like collections, document store, id field, NoSQL and Spring Boot.

Firstly, if you do not want to go through the hassle of installing MongoDB, get a cloud free test version from www.mongodb.com (Free at the time of writing this article).

Initial Configuration

Step1: Add below configuration to your spring boot application.properties

spring.data.mongodb.uri=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<DB_IP:DB_PORT>/?retryWrites=true&w=majority
spring.data.mongodb.database=<DB_NAME>

In case of YAML, application.yml

spring:
data:
mongodb:
uri: mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<DB_IP:DB_PORT>/?retryWrites=true&w=majority
database: <DB_NAME>

Step 2: Create your Repository classes in the below style so as to auto injected database connection

@Repository
public class YourClassNameRepository {
//Spring boot based mongoDB template provided by spring
@Autowired
private MongoTemplate mongoTemplate;
}

You can view the Mongodb reference queries being fired to MongoDB with logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG

And Voila!!! You are ready with configurations.

Before we start one should note that we are using Document class as a generic class, package org.bson.Document, a LinkedHashMap meaning the order of fields(keys) is maintained and value can be retrieved by key.

In Production with heavy loads, Document class is not recommend. One should create custom POJO classes based on the input and output fields needed for your results, so that you save on memory.

One modern way of querying your collections is using MongoRepository with less overhead of code. More information available here: https://www.mongodb.com/compatibility/spring-boot. However, this article will cover the traditional method which will come to rescue on need basis.

Simple CRUD Operations:

/**
* Insert a document to MongoDB
*/
public void saveData(Document document) {
mongoTemplate.insert(document, "collection_name");
}

/**
* this query will return all fields including mongodb id field,
* if you do not write include id, it will written all fields except id
* if you include any other actual field from collection,
* then that field ONLY will be returned
*/
public List<Document> getDataWithInclude() {
Query query = new Query();
query.fields().include("_id");
return mongoTemplate.find(query, Document.class, "collection_name");
}

/**
* this query will return all fields excluding mongodb id field,
* and the field specified in excluded
*/
public List<Document> getDataWithExclude() {
Query query = new Query();
query.fields().exclude("some_field_name");
return mongoTemplate.find(query, Document.class, "collection_name");
}

/**
* update a mongoDB record by find and update method
*/
public void updateDocument(Document document) {
//fetch record by Mongo DB ID
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(document.getObjectId("_id")));
Update update = new Update();
update.set("field_to_be_updated", "updated value");
//update first record and stop searching for more records
mongoTemplate.updateFirst(query, update, "collection_name");
}

//Further you can check more methods on MongoDB template class for additioal usage
// E.x. mongoTemplate.dropCollection, mongoTemplate.remove, mongoTemplate.count, etc

/**
* Example of OR conditon
* WHERE field1='value1' OR field1='value2'
*/
public List<Document> getDataWithOr() {
Query query = new Query();
query.fields().include("project_id", "_id", "iid");
query.addCriteria(new Criteria().orOperator(
Criteria.where("field1").is("value1")
,Criteria.where("field1").is("value2")));
return mongoTemplate.find(query, Document.class, "collection_name");
}

/**
* Example of AND condition
* every "query.addCriteria" is like an AND operation
*/
public List<Document> getDataWithAnd() {
Query query = new Query();
//equals
query.addCriteria(Criteria.where("field1").is("value1"));
//greater than
query.addCriteria(Criteria.where("field2").gt("Java Date class"));
//is null
query.addCriteria(Criteria.where("field3").isNull());
//in keyword
query.addCriteria(
Criteria.where("field4").in(Arrays.asList(new String[] { "A", "B", "C", "D" }).stream()
.map(f -> f.toString()).toArray(Object[]::new)));
return mongoTemplate.find(query, Document.class, "collection_name");
}

Note: Above operations work only if you need to query just a single collection

Complex Queries

For Complex queries including joins, custom column addition, etc. which include multiple collections, you need to be aware of how MongoDB’s Aggregation Pipeline works (https://www.mongodb.com/docs/manual/core/aggregation-pipeline/).

Step 1: Create a list of aggregations you want to perform in the pipeline in an order and choose the base collection where aggregation will start from.

List<AggregationOperation> lstAggregationOperation = new ArrayList<>();

Step 2: Perform operation that should take place and attach to the above list. Examples below;

  1. MatchOperation: Filter results; more like WHERE clause in SQL
  2. ProjectionOperation: To shorten the list of fields you want to pass in the pipeline
  3. LookupOperation: To join two collections with a common unique field
  4. SortOperation: To sort the result
  5. GroupOperation: To generate grouped field value, like SUM, COUNT, etc
  6. AddFieldsOperation: To add a calculated field to your output
  7. ConditionalOperators: Comparing fields and adding custom value
  8. UnwindOperation: Used to deconstruct an array field in a document and create separate output documents for each item in the array

Note: Only one operation is performed at a time in the pipeline and the order of execution is decided by Ordered List lstAggregationOperation variable we defined above.

Lets see some code.

public List<Document> getAggregatedData() {

//base collection where conditions
MatchOperation matchFilterData = Aggregation
.match(new Criteria("field1").is("value"));

//join another collection to base collection
LookupOperation lookupIssues = LookupOperation.newLookup()
.from("collection_to_be_joined")
.localField("fieldname_from_base_collection")
.foreignField("fieldname_from_foreign_collection")
.as("custom_name_field");

//unwinding, refer unwinding description
UnwindOperation unwindIssuesNode = Aggregation.unwind("custom_name_field");

//create a custom condition
Cond condOperation = ConditionalOperators
.when(Criteria.where("$fieldx").is("$fieldy"))
.then("valueXYZ").otherwise("valuePQR");

//add a new field to the output with custom logic
AddFieldsOperation addOwnerField = Aggregation.addFields()
.addFieldWithValue("owner", condOperation)
.build();

//narrow results with selected fields only
ProjectionOperation projectColumns = Aggregation.project()
.and("owner").as("owner") //keep column name same
.and("time").as("new_time") //renaming this column
.andExclude("_id");

//perform group operations
GroupOperation groupfields = Aggregation
.group("field1", "field2")
.sum("field3")
.as("new_custom_field_name");

//last perform sort operation
SortOperation sortBy = Aggregation.sort(Direction.ASC, "field_name1")
.and(Direction.ASC, "field_name2");

/**
* create List<AggregationOperation> lstAggregationOperation = new ArrayList<>();
* at start and add at every step or another way is as below. Note order is important
*/
Aggregation aggregation = Aggregation.newAggregation(matchFilterData,
lookupIssues, unwindIssuesNode, addOwnerField, projectColumns,
groupfields,sortBy);

//Finally this is where the actual query is executed
List<Document> result = mongoTemplate
.aggregate(aggregation, "base_collection", Document.class).getMappedResults();
}
}

The field names are for representation purpose only.

Final Tips

If you download the MongoDB editor (MongoDB Compass) and play with the tabs (Documents, Aggregations) on your collections you will get a hang of the aggregation concepts even better.

Remember there are some minor format differences in the Java logged MongoDB queries in the log file and the actual MongoDB queries that you can execute in MongoDB Compass. i.e. when playing with datetime fields, etc. So don’t expect all queries to run from your java logs as-is to MongoDB when debugging with MongoDB Compass.

Hope that this cheat sheet will help you find majority of the Spring Boot + MongoDB query guide used in general project need scenarios, most of them at one place. Thanks for reading.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Godwin Pinto
Godwin Pinto

Written by Godwin Pinto

Principal engineer by profession | Business software application ideation and development enthusiast

No responses yet

Write a response