@SuppressWarnings("unchecked")
  public static Predicate[] deviceListPredicates(
      CriteriaBuilder cb,
      Root<Device> from,
      Optional<String> name,
      Optional<String> namePattern,
      Optional<String> status,
      Optional<Long> networkId,
      Optional<String> networkName,
      Optional<Long> deviceClassId,
      Optional<String> deviceClassName,
      Optional<String> deviceClassVersion,
      Optional<HivePrincipal> principal) {
    final List<Predicate> predicates = new LinkedList<>();

    name.ifPresent(n -> predicates.add(cb.equal(from.<String>get("name"), n)));
    namePattern.ifPresent(np -> predicates.add(cb.like(from.<String>get("name"), np)));
    status.ifPresent(s -> predicates.add(cb.equal(from.<String>get("status"), s)));

    final Join<Device, Network> networkJoin = (Join) from.fetch("network", JoinType.LEFT);
    networkId.ifPresent(nId -> predicates.add(cb.equal(networkJoin.<Long>get("id"), nId)));
    networkName.ifPresent(
        nName -> predicates.add(cb.equal(networkJoin.<String>get("name"), nName)));

    final Join<Device, DeviceClass> dcJoin = (Join) from.fetch("deviceClass", JoinType.LEFT);
    deviceClassId.ifPresent(dcId -> predicates.add(cb.equal(dcJoin.<Long>get("id"), dcId)));
    deviceClassName.ifPresent(
        dcName -> predicates.add(cb.equal(dcJoin.<String>get("name"), dcName)));
    deviceClassVersion.ifPresent(
        dcVersion -> predicates.add(cb.equal(dcJoin.<String>get("version"), dcVersion)));

    predicates.addAll(deviceSpecificPrincipalPredicates(cb, from, principal));

    return predicates.toArray(new Predicate[predicates.size()]);
  }
  @SuppressWarnings("unchecked")
  private static List<Predicate> deviceSpecificPrincipalPredicates(
      CriteriaBuilder cb, Root<Device> from, Optional<HivePrincipal> principal) {
    final List<Predicate> predicates = new LinkedList<>();
    final Join<Device, Network> networkJoin = (Join) from.fetch("network", JoinType.LEFT);
    final Join<Device, Network> usersJoin = (Join) networkJoin.fetch("users", JoinType.LEFT);
    from.fetch("deviceClass", JoinType.LEFT); // need this fetch to populate deviceClass
    principal.ifPresent(
        p -> {
          User user = p.getUser();
          if (user == null && p.getKey() != null) {
            user = p.getKey().getUser();
          }
          if (user != null && !user.isAdmin()) {
            predicates.add(cb.equal(usersJoin.<Long>get("id"), user.getId()));
          }

          if (p.getDevice() != null) {
            predicates.add(cb.equal(from.<Long>get("id"), p.getDevice().getId()));
          }

          if (p.getKey() != null) {
            for (AccessKeyBasedFilterForDevices extraFilter :
                AccessKeyBasedFilterForDevices.createExtraFilters(p.getKey().getPermissions())) {
              if (extraFilter.getDeviceGuids() != null) {
                predicates.add(from.<String>get("guid").in(extraFilter.getDeviceGuids()));
              }
              if (extraFilter.getNetworkIds() != null) {
                predicates.add(networkJoin.<Long>get("id").in(extraFilter.getNetworkIds()));
              }
            }
          }
        });

    return predicates;
  }
  public static Predicate[] accessKeyListPredicates(
      CriteriaBuilder cb,
      Root<AccessKey> from,
      Long userId,
      Optional<String> labelOpt,
      Optional<String> labelPatten,
      Optional<Integer> typeOpt) {
    List<Predicate> predicates = new LinkedList<>();

    Join user = (Join) from.fetch("user", JoinType.LEFT);
    predicates.add(cb.equal(user.get("id"), userId));

    if (labelPatten.isPresent()) {
      labelPatten.ifPresent(pattern -> predicates.add(cb.like(from.get("label"), pattern)));
    } else {
      labelOpt.ifPresent(label -> predicates.add(cb.equal(from.get("label"), label)));
    }

    typeOpt.ifPresent(type -> predicates.add(cb.equal(from.get("type"), type)));

    return predicates.toArray(new Predicate[predicates.size()]);
  }
  @Override
  public List<VeteranAssessment> searchVeteranAssessmentForExport(
      Integer clinicanId,
      Integer createdByUserId,
      Integer programId,
      Date fromAssessmentDate,
      Date toAssessmentDate,
      Integer veteranId,
      List<Integer> programIdList) {

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<VeteranAssessment> criteriaQuery =
        criteriaBuilder.createQuery(VeteranAssessment.class);

    Root<VeteranAssessment> veteranAssessmentRoot = criteriaQuery.from(VeteranAssessment.class);
    Join<VeteranAssessment, AssessmentStatus> assessmentStatusJoin =
        veteranAssessmentRoot.join("assessmentStatus");

    List<Predicate> criteriaList = new ArrayList<Predicate>();

    // only include assessments in the appropriate state
    Expression<Integer> statusExp = assessmentStatusJoin.get("assessmentStatusId");
    Predicate statusPredicate = statusExp.in(getExportAssessmentStates());
    criteriaList.add(statusPredicate);

    if (clinicanId != null) {
      Join<VeteranAssessment, User> clinicianJoin = veteranAssessmentRoot.join("clinician");
      criteriaList.add(criteriaBuilder.equal(clinicianJoin.get("userId"), clinicanId));
    }

    if (createdByUserId != null) {
      Join<VeteranAssessment, User> createdByUserJoin = veteranAssessmentRoot.join("createdByUser");
      criteriaList.add(criteriaBuilder.equal(createdByUserJoin.get("userId"), createdByUserId));
    }

    if (fromAssessmentDate != null) {
      criteriaList.add(
          criteriaBuilder.greaterThan(
              veteranAssessmentRoot.<Date>get("dateCreated"), fromAssessmentDate));
    }

    if (toAssessmentDate != null) {
      criteriaList.add(
          criteriaBuilder.lessThanOrEqualTo(
              veteranAssessmentRoot.<Date>get("dateCreated"), toAssessmentDate));
    }

    if (veteranId != null) {
      Join<VeteranAssessment, Veteran> veteranJoin = veteranAssessmentRoot.join("veteran");
      criteriaList.add(criteriaBuilder.equal(veteranJoin.get("veteranId"), veteranId));
    }

    if (programId != null) {
      // criteriaList.add(criteriaBuilder.equal(programJoin.get("programId"), programId));
      if (programIdList == null) {
        programIdList = new ArrayList<Integer>();
      }

      programIdList.add(programId);
    }

    if (programIdList != null && programIdList.size() > 0) {
      Join<VeteranAssessment, Program> programJoin = veteranAssessmentRoot.join("program");
      Expression<Integer> exp = programJoin.get("programId");
      Predicate programIdPredicate = exp.in(programIdList);
      criteriaList.add(programIdPredicate);
    }

    criteriaQuery.select(veteranAssessmentRoot);
    criteriaQuery.where(criteriaBuilder.and(criteriaList.toArray(new Predicate[0])));

    @SuppressWarnings("rawtypes")
    Expression orderByPath = veteranAssessmentRoot.get("veteranAssessmentId");

    criteriaQuery.orderBy(criteriaBuilder.desc(orderByPath));

    // Generate the query based on the criteria.
    TypedQuery<VeteranAssessment> query = entityManager.createQuery(criteriaQuery);
    List<VeteranAssessment> veteranAssessments = query.getResultList();

    return veteranAssessments;
  }
  @Override
  public SearchResult<VeteranAssessment> searchVeteranAssessment(
      Integer veteranAssessmentId,
      Integer veteranId,
      Integer programId,
      Integer clinicanId,
      Integer createdByUserId,
      Date fromAssessmentDate,
      Date toAssessmentDate,
      List<Integer> programIdList,
      SearchAttributes searchAttributes) {

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<VeteranAssessment> criteriaQuery =
        criteriaBuilder.createQuery(VeteranAssessment.class);

    Root<VeteranAssessment> veteranAssessmentRoot = criteriaQuery.from(VeteranAssessment.class);
    Join<VeteranAssessment, AssessmentStatus> assessmentStatusJoin =
        veteranAssessmentRoot.join("assessmentStatus");
    Join<VeteranAssessment, Veteran> veteranJoin = veteranAssessmentRoot.join("veteran");
    Join<VeteranAssessment, Program> programJoin = veteranAssessmentRoot.join("program");
    Join<VeteranAssessment, User> clinicianJoin = veteranAssessmentRoot.join("clinician");
    Join<VeteranAssessment, User> createdByUserJoin = veteranAssessmentRoot.join("createdByUser");

    List<Predicate> criteriaList = new ArrayList<Predicate>();

    if (veteranAssessmentId != null) {
      criteriaList.add(
          criteriaBuilder.equal(
              veteranAssessmentRoot.get("veteranAssessmentId"), veteranAssessmentId));
    }

    if (veteranId != null) {
      criteriaList.add(criteriaBuilder.equal(veteranJoin.get("veteranId"), veteranId));
    }

    if (programId != null) {
      criteriaList.add(criteriaBuilder.equal(programJoin.get("programId"), programId));
    }

    if (clinicanId != null) {
      criteriaList.add(criteriaBuilder.equal(clinicianJoin.get("userId"), clinicanId));
    }

    if (createdByUserId != null) {
      criteriaList.add(criteriaBuilder.equal(createdByUserJoin.get("userId"), createdByUserId));
    }

    if (fromAssessmentDate != null) {
      criteriaList.add(
          criteriaBuilder.greaterThanOrEqualTo(
              veteranAssessmentRoot.<Date>get("dateUpdated"), fromAssessmentDate));
    }

    if (toAssessmentDate != null) {
      criteriaList.add(
          criteriaBuilder.lessThanOrEqualTo(
              veteranAssessmentRoot.<Date>get("dateUpdated"), toAssessmentDate));
    }

    if (programIdList != null && programIdList.size() > 0) {
      Expression<Integer> exp = programJoin.get("programId");
      Predicate programIdPredicate = exp.in(programIdList);
      criteriaList.add(programIdPredicate);
    }

    criteriaQuery.select(veteranAssessmentRoot);
    criteriaQuery.where(criteriaBuilder.and(criteriaList.toArray(new Predicate[0])));

    // Set default order by field and then check if one was passed to us.
    @SuppressWarnings("rawtypes")
    Expression orderByPath = veteranAssessmentRoot.get("veteranAssessmentId");

    if (StringUtils.isNotBlank(searchAttributes.getSortColumn())) {
      if (searchAttributes.getSortColumn().equalsIgnoreCase("programName")) {
        orderByPath = programJoin.get("name");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("clinicianName")) {
        orderByPath = clinicianJoin.get("lastName");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("createdBy")) {
        orderByPath = createdByUserJoin.get("lastName");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("createDate")) {
        orderByPath = veteranAssessmentRoot.get("dateCreated");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("assessmentDate")) {
        orderByPath = veteranAssessmentRoot.get("dateUpdated");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("completeDate")) {
        orderByPath = veteranAssessmentRoot.get("dateCompleted");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("veteranId")) {
        orderByPath = veteranJoin.get("veteranId");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("veteranName")) {
        orderByPath = veteranJoin.get("lastName");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("assessmentStatusName")) {
        orderByPath = assessmentStatusJoin.get("name");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("ssnLastFour")) {
        orderByPath = veteranJoin.get("ssnLastFour");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("duration")) {
        orderByPath = veteranAssessmentRoot.get("duration");
      } else if (searchAttributes.getSortColumn().equalsIgnoreCase("percentComplete")) {
        orderByPath = veteranAssessmentRoot.get("percentComplete");
      }
    }

    if (searchAttributes.getSortDirection() == SortDirection.SORT_DESCENDING) {

      criteriaQuery.orderBy(criteriaBuilder.desc(orderByPath));
    } else {
      criteriaQuery.orderBy(criteriaBuilder.asc(orderByPath));
    }

    // Generate the query based on the criteria.
    TypedQuery<VeteranAssessment> query = entityManager.createQuery(criteriaQuery);

    SearchResult<VeteranAssessment> searchResult = new SearchResult<VeteranAssessment>();

    // Get the total count. Not a very efficient way....
    Integer totalCount = query.getResultList().size();
    searchResult.setTotalNumRowsFound(totalCount);

    // Now get only the page.
    query.setFirstResult(searchAttributes.getRowStartIndex());
    query.setMaxResults(searchAttributes.getPageSize());

    List<VeteranAssessment> veteranAssessments = query.getResultList();
    searchResult.setResultList(veteranAssessments);

    return searchResult;
  }