Docs Menu
Docs Home
/ /

Tutorial: Query MongoDB with Spring Data

This tutorial shows you how to create advanced queries by using Spring Data to interact with MongoDB. You learn how to use the MongoRepository interface and MongoTemplate class to perform operations such as filtering, updating, and aggregating data.

Note

This tutorial requires a basic understanding of Spring Data and MongoDB. For an introduction to using Spring Data with MongoDB, see the Spring Data Framework Integration tutorial.

You must have the following items to complete this tutorial:

  • Source code from the spring-data-unlocked example repository. Follow the instructions in the repository's README to set up and run the project.

  • MongoDB Atlas account with a free cluster. To learn how to create an Atlas account and cluster, see the MongoDB Get Started guide.

  • Java 21 or later.

  • Gradle 8.8 or later.

  • Your preferred IDE.

This tutorial uses a transaction data model that has the following structure:

{
"id": "672182814338f60133ee26e1",
"transactionType": "Debit",
"amount": 888.0,
"currency": "USD",
"status": "In Progress",
"description": "Transfer to Ricardo",
"createdAt": "2024-10-09T14:00:00",
"accountDetails": {
"originator": {
"accountNumber": "2376543213",
"name": "Maria",
"bank": "Bank G"
},
"beneficiary": {
"accountNumber": "2234987651",
"name": "Ricardo Mello",
"bank": "Bank V"
}
}
}

The application in this tutorial is a REST API that manages customer records and transactions.

This tutorial shows you how to perform the following actions:

  • Create derived queries by using MongoRepository.

  • Create queries by using the @Query annotation.

  • Update data by using the @Update annotation.

  • Review aggregation pipeline methods.

  • Implement pagination.

  • Use MongoTemplate for flexible operations.

1

Navigate to the src/main/java/com/mongodb/resources directory in the spring-data-unlocked project and open the TransactionRepository interface. This interface includes the following method to find transactions by type:

List<Transaction> findByTransactionType(String type);

Spring Data uses the method name to create a query. The findBy prefix instructs Spring Data to create a query, and TransactionType specifies the field to filter on.

You can enable debug logging to see the generated queries by adding the following line to your application.properties file:

logging.level.org.springframework.data.mongodb=DEBUG

Tip

Ensure that the application.properties file is in your classpath so that Spring Boot can load the specified properties.

When you run the findByTransactionType query, the console displays output similar to the following:

2024-10-15T18:30:33.855-03:00 DEBUG 28992
[SpringShop] [nio-8080-exec-6] o.s.data.mongodb.core.MongoTemplate:
find using query: { "transactionType" : "Transfer"} fields: Document{{}} sort: { "transactionType" : "Transfer"} for class: class com.mongodb.Transaction in collection: transactions

The TransactionRepository interface also includes the following methods to find transactions by amount and delete transactions by type:

List<Transaction> findByAmountGreaterThan(double amount);
void deleteByTransactionType(String type);
2

The TransactionRepository defines a findByStatus() method that uses the @Query annotation to find transactions by their status field value. This method returns only specific fields and sorts the results.

@Query(
value= "{ 'status' : ?0 }",
fields="{ 'createdAt': 1, 'accountDetails' : 1, 'amount' : 1}",
sort = "{ createdAt: -1}"
)
List<Transaction> findByStatus(String status);

The parameters in the @Query annotation specify the following:

  • value specifies the filter criteria.

  • fields defines which fields to include in the results.

  • sort orders the results by the createdAt field value in descending order.

To learn more about the @Query annotation, see the Spring Data MongoDB documentation.

3

The TransactionRepository interface includes a method that combines the @Query and @Update annotations to update a transaction status:

@Query("{ '_id' : ?0 }")
@Update("{ '$set' : { 'status' : ?1 } }")
void updateStatus(String id, String status);

The @Query annotation finds the document by ID, and the @Update annotation modifies the status field.

To learn more, see the Spring Data MongoDB documentation on update operations.

4

The TransactionRepository interface includes a method that uses the @Aggregation annotation to calculate the total amount grouped by transaction type:

@Aggregation(pipeline = {
"{ '$match': { 'transactionType': ?0 } }",
"{ '$group': { '_id': '$transactionType', 'amount': { '$sum': '$amount' } } }",
"{ '$project': { 'amount': 1 } }"
})
List<Transaction> getTotalAmountByTransactionType(String transactionType);

This aggregation pipeline performs the following operations:

  • $match filters transactions by type.

  • $group groups transactions by type and sums the amounts.

  • $project displays the total amount.

5

The TransactionRepository interface also includes a method that uses the $out stage to copy error transactions to a new collection:

@Aggregation(pipeline = {
"{ '$match': { 'status': 'error' } }",
"{ '$project': { '_id': 1, 'amount': 1, 'status': 1, 'description': 1, 'createdAt': 1} }",
"{ '$out': 'error_transactions' }"
})
void exportErrorTransactions();

When you call this method, MongoDB creates a new collection called error_transactions and inserts all documents that have an error status.

Important

Review the $out operator documentation before you use this stage in production. The $out stage replaces the target collection if it already exists.

6

The SearchAggregate.java file in the src/main/java/com/mongodb/resources directory includes the following code that creates a custom annotation that performs a text search.

import org.springframework.data.mongodb.repository.Aggregation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Aggregation(pipeline = {
"{ '$search': { 'text': { 'query': ?0, 'path': ?1 } } }"
})
@interface SearchAggregate {
}

The TransactionRepository interface includes the following code that uses the annotation:

@SearchAggregate
List<Transaction> search(String query, String path);
7

The MongoRepository interface extends PagingAndSortingRepository, which provides pagination support. Open your TransactionService class and add the following method:

public Page<Transaction> findPageableTransactions(
Pageable pageable
) {
return transactionRepository.findAll(pageable);
}

The TransactionController class in the src/main/java/com/mongodb/application/web directory includes the following controller method that accepts pagination parameters:

@GetMapping("/pageable")
public PagedModel<Transaction> findAll(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "100") int sizePerPage,
@RequestParam(defaultValue = "ID") String sortField,
@RequestParam(defaultValue = "DESC") Sort.Direction sortDirection) {
Pageable pageable = PageRequest.of(page, sizePerPage, Sort.by(sortDirection, sortField));
return new PagedModel<>(transactionService.findPageableTransactions(pageable));
}

Call this endpoint by running the following command from your terminal while your application runs:

curl --location 'http://localhost:8080/transactions?page=0&sizePerPage=10&sortField=description&sortDirection=ASC'
8

The MongoTemplate class provides more flexibility than MongoRepository for performing operations on MongoDB. In the following steps, you learn how to use MongoTemplate to perform bulk operations, queries, and aggregations.

Navigate to the src/main/java/com/mongodb/resources directory to view the configuration class named MongoConfig.java, which sets up MongoTemplate:

package com.mongodb;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
@Configuration
public class MongoConfig {
@Bean
public MongoClient mongoClient() {
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString("<your connection string>"))
.build();
return MongoClients.create(settings);
}
@Bean
MongoOperations mongoTemplate(MongoClient mongoClient) {
return new MongoTemplate(mongoClient, "springshop");
}
}
9

The Customer.java file in the src/main/java/com/mongodb/model directory defines the following record:

package com.mongodb.model;
public record Customer(
String name,
String email,
String accountNumber,
String phone,
Address address
) {
public record Address(
String street,
String city
) {}
}
10

The CustomerService class in the src/main/java/com/mongodb/domain/service directory includes the following method that inserts multiple documents at once:

public int bulkCustomerSample(List<Customer> customerList) {
if (findAll().isEmpty()) {
BulkWriteResult result = mongoOperations.bulkOps(BulkOperations.BulkMode.ORDERED, Customer.class)
.insert(customerList)
.execute();
return result.getInsertedCount();
}
return 0;
}

The bulkOps method creates a bulk operation that inserts all documents in the list.

11

The CustomerService class includes a method that finds a customer by email:

public Customer findCustomerByEmail(String email) {
return mongoOperations.query(Customer.class)
.matching(query(where("email").is(email)))
.one()
.orElseThrow(() -> new RuntimeException("Customer not found with email: " + email));
}

The query method accepts a Criteria object that defines the filter. You can use methods such as gt(), lt(), and(), and or() to build complex queries.

To learn more, see the Criteria class documentation.

12

The CustomersByCity class in the src/main/java/com/mongodb/domain/model directory contains the following record that holds aggregation results:

public record CustomersByCity(
String id,
int total
){}

The CustomerService.java file includes a method that counts customers by city:

public List<CustomersByCity> totalCustomerByCity() {
TypedAggregation<Customer> aggregation = newAggregation(Customer.class,
group("address.city")
.count().as("total"),
Aggregation.sort(Sort.Direction.ASC, "_id"),
project(Fields.fields("total", "_id")));
AggregationResults<CustomersByCity> result = mongoOperations.aggregate(aggregation, CustomersByCity.class);
return result.getMappedResults();
}

This method groups customers by city and counts the number of customers in each city.

13

Run your application by running the SpringShopApplication class in your IDE or by running the following commands from your terminal:

export MONGODB_URI="<YOUR_CONNECTION_STRING>"
./gradlew bootRun

The application runs on port 8080. You can test your queries by sending requests to the endpoints defined in the previous steps.

For more information about testing the endpoints in this application, see the README in the spring-data-unlocked repository.

To learn more about Spring Data MongoDB, see the Spring Data MongoDB documentation.

To learn more about aggregation pipelines, see Aggregation in the MongoDB Server manual.

Back

Spring Data MongoDB