I'm trying to use the criteria API to perform a rather complex search in a hierarchical structure of locations.
I'm only writing here the relevant entities and their relevant attributes
Entities
Location
- Location upperLocation
(may be null)
- Set<Location> childrenLocation
(may be empty)
- Set<User> managers
(may be empty)
Alert
- Location originatedIn
(may be null)
User
More details
The relation between User and Location is ManyToMany.
The locations representation is hierarchical. For example World is a location which contains the countries USA, England, France, which themselves contains cities, ...
There can be unlimited levels of sub locations in the trees.
The alerts are originated in a Location or not.
A user is considered manager of a location if he his effectively (in the database) manager of it or if he is manager of one of the location's parents in the tree. Basically if you are manager of USA, you are automatically considered manager of all the children locations in USA, and their children too, etc ...
The criteria I'm trying to build has to find alerts that are originated in a location for which the user is either a direct manager or an inherited manager.
Code
I have a DAO :
AlertRepository extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert>
on which I request the query, just passing the specifications.
List<Alert> alerts = dao.findAll(buildSpecificationForUser(user))
And the specification builder :
private Specification<Alert> buildSpecificationForUser(final User user) {
return new Specification<Alert>() {
@Override
public Predicate toPredicate(Root<Alert> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
query.distinct(true);
Expression<Collection<User>> managersOfLocations = root.get("originatedIn").get("managers");
return builder.isMember(user, managersOfLocations);
}
};
}
With this function I only get the alerts of the locations for which the user is directly a manager of.
Question
How to make it so that it would find also the alerts in the locations for which the user is an inherited manager ?
Update
I've tried also this :
Join<Alert, User> managersOfLocation = root.join("originatedIn").join("upperLocation",JoinType.LEFT).join("managers",JoinType.LEFT);
return builder.equal(managersOfLocation.get("id"),user.getId());
But the result set return all the alerts event if the user is not manager in any of the locations