package org.sigmah.server.dao.impl; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.sigmah.server.dao.ContactDAO; import org.sigmah.server.dao.base.AbstractDAO; import org.sigmah.server.domain.Contact; import org.sigmah.server.domain.ContactModel; import org.sigmah.shared.dto.referential.ContactModelType; public class ContactHibernateDAO extends AbstractDAO<Contact, Integer> implements ContactDAO { private static final float MIN_SIMILARITY_SCORE = 0.5f; @Override public List<Contact> findContactsByTypeAndContactModels(Integer organizationId, ContactModelType type, Set<Integer> contactModelIds, boolean onlyWithoutUser, boolean withEmailNotNull, Set<Integer> orgUnitsIds) { // Too much nullable parameters, let's use criteria query builder to ease the query creation // and to avoid using dangerous string concatenation CriteriaBuilder criteriaBuilder = em().getCriteriaBuilder(); CriteriaQuery<Contact> criteriaQuery = criteriaBuilder.createQuery(Contact.class); Root<Contact> contactRoot = criteriaQuery.from(Contact.class); Join<Object, Object> contactModelJoin = contactRoot.join("contactModel", JoinType.INNER); Join<Object, Object> organizationJoin = contactModelJoin.join("organization", JoinType.INNER); Join<Object, Object> userJoin = contactRoot.join("user", JoinType.LEFT); Join<Object, Object> mainOrgUnitJoin = contactRoot.join("mainOrgUnit", JoinType.LEFT); Join<Object, Object> secondaryOrgUnitJoin = contactRoot.join("secondaryOrgUnits", JoinType.LEFT); Join<Object, Object> userOrgUnitsJoin = userJoin.join("orgUnitsWithProfiles", JoinType.LEFT); Join<Object, Object> organizationOrgUnitsJoin = contactRoot.join("organization", JoinType.LEFT).join("orgUnit", JoinType.LEFT); List<Predicate> predicates = new ArrayList<>(); predicates.add(criteriaBuilder.equal(organizationJoin.get("id"), organizationId)); if (type != null) { predicates.add(criteriaBuilder.equal(contactModelJoin.get("type"), type)); } if (contactModelIds != null && !contactModelIds.isEmpty()) { predicates.add(contactModelJoin.get("id").in(contactModelIds)); } if (onlyWithoutUser) { predicates.add(userJoin.get("id").isNull()); } if (withEmailNotNull) { predicates.add(criteriaBuilder.or( contactRoot.get("email").isNotNull(), userJoin.get("email").isNotNull() )); } if(orgUnitsIds != null && !orgUnitsIds.isEmpty()) { predicates.add(criteriaBuilder.or( criteriaBuilder.or(mainOrgUnitJoin.get("id").in(orgUnitsIds), secondaryOrgUnitJoin.get("id").in(orgUnitsIds)), criteriaBuilder.or(userOrgUnitsJoin.get("orgUnit").get("id").in(orgUnitsIds), organizationOrgUnitsJoin.get("id").in(orgUnitsIds)) )); } criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()])); criteriaQuery.select(contactRoot); criteriaQuery.orderBy(criteriaBuilder.asc(contactRoot.get("name"))); return em().createQuery(criteriaQuery).getResultList(); } @Override public List<Contact> findContactsByEmailOrSimilarName(Integer organizationId, Integer contactId, String email, String firstName, String name) { CriteriaBuilder criteriaBuilder = em().getCriteriaBuilder(); CriteriaQuery<Contact> criteriaQuery = criteriaBuilder.createQuery(Contact.class); Root<Contact> contactRoot = criteriaQuery.from(Contact.class); Join<Object, Object> userJoin = contactRoot.join("user", JoinType.LEFT); Join<Object, Object> organizationJoin = contactRoot.join("organization", JoinType.LEFT); Join<Object, Object> contactModelOrganizationJoin = contactRoot.join("contactModel").join("organization"); List<Predicate> andPredicates = new ArrayList<>(); andPredicates.add(criteriaBuilder.equal(contactModelOrganizationJoin.get("id"), organizationId)); if (contactId != null) { // Let's remove the current contact from the result andPredicates.add(criteriaBuilder.notEqual(contactRoot.get("id"), contactId)); } List<Predicate> orPredicates = new ArrayList<>(); if (email != null) { orPredicates.add(criteriaBuilder.equal(contactRoot.get("email"), email)); orPredicates.add(criteriaBuilder.and( criteriaBuilder.isNull(contactRoot.get("email")), criteriaBuilder.equal(userJoin.get("email"), email) )); } orPredicates.add(criteriaBuilder.and( criteriaBuilder.isNotNull(contactRoot.get("name")), criteriaBuilder.isNotNull(contactRoot.get("firstname")), similarity(criteriaBuilder, contactRoot.get("name").as(String.class), name, contactRoot.get("firstname").as(String.class), firstName) )); orPredicates.add(criteriaBuilder.and( criteriaBuilder.isNull(contactRoot.get("name")), criteriaBuilder.isNull(contactRoot.get("firstname")), criteriaBuilder.or( criteriaBuilder.and( criteriaBuilder.isNotNull(userJoin.get("id")), similarity(criteriaBuilder, userJoin.get("name").as(String.class), name, userJoin.get("firstName").as(String.class), firstName) ), criteriaBuilder.and( criteriaBuilder.isNotNull(organizationJoin.get("id")), similarity(criteriaBuilder, organizationJoin.get("name").as(String.class), name, null, null) ) ) )); andPredicates.add(criteriaBuilder.or(orPredicates.toArray(new Predicate[orPredicates.size()]))); criteriaQuery.where(andPredicates.toArray(new Predicate[andPredicates.size()])); criteriaQuery.select(contactRoot); criteriaQuery.orderBy(criteriaBuilder.asc(contactRoot.get("name"))); return em().createQuery(criteriaQuery).getResultList(); } @Override public List<Contact> getContacts(final Collection<ContactModel> cmodels) { final TypedQuery<Contact> query = em().createQuery("FROM Contact c WHERE c.contactModel IN (:cmodels)", Contact.class); query.setParameter("cmodels", cmodels); return query.getResultList(); } @Override public List<Contact> findByDirectMembership(Integer directMembershipId) { return em().createQuery("" + "SELECT c " + "FROM Contact c " + "WHERE c.parent.id = :directMembershipId ", Contact.class) .setParameter("directMembershipId", directMembershipId) .getResultList(); } private Predicate similarity(CriteriaBuilder criteriaBuilder, Expression<String> nameExpression, String nameValue, Expression<String> firstNameExpression, String firstNameValue) { Expression<String> fullNameExpression; String fullnameValue; if (firstNameExpression != null && firstNameValue != null) { fullNameExpression = criteriaBuilder.concat(criteriaBuilder.concat(nameExpression, " "), firstNameExpression); fullnameValue = (nameValue + " " + firstNameValue).toLowerCase(); } else { fullNameExpression = nameExpression; fullnameValue = nameValue != null ? nameValue.toLowerCase() : ""; } return criteriaBuilder.greaterThanOrEqualTo( criteriaBuilder.function("similarity", Float.class, criteriaBuilder.lower(fullNameExpression), criteriaBuilder.literal(fullnameValue)), MIN_SIMILARITY_SCORE ); } @Override public Contact update(Contact contact) { return em().merge(contact); } }