0

What is the best practice for solving the number of cases of N searches?

There are 3 search conditions for me. There are search conditions of A, B, and C.

In this case, the number of possible cases is Search only A, search only B, search only C, Search only AB, search only AC, search only BC Search only ABC

In the above situation, there are a total of 6 cases like this.(3!)

To map the number of all cases without thinking

@GetMapping("/A/{A}/B/{B}/C/{C}")
public ReturnType MethodName(@PathVariable AClass A
                             @PathVariable BClass B,
                             @PathVariable CClass C) {
return service.findByAandBandC(A, B, C);
...

I have to create 6 controller methods like the one above.

With 4 search conditions, need 24 methods (4! = 4 * 3 * 2)

If there are 5 search conditions, need 120 methods (5! = 5 * 4 * 3 * 2)

As above, it grows exponentially.

Instead of making all cases a method, I wonder if there is a best practice.

If possible, any answer utilizing spring data jpa would be appreciated.

best regards!

  • See if [Spring Data - ignore parameter if it has a null value](https://stackoverflow.com/questions/43780226/spring-data-ignore-parameter-if-it-has-a-null-value) helps – samabcde May 14 '22 at 15:01

1 Answers1

0

Instead of creating different controllers for each search option, try to make use of one controller. Suppose you have an endpoint to get the list of all users.

@GetMapping
public List<User> listAll() {
    return service.listAll();
}

Accepting query parameters in a map is a good solution. Change the method signature and pass the query parameter map to the service:

@GetMapping
public List<User> listAll(@RequestParam Map<String, String> queryParams) {
    return service.listAll(queryParams);
}

This way you can accept dynamic number of search options. In your service class use Spring JPA Specification. To execute specification your repository interface need to extend JpaSpecificationExecutor<T> interface.

public List<User> listAll(Map<String, String> queryParams) {
    return repository.findAll(createSpec(queryParams));
}

private Specification<User> createSpec(Map<String, String> queryParams) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            String value = queryParams.get("email");
            if(StringUtils.isNotBlank(value)) {
                Predicate email = criteriaBuilder.like(root.get("email"), "%" + value + "%");
                predicates.add(name);
            }

            value = queryParams.get("name");
            if(StringUtils.isNotBlank(value)) {
                Predicate name = criteriaBuilder.like(root.get("name"), "%" + value + "%");
                predicates.add(name);
            }

            return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
        };
    }

When there is a requirement to add more search options, you can add them inside createSpec() method without breaking existing API contract or adding more methods.