SpringPortal #4 - Querying MongoDB with Spring Boot

Querying data from MongoDB with Spring Boot - Part 1

Loading Sample data in MongoDB Atlas

A task of an API that allows us to interact with a database. In the earlier article, we discussed how to interact with MongoDB in the Spring framework by adding and querying the data by the ID.

But sometimes we'll require more than just a simple query with the ID. Spring allows us to define a query by writing a method in an interface that extends MongoRepository.

In this article, we'll go through the various ways to write these functions to convey our query to the Spring framework. Also, what to do when writing these function definitions is not enough.

We'll use sample_airbnb.listingsAndReviews collection which is available in the MongoDB sample database

Setting up the Application

  1. Create a new Spring Application

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
    
    @SpringBootApplication
    @EnableMongoRepositories
    public class HelloSpringMongdbApplication {
    
     public static void main(String[] args) {
         SpringApplication.run(HelloSpringMongdbApplication.class, args);
     }
    
    }
    
  2. Document Class

    import java.util.Date;
    
    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;
    import org.springframework.data.mongodb.core.mapping.Field;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Document("listingsAndReviews")
    public class AirBNBListingAndReview {
    
       @Id
       private String id;
    
       @Field("last_scraped")
       private Date lastScraped;
    
       @Field("listing_url")
       private String listingUrl;
    
       private String name;
    
       private String summary;
    
       private String space;
    
       private String description;
    
       @Field("neighborhood_overview")
       private String neighborhoodOverview;
    
       private String notes;
    
       private String transit;
    
       private String access;
    
       private String interaction;
    
       @Field("house_rules")
       private String houseRules;
    
       @Field("property_type")
       private String propertyType;
    
       private Double price;
    
       @Field("room_type")
       private String roomType;
    }
    
    1. @Document("listingsAndReviews") tells Spring the collection name from MongoDB to which our Document class is mapped.
    2. @Field("...") annotation tells Spring the field name from MongoDB collection to which our field from Document class is mapped.
    3. This is required if our field names from the class and MongoDB collection do not match.
  3. Repository Interface for the application

    @Repository
    public interface AirBNBListingAndReviewRepository extends MongoRepository<AirBNBListingAndReview, String> {
    }
    
    • This repository will give us the space to define our query to interact with MongoDB from our Spring application.

Querying from MongoRepository

  • Spring allows creating functions in MongoRepository which is similar to the following pattern:

    • findBy{fieldName1}{And}{fieldNameN}{OrderBy}{sortingField{Asc|Desc}}
  • This helps run simple queries in MongoRepository by defining them as function names.

  • But if the query becomes too complex (if the query uses several and & or clauses) then it is better to use an alternative method as the function names become hard to read and manage.

  • Spring provides @Query annotation which makes it easy to declare queries in the repository.

  • @Query allows writing native JSON-like queries from MongoDB in the Spring application.
  • This annotation is at the method level, so we can define a method under any name and annotate it with @Query to run our native queries.
    • To use function params in the @Query annotation, params in Spring queries are marked as ?0, ?1, etc. and Spring passes the corresponding from the function definition to the @Query.

MongoRepository Queries

In the following examples, each query is defined in two ways:

1. Defined in the function name
2. Defined with  ```@Query``` annotation.
  1. Find by 1 field

    // find by 1 field
    List<AirBNBListingAndReview> findByPropertyType(String propertyType, Pageable pageRequest);
    
    // find by 1 field - @Query
    @Query(value = "{ 'propertyType': ?0 }")
    List<AirBNBListingAndReview> findByPropertyTypeQuery(String propertyType, Pageable pageRequest);
    
  2. Find by 2 fields

    // find by 2 fields
    List<AirBNBListingAndReview> findByPropertyTypeAndRoomType(String propertyType, String roomType, Pageable pageRequest);
    
    // find by 2 fields - @Query
    @Query(value = "{ 'propertyType': ?0, 'roomType': ?1 }")
    List<AirBNBListingAndReview> findByPropertyTypeAndRoomTypeQuery(String propertyType, String roomType, Pageable pageRequest);
    
  3. Find by - not equal to 1 field

    // find by - not equal to 1 field
    List<AirBNBListingAndReview> findByPropertyTypeNot(String propertyType, Pageable pageRequest);
    
    // find by - not equal to 1 field - @Query
    @Query(value = "{ 'propertyType': { '$ne': ?0 } }")
    List<AirBNBListingAndReview> findByPropertyTypeNotQuery(String propertyType, Pageable pageRequest);
    
  4. Find by 1 field and sort by field[s]

    // find by 1 field and sorted by last_scraped - @Query
    @Query(value = "{ 'propertyType': { '$ne': ?0 } }", sort = "{ last_scraped: 1 }")
    List<AirBNBListingAndReview> findByPropertyTypeOrderByLastScrapedQuery(String propertyType, Pageable pageRequest);
    
    // find by 1 field and sorted by last_scraped in descending order
    List<AirBNBListingAndReview> findByPropertyTypeOrderByLastScrapedDesc(String propertyType, Pageable pageRequest);
    
    // find by 1 field and sorted by first_review in ascending order & last_scraped in descending order - @Query
    @Query(value = "{ 'propertyType': { '$ne': ?0 } }", sort = "{ first_review, last_scraped: -1 }")
    List<AirBNBListingAndReview> findByPropertyTypeOrderByLastScrapedDescQuery(String propertyType, Pageable pageRequest);
    
  5. Find by value which exists between two values

    // find by value between 2 field
    List<AirBNBListingAndReview> findByPriceBetween(double priceFrom, double priceTo, Pageable pageRequest);
    
    // find by value between 2 field - @Query
    @Query(value = "{ 'price': { '$gt': ?0, '$lt': ?1 } }")
    List<AirBNBListingAndReview> findByPriceBetweenQuery(double priceFrom, double priceTo, Pageable pageRequest);
    
  6. Find by Regular expression

    // find by name regex
    List<AirBNBListingAndReview> findByNameRegex(String firstname, Pageable pageRequest);
    
    // find by name regex - @Query
    @Query(value = "{ 'name': { '$regex': ?0, '$options': 'i' } }")  List<AirBNBListingAndReview>  
    findByNameRegexQuery(String firstname, Pageable pageRequest);
    

Pageable?

  • Pageable class allows us to handle a large subset of the dataset.
  • It helps in navigating the data in a paginated manner.
  • Page numbers start from 0 and each page size (number of records on one page) is user-defined.
  • A pageable request is created using PageRequest class.
  • PageRequest have 4 static methods to create a request for Pageable:
    1. PageRequest of(int page, int size)
    2. PageRequest of(int page, int size, Sort sort)
    3. PageRequest of(int page, int size, Direction direction, String... properties)
    4. PageRequest ofSize(int pageSize) - first (0-th) page with specified size.
  • Direction is an Enum with two values - ASC & DESC.

GitLab

Code Repository

Article-specific commit

Did you find this article valuable?

Support Naman Jain by becoming a sponsor. Any amount is appreciated!