I'm using QueryDSL predicates with Spring Data Mongodb. However, I'm facing situations where I have to use the Query API with MongoTemplate (for instance to filter fields to fetch from Mongo). Here is a simple example:
public Stream<MyModel> findSummary(Predicate predicate){
Query query = new Query();
query.fields.include("field1").include("field2");
return mongoTemplate.stream(query, MyModel.class);
}
I would like to transform my Predicate into a Criteria so that I could do something like:
Query query = new Query();
query.addCriteria(XXXXX.toCriteria(predicate));
However I cannot find such utility class.
I've found that a QueryDSL Predicate can be visited and so I started implementing a custom Visitor (com.mysema.query.types.Visitor) but the Criteria API was not designed for this purpose: for example implementing the simple "and" operator of QueryDSL (com.mysema.query.types.Ops#AND) has to be turned into something like
<result of left argument visit assumed to be a Criteria>.and("<path of right argument>").<operator of right argument>(<result of right argument visit>);
Can someone suggest an approach to make QueryDSL Predicates and Spring Data Mongodb Query interoperate ?
Thanks
Benoit
I ran into same problem and did not find any solution on internet for this problem. After many trial & errors, I have implemented a custom solution that works well in my project and it may help others.
Note
Versions used:
Implemetation
first create a class that extends org.springframework.data.mongodb.repository.support.SpringDataMongodbQuery
& override createQuery(Predicate predicate)
method with public
modifier.
public class CustomSpringDataMongodbQuery<T> extends SpringDataMongodbQuery<T> {
public CustomSpringDataMongodbQuery(MongoOperations operations, Class<? extends T> type) {
super(operations, type);
}
@Override
public Document createQuery(Predicate predicate) {
return super.createQuery(predicate);
}
}
now create a class that implements org.springframework.data.mongodb.core.query.CriteriaDefinition
.
public class DocumentCriteria implements CriteriaDefinition {
private final Document criteriaObject;
public DocumentCriteria(Document criteriaObject) {
this.criteriaObject = criteriaObject;
}
@Override
public Document getCriteriaObject() {
return criteriaObject;
}
@Override
public String getKey() {
return null;
}
}
now you can obtain query from predicate using these two classes.
Document document = new CustomSpringDataMongodbQuery<>(mongoTemplate, MyModel.class).createQuery(predicate);
Query query = Query.query(new DocumentCriteria(document));
Query with Projection
If you want to use QClass fields for projection, then it's also possible.
In CustomSpringDataMongodbQuery class, add method
public Query createQuery(Predicate filter, List<Path<?>> fields) {
QTuple qTuple = Projections.tuple(fields.toArray(new Path[0]));
return createQuery(filter, qTuple, QueryModifiers.EMPTY, Collections.emptyList());
}
and pass list of Path(QClass fields) along with predicate
Query with Projection & Pagination
You can add pagination to above method using
public Query createQuery(Predicate filter, List<Path<?>> fields, int page, int size, List<OrderSpecifier<?>> orderSpecifiers) {
QTuple qTuple = Projections.tuple(fields.toArray(new Path[0]));
QueryModifiers queryModifiers = new QueryModifiers((long) size, (long) (page - 1) * size);
return createQuery(filter, qTuple, queryModifiers, orderSpecifiers);
}
Hope this helps everyone who wants to make QueryDSL Predicates and Spring Data Mongodb Query interoperate.
Any suggestions are welcomed!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With