17

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

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

Hi! Can you also provide a usage example? How do you use the Query with Projection & Pagination method and exclude an element in projection? Is there any option to provide only the fields we want to exclude or we have to include all the properties here?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.