TraceSpecification.java

package fr.avenirsesr.portfolio.api.infrastructure.adapter.specification;

import fr.avenirsesr.portfolio.api.infrastructure.adapter.model.AMSEntity;
import fr.avenirsesr.portfolio.api.infrastructure.adapter.model.SkillLevelEntity;
import fr.avenirsesr.portfolio.api.infrastructure.adapter.model.TraceEntity;
import fr.avenirsesr.portfolio.api.infrastructure.adapter.model.UserEntity;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.springframework.data.jpa.domain.Specification;

public class TraceSpecification {
  public static Specification<TraceEntity> ofUser(UserEntity user) {
    return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("user"), user);
  }

  public static Specification<TraceEntity> unassociated() {
    return (root, query, criteriaBuilder) -> {
      Subquery<SkillLevelEntity> skillLevelSubquery = query.subquery(SkillLevelEntity.class);
      Root<TraceEntity> skillLevelSubRoot = skillLevelSubquery.from(TraceEntity.class);
      Join<TraceEntity, SkillLevelEntity> skillLevelJoin = skillLevelSubRoot.join("skillLevels");

      skillLevelSubquery
          .select(skillLevelJoin)
          .where(criteriaBuilder.equal(skillLevelSubRoot.get("id"), root.get("id")));

      Subquery<AMSEntity> amsSubquery = query.subquery(AMSEntity.class);
      Root<TraceEntity> amsSubRoot = amsSubquery.from(TraceEntity.class);
      Join<TraceEntity, AMSEntity> amsJoin = amsSubRoot.join("amses");

      amsSubquery
          .select(amsJoin)
          .where(criteriaBuilder.equal(amsSubRoot.get("id"), root.get("id")));

      Predicate noSkillLevels = criteriaBuilder.not(criteriaBuilder.exists(skillLevelSubquery));
      Predicate noAmses = criteriaBuilder.not(criteriaBuilder.exists(amsSubquery));

      query.distinct(true);

      return criteriaBuilder.and(noSkillLevels, noAmses);
    };
  }
}