简体   繁体   中英

spring-data-mongo - optional query parameters?

I am using spring-data mongo with the JSON based query methods, and am unsure how to allow optional parameters in a search query.

For instance - say I had the following function

@Query("{ 'name' : {$regex : ?0, $options : 'i'}, 'createdDate' : {$gte : ?1, $lt : ?2 }} }")
List<MyItem> getItemsLikeNameByDateRange(String name, Date startDateRange, Date endDateRange);

-but I didnt want to apply the name regex match, or not apply a date range restriction if NULL values were passed to the method.

At the moment it looks like I might have to build the query using the mongoTemplate.

Are there any alternatives - or is using mongoTemplate the best option?

Thanks

To implement this in Boolean logic I do the following and the conversion to operations that are available in programming languages

:query != null -> field == :query
!(:query != null) || (field == :query)
(:query == null) || (field == :query)

In plain SQL, this is done as

where (null = :query) or (field = :query)

In MongoDB this is done through the $where

{ $where: '?0 == null || this.field == ?0' } 

We can speed this up a little by using Mongo Operations rather than building everything to the function at the expense of some readability. does not work unfortunately.

{ $or : [ { $where: '?0 == null' } , { field : ?0 } ] } 

So what you have is

@Query("{ $or : [ { $where: '?0 == null' } , { field : ?0 } ] }")
List<Something> findAll(String query, Pageable pageable);

This can be further expanded to handle arrays for in/all clauses

@Query("{ $or : [ { $where: '?0.length == 0' } , { field : { $in : ?0 } } ] }")
List<Something> findAll(String query, Pageable pageable);

I am using spring-data mongo with the JSON based query methods, and am unsure how to allow optional parameters in a search query.

For instance - say I had the following function

@Query("{ 'name' : {$regex : ?0, $options : 'i'}, 'createdDate' : {$gte : ?1, $lt : ?2 }} }")
List<MyItem> getItemsLikeNameByDateRange(String name, Date startDateRange, Date endDateRange);

-but I didnt want to apply the name regex match, or not apply a date range restriction if NULL values were passed to the method.

At the moment it looks like I might have to build the query using the mongoTemplate.

Are there any alternatives - or is using mongoTemplate the best option?

Thanks

In addition to Archimedes's answer:
If you need the count of matched documents, replace $where with $expr .

@Query("{ $or : [ { $expr: { $eq: ['?0', 'null'] } } , { field : ?0 } ] }")
Page<Something> findAll(String query, Pageable pageable);

Filtering out parts of the query depending on the input value is not directly supported. Nevertheless it can be done using @Query the $and and operator and a bit of SpEL .

interface Repo extends CrudRepository<MyItem,...> {

  @Query("""
         { $and : [ 
            ?#{T(com.example.Repo.QueryUtil).ifPresent([0], 'name')}, 
            ?#{T(com.example.Repo.QueryUtil).ifDateBetween([1], [2], 'startDate')},
            ... 
         ]}
         """)
  Page<MyItem> findDataSearchParams(String name, String city, ...)

  class QueryUtil {
    public static Document ifPresent(Object value, String property) {
      if(value == null) {
        return new Document("$expr", true); // always true
      }
      return new Document(property, value); // eq match
    }

    public static Document ifDateBetween(Object value1, Object value2, String property) {
      // ...
    }
  }

  // ...
}

Instead of addressing the target function via the T(...) Type expression writing an EvaluationContextExtension (see: json spel for details) allows to get rid of repeating the type name over and over again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM