I'm using the Spring-data-mongodb repository framework in my Spring Boot project. I have an API where users can specify 1+ query parameters when searching for an object, and I would like to convert those query parameters to search criteria using the @Query annotation.
Since the number of search fields would not be consistent as users can specify a varying number of query parameters, I cannot hardcode fieldnames like the official documentation examples show. I tried putting together some SpEl strings to do this "dynamic exclusion" of criteria within the @Query annotation, but Spring is escaping the strings I'm returning so I'm unable to concatenate a query together piecemeal. Is there a way to do this using the @Query annotation alone?
(I don't want to declare a repository extension, write Java code calling Spring Mongo DSL methods, nor use the QueryByExample framework. I just want to stick with the @Query annotation)
Dummy sample code:
BookRepo.java:
@Repository
public interface BookRepo extends MongoRepository<Book, String> {
// Want to make the fields dynamic in this query so that if "author" or "publisher" is null/blank, then it's not included in the query
@Query("{'author': ?0, 'publisher': ?1}")
List<Book> getAllBooksBy(String author, String publisher);
}
This doesn't seem to be openly documented in the Spring-data-mongodb Repository framework docs, but you can convert the entire query to a SpEl expression so that you can dynamically construct queries.
SpEl has the concept of a map which is written like this:
{author: 'Joe'}. Maps get converted to standard json queries within the@Queryannotation for Spring Mongo repositories, so you could define a little helper method to take in a SpEl map, filter out the blanks, and return that filtered map for the original@Queryannotation to use for querying.First we define a bean with a method to filter out blanks from maps:
QueryHelper.java:Then, we adjust our
@Queryannotation to start with?#{and invoke our query helper method with the pre-initialized map of all query fields populated:BookRepo.java:We can run our Spring Boot server with this argument:
-Dlogging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUGAnd it'll print out the query that actually got executed when author is blank:
find using query: { "publisher" : "Random House"} fields: Document{{}} for class...Nice! This is quite powerful since you can reuse
QueryHelperfor other queries with optional parameters. This also works for theprojectparameter of the@Queryannotation, which means you could dynamically convert a theoretical "filter" array of Strings into a projection map based on what a user passes to your API.Technical note: The code that triggers this behavior seems to be in ParameterBindingJsonReader with this regex they defined:
private static final Pattern ENTIRE_QUERY_BINDING_PATTERN = Pattern.compile("^\\?(\\d+)$|^[\\?:]#\\{.*\\}$");