I have an endpoint to get all Posts, I also have multiple @RequestParams used to filter and search for values etc.
The issue I'm having is that when trying to filter based on specific @RequestParams, I would need to have multiple checks to see whether that specific parameter is passed when calling the endpoint, so in my Controller I have something like this. The parameters are optional, I also have parameters for Pagination etc, but I left it out below.
I have these criteria:
@RequestParam(required=false) List<String> brand - Used to filter by multiple brands
@RequestParam(required=false) String province - Used to filter by province
@RequestParam(required=false) String city - Used to filter by city
// Using these 2 for getting Posts within a certain price range
@RequestParam(defaultValue = "0", required = false) String minValue - Used to filter by min price
@RequestParam(defaultValue = "5000000", required = false) String maxValue - Used to filter by max price
I also have this in my Controller when checking which of my service methods to call based on the parameters passed.
if(query != null) {
pageTuts = postService.findAllPosts(query, pagingSort);
} else if(brand != null) {
pageTuts = postService.findAllByBrandIn(brand, pagingSort);
} else if(minValue != null && maxValue != null) {
pageTuts = postService.findAllPostsByPriceBetween(minValue, maxValue, pagingSort);
} else if(brand != null & minValue != null & maxValue != null) {
pageTuts = postService.findAllPostsByPriceBetween(minValue, maxValue, pagingSort);
} else {
// if no parameters are passed in req, just get all the Posts available
pageTuts = postService.findAllPosts(pagingSort);
}
// I would need more checks to handle all parameters
The issue is that I'm struggling to find out, if I need this condition for each and every possible parameter, which will be a lot of checks and Repository/Service methods based on that parameter.
For example in my Repository I have abstract methods like these:
Page<Post> findAllByProvince(String province, Pageable pageable);
Page<Post> findAllByCity(String city, Pageable pageable);
Page<Post> findAllByProvinceAndCity(String province, String city, Pageable pageable);
Page<Post> findAllByBrandInAndProvince(List<String> brand, String province, Pageable pageable);
And I'd need much more so I could handle the other potential values, ie. findAllByPriceBetween(), findAllByCityAndPriceBetween(), findAllByProvinceAndPriceBetween()...
So I'd like some suggestions on how to handle this?.
Edit
Managed to get it working by overriding the toPredicate method as shown by @M. Deinum with some small tweaks according to my use case.
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
// min/max is never not set as they have default values
predicates.add(builder.between(root.get("price"), params.getMinValue(), params.getMaxValue()));
if (params.getProvince() != null) {
predicates.add(builder.equal(root.get("province"), params.getProvince()));
}
if (params.getCity() != null) {
predicates.add(builder.equal(root.get("city"), params.getCity()));
}
if (!CollectionUtils.isEmpty(params.getBrand())) {
Expression<String> userExpression = root.get("brand");
Predicate p = userExpression.in(params.getBrand());
predicates.add(p);
}
return builder.and(predicates.toArray(new Predicate[0]));
}