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.

  • Java 21 or later.

  • Gradle 8.8 or later.

  • 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 @Query annotation.

  • Update data by using the @Update annotation.

  • Review aggregation pipeline methods.

  • Implement pagination.

  • Use MongoTemplate for 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);

  1. 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:

  • 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.](https://docs.spring.io/spring-data/mongodb/reference/mongodb/repositories/query-methods.html#mongodb.repositories.queries.json-based

  1. 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.

  1. 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:

  • $match filters transactions by type.

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

  • $project displays 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.

  1. 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);

  1. 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'

  1. 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");
  }

}

```

  1. 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 ) {} } ```

  1. Perform bulk insert operations.

    The CustomerService class in the src/main/java/com/mongodb/domain/service directory 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 bulkOps method creates a bulk operation that inserts all documents in the list.

  2. Query documents with MongoTemplate.

    The CustomerService class 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 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.

  3. Perform aggregations with MongoTemplate.

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

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

    The CustomerService.java file 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.

  4. Test your queries.

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

    bash 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.

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.