Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data Mongodb: QueryDsl Predicate and Criteria API interoperability

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

like image 566
Benoit Guillon Avatar asked Nov 25 '15 09:11

Benoit Guillon


1 Answers

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:

  • spring-data-mongodb - 3.0.2.RELEASE
  • querydsl - 4.3.1

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!!

like image 99
Harshit Avatar answered Oct 30 '22 03:10

Harshit