Spring Boot Query DSL Filters with MongoDB

1.Overview
Querydsl is a framework that helps to write type-safe SQL-like queries for any database. Pretty much like a proxy layer to the database layer.
2 Setup
Add dependency
<dependencies><dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency><dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency></dependencies>
To Generate Q-Classes
<plugins> <plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>
org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor
</processor>
</configuration>
</execution>
</executions>
</plugin></plugins>
Run maven goal mvn clean install
Will generate new classes under target/generated-sources/java/{your package structure}
3. QueryDslPredicateExecutor & QuerydslBinderCustomizer
Enable Querydsl in our User repository using QueryDslPredicateExecutor
@Repository
public interface UserRepository extends BaseRepository<User>, QuerydslPredicateExecutor<User> {
}
Now to filter create a predicate under folder “repository → predicate → UserPredicate.java”
public class UserPredicate implements QuerydslBinderCustomizer<QUser> {
@Override
public void customize(QuerydslBindings querydslBindings,
QUser qUser) {
// your queries will go here
}
}
4. Queries with DSL
Following imports can help to build the query
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.EnumExpression;
import com.querydsl.core.types.dsl.StringExpression;
4.1 Eq operator
querydslBindings
.bind(qUser.active)
.as("active")
.first(BooleanExpression::eq); // we are using boolean expression because `active` field type is boolean
4.2 String operator
querydslBindings.bind(qUser.fullName)
.as("name")
.first(StringExpression::eq);// we are using boolean expression because `fullName` field type is string
4.3 Enum operator
querydslBindings.bind(qUser.type)
.as("type")
.first(EnumExpression::eq);// we are using boolean expression because `type` field type is ENUM
4.4 In Array Operator (match-any)
querydslBindings.bind(qUser.cities)
.as("city-id")
.first((path, integers) -> path.any().in(integers));
4.5 Greater or Lesser operator
querydslBindings.bind(qUser.registerDate)
.as("register-date")
.first((path, value) -> path.goe(value));or bindings.bind(qUser.registerDate)
.as("register-date")
.first((path, value) -> path.loe(value));
4.6 StartingWith and EndingWith
querydslBindings.bind(qUser.fullName)
.as("name")
.first((path, value) -> path.startsWith("Irfan"));
4.7 Between
querydslBindings.bind(qUser.createdAt)
.as("created-at")
.all((path, values) -> {
Iterator<? extends LocalDateTime> it = values.stream().sorted().iterator();
LocalDateTime from = it.next();
if (values.size() >= 2) {
LocalDateTime to = it.next();
return Optional.of(path.between(from, to));
} else {
return Optional.of(path.goe(from));
}
});
4.8 Transient Fields
This is used when you have a different type or format of the field than specified in the entity. For example if createAt is date-time field but you want to filter only by date without sending time in the query or in the URL. We can achieve this using the below method
Goto respective entity class in our case it is User
com/irfanbaigse/repository/entity/User.java and add folowing
// this fields will not be saved in db
@Transient
@Getter(value = AccessLevel.PRIVATE)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDate creationDateTo;@Transient
@Getter(value = AccessLevel.PRIVATE)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDate creationDateFrom;
Then use it in predicate like this
// date fieldquerydslBindings.bind(qUser.creationDateFrom)
.as("created-from")
.first((path, value) -> qUser.createdAt.goe(value.atStartOfDay()));querydslBindings.bind(qUser.creationDateTo)
.as("created-to")
.first((path, value) ->qUser.createdAt.loe(value.atTime(LocalTime.MAX)));
// end of the day
sample code can be found here on Github.