Search code examples
javaspringspring-data-jpapredicatesjpa-criteria

Spring Data Specifications - how can I filter objects by mulitple attributes


I'm trying to implement searching of users by mulitple attributes. Let's say, my Person has Name and Lastname. I want to type 'Michael Doe' and list all persons with this name and lastname. When I type only 'Michael', I want all Michaels and so on.

I tried to make it possible by using Predicates like:

private static Specification<UserEntity> containsTextInAttributes(List<String> text, List<String> attributes) {
    List<String> finalTextArray = text.stream().map(e -> "%"+e+"%").collect(Collectors.toList());
    return (root, query, builder) -> builder.or(root.getModel().getDeclaredSingularAttributes().stream()
            .filter(a -> attributes.contains(a.getName()))
            .map(a -> builder.or(finalTextArray.stream().map(e -> builder.like(root.get(a.getName()), e)).toArray(Predicate[]::new)))
            .toArray(Predicate[]::new)
    );
}

public static Specification<UserEntity> containsTextInAllAttributes(List<String> text) {
    return containsTextInAttributes(text, Arrays.asList("lastname", "firstname", "email", "phoneNumber"));
}

However, when I search 'Michael Doe" I have all Michaels and all with Lastname Doe. How can I solve my problem?

Thank you in advance for help.


Solution

  • I. "Michael D", "firstName", "lastName", or "mdoe@gmail", "email"

    public <T> Specification<T> dynamicLike(String likeValue, String... properties) {
        return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
    
            Expression<String> concat = null;
    
            for (String property : properties) {
                if (concat == null) {
                    concat = cb.concat("", root.get(property));
                } else {
                    concat = cb.concat(concat, cb.concat(" ", root.get(property)));
                }
            }
    
            return cb.like(cb.lower(concat), "%" + likeValue.toLowerCase() + "%");
        };
    }
    

    II. "Mic Do @gmail", "firstName", "lastName", "email"

    public <T> Specification<T> dynamicLike2(String value, String... properties) {
        return (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
    
            String[] values = value.split("\\s");
            int minNumber = Integer.min(values.length, properties.length);
    
            Predicate[] likes = new Predicate[minNumber];
    
            for (int i = 0; i < minNumber; i++) {
                likes[i] = cb.like(cb.lower(root.get(properties[i])), "%" + values[i].toLowerCase() + "%");
            }
    
            return cb.and(likes);
        };
    }