Tutorial: Query MongoDB with Spring Data
For the complete MongoDB documentation index, see www.mongodb.com/docs/llms.txt
Tutorial: Query MongoDB with Spring Data
Overview
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](https://www.mongodb.com/docs/drivers/java/sync/current/integrations/spring-data-integration.md#std-label-java-spring-data tutorial.
Prerequisites
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](https://www.mongodb.com/docs/get-started.md#std-label-unified-get-started guide.
-
Your preferred IDE.
Data Model
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"
}
}
}
Tutorial
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
@Queryannotation. -
Update data by using the
@Updateannotation. -
Review aggregation pipeline methods.
-
Implement pagination.
-
Use
MongoTemplatefor flexible operations. -
Create derived queries by using MongoRepository.
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:
java
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:
properties
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:
text
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:
java
List<Transaction> findByAmountGreaterThan(double amount);
void deleteByTransactionType(String type);
- Create queries by using the @Query annotation.
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.
java
@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:
-
valuespecifies the filter criteria. -
fieldsdefines which fields to include in the results. -
sortorders the results by thecreatedAtfield value in descending order.
To learn more about the @Query annotation, see the [Spring Data MongoDB documentation.](https://docs.spring.io/spring-data/mongodb/reference/mongodb/repositories/query-methods.html#mongodb.repositories.queries.json-based
- Update data by using the @Update annotation.
The TransactionRepository interface includes a method that combines the @Query and @Update annotations to update a transaction status:
java
@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.
- Review aggregation pipeline methods.
The TransactionRepository interface includes a method that uses the @Aggregation annotation to calculate the total amount grouped by transaction type:
java
@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:
-
$matchfilters transactions by type. -
$groupgroups transactions by type and sums the amounts. -
$projectdisplays the total amount. -
Export error transactions to a new collection.
The TransactionRepository interface also includes a method that uses the $out stage to copy error transactions to a new collection:
java
@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.
- Review the text search annotation.
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.
```java 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:
java
@SearchAggregate
List<Transaction> search(String query, String path);
- Implement pagination.
The MongoRepository interface extends PagingAndSortingRepository, which provides pagination support. Open your TransactionService class and add the following method:
java
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:
java
@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:
bash
curl --location 'http://localhost:8080/transactions?page=0&sizePerPage=10&sortField=description&sortDirection=ASC'
- Configure MongoTemplate.
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:
```java 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");
}
}
```
- Review the Customer model.
The Customer.java file in the src/main/java/com/mongodb/model directory defines the following record:
```java package com.mongodb.model;
public record Customer( String name, String email, String accountNumber, String phone, Address address ) { public record Address( String street, String city ) {} } ```
-
Perform bulk insert operations.
The
CustomerServiceclass in thesrc/main/java/com/mongodb/domain/servicedirectory includes the following method that inserts multiple documents at once:```java public int bulkCustomerSample(List
customerList) { if (findAll().isEmpty()) { BulkWriteResult result = mongoOperations.bulkOps(BulkOperations.BulkMode.ORDERED, Customer.class) .insert(customerList) .execute(); return result.getInsertedCount(); } return 0;} ```
The
bulkOpsmethod creates a bulk operation that inserts all documents in the list. -
Query documents with MongoTemplate.
The
CustomerServiceclass includes a method that finds a customer by email:java 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
querymethod accepts aCriteriaobject that defines the filter. You can use methods such asgt(),lt(),and(), andor()to build complex queries.To learn more, see the Criteria class documentation.
-
Perform aggregations with MongoTemplate.
The
CustomersByCityclass in thesrc/main/java/com/mongodb/domain/modeldirectory contains the following record that holds aggregation results:java public record CustomersByCity( String id, int total ){}The
CustomerService.javafile includes a method that counts customers by city:```java public List
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.
-
Test your queries.
Run your application by running the
SpringShopApplicationclass in your IDE or by running the following commands from your terminal:bash export MONGODB_URI="<YOUR_CONNECTION_STRING>" ./gradlew bootRunThe 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.
Additional Resources
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.
