Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring - Advanced comparator for QueryDsl support

Following the official documentation, adding @EnableSpringDataWebSupport annotation to my Spring configuration allows to automatically inject a Predicate class in query:

@RequestMapping(method = RequestMethod.GET, path="/find")
public ResponseEntity<PagedResources<FooResource>> find(Pageable pageable, PagedResourcesAssembler<Foo> assembler, @QuerydslPredicate(root = Foo.class) Predicate predicate) {
    Page<Foo> foos = fooRepository.findAll(predicate, pageable)
    final ResourceAssemblerSupport<Foo, FooResource> fooResourceAssembler = new ....;
    final PagedResources<FooResource> pagedResources = assembler.toResource(foos, fooResourceAssembler);
    return new ResponseEntity<>(pagedResources, HttpStatus.OK);
}

Then I can easily search when performing the GET request:

GET /foo/name?=bob&name=alice&age=20

This works fine. However I was wondering how to achieve more advanced search criteria:

  • >
  • <
  • >=
  • <=

Typically I would like to apply these operators to numeric and date fields in my data models. These sorts of criterion are supported in Querydsl.

I tried adding > (%3E) in my query parameters but it fails parsing (for example for numeric field like age it complains it cannot parse >10 as a number.

Is it possible to use this operator directly in the query?

(In case it matters I'm using Spring Data Mongodb)

like image 702
phoenix7360 Avatar asked Aug 19 '16 12:08

phoenix7360


1 Answers

Custom Query DSL binding - greater than comparison

What you can do is define your own QueryDSL Binding in your repository, by extending QueryDslPredicateExecutor and QuerydslBinderCustomizer:

public interface FooRepository
        extends CrudRepository<Foo, Integer>, QueryDslPredicateExecutor<Foo>, QuerydslBinderCustomizer<QFoo> {

    default void customize(final QuerydslBindings bindings, final QFoo foo) {
        SingleValueBinding<NumberPath<Integer>, Integer> singleBinding = new SingleValueBinding<NumberPath<Integer>,Integer>(){
            @Override
            public Predicate bind(NumberPath<Integer> path, Integer ageValue) {
                return path.gt(ageValue);
            }
        };

        bindings.bind(foo.age).first(singleBinding);
    }
}

I'm no Query DSL expert, but my understanding is the following:

a binding defines how a specific field is to be compared to its database column.

The same binding with java 8 lambda: (path, ageValue) -> path.gt(ageValue). You have to read the code in customize method from the url parameter's perspective:

fetch the Foos for which the age provided as parameter is greater than the database's value.

Custom Query DSL binding - between comparison

Another option is to provide lower and upper bound for your parameter, like this: ?age=10&age=30. Then, define the following binding:

default void customize(final QuerydslBindings bindings, final QFoo foo) {
    bindings.bind(foo.age).all((path, values) -> {
        Iterator<? extends Long> it = values.iterator();
        return path.between(it.next(), it.next());
    });
}
like image 69
alexbt Avatar answered Nov 06 '22 01:11

alexbt