/*
* Copyright 2015 herd contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.finra.herd.dao.impl;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import org.finra.herd.dao.TagDao;
import org.finra.herd.model.api.xml.TagChild;
import org.finra.herd.model.api.xml.TagKey;
import org.finra.herd.model.jpa.TagEntity;
import org.finra.herd.model.jpa.TagEntity_;
import org.finra.herd.model.jpa.TagTypeEntity;
import org.finra.herd.model.jpa.TagTypeEntity_;
/**
* The tag dao implementation.
*/
@Repository
public class TagDaoImpl extends AbstractHerdDao implements TagDao
{
@Override
public List<TagEntity> getChildrenTags(List<TagEntity> parentTagEntities)
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Get the columns.
Path<String> tagDisplayNameColumn = tagEntityRoot.get(TagEntity_.displayName);
// Create the standard restrictions (i.e. the standard where clauses).
Predicate predicate = getPredicateForInClause(builder, tagEntityRoot.get(TagEntity_.parentTagEntity), parentTagEntities);
// Add all clauses to the query.
criteria.select(tagEntityRoot).where(predicate).orderBy(builder.asc(tagDisplayNameColumn));
// Run the query to get a list of tag children.
return entityManager.createQuery(criteria).getResultList();
}
@Override
public TagEntity getTagByKey(TagKey tagKey)
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Join on the other tables we can filter on.
Join<TagEntity, TagTypeEntity> tagTypeEntityJoin = tagEntityRoot.join(TagEntity_.tagType);
// Create the standard restrictions (i.e. the standard where clauses).
List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(builder.upper(tagTypeEntityJoin.get(TagTypeEntity_.code)), tagKey.getTagTypeCode().toUpperCase()));
predicates.add(builder.equal(builder.upper(tagEntityRoot.get(TagEntity_.tagCode)), tagKey.getTagCode().toUpperCase()));
// Add all clauses to the query.
criteria.select(tagEntityRoot).where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
return executeSingleResultQuery(criteria,
String.format("Found more than one tag with parameters {tagType=\"%s\", tagCode=\"%s\"}.", tagKey.getTagTypeCode(), tagKey.getTagCode()));
}
@Override
public TagEntity getTagByTagTypeAndDisplayName(String tagTypeCode, String displayName)
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Join on the other tables we can filter on.
Join<TagEntity, TagTypeEntity> tagTypeEntityJoin = tagEntityRoot.join(TagEntity_.tagType);
// Create the standard restrictions (i.e. the standard where clauses).
List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(builder.upper(tagTypeEntityJoin.get(TagTypeEntity_.code)), tagTypeCode.toUpperCase()));
predicates.add(builder.equal(builder.upper(tagEntityRoot.get(TagEntity_.displayName)), displayName.toUpperCase()));
// Add all clauses to the query.
criteria.select(tagEntityRoot).where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
return executeSingleResultQuery(criteria,
String.format("Found more than one tag with parameters {tagType=\"%s\", displayName=\"%s\"}.", tagTypeCode, displayName));
}
@Override
public List<TagEntity> getTags()
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Join on the other tables we can filter on.
Join<TagEntity, TagTypeEntity> tagTypeEntityJoin = tagEntityRoot.join(TagEntity_.tagType);
// Get the columns.
Path<String> displayNameColumn = tagEntityRoot.get(TagEntity_.displayName);
Path<Integer> tagTypeOrderNumberColumn = tagTypeEntityJoin.get(TagTypeEntity_.orderNumber);
Path<String> tagTypeCodeColumn = tagTypeEntityJoin.get(TagTypeEntity_.code);
// Add all clauses to the query.
criteria.select(tagEntityRoot).orderBy(builder.asc(tagTypeOrderNumberColumn), builder.asc(tagTypeCodeColumn), builder.asc(displayNameColumn));
// Run the query to get the results.
return entityManager.createQuery(criteria).getResultList();
}
@Override
public List<TagEntity> getTagsByIds(List<Integer> ids)
{
// Create the criteria builder and a tuple style criteria query.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Create the standard restrictions (i.e. the standard where clauses).
Expression<Integer> expression = tagEntityRoot.get(TagEntity_.id);
Predicate queryRestriction = expression.in(ids);
criteria.select(tagEntityRoot).where(queryRestriction);
return entityManager.createQuery(criteria).getResultList();
}
@Override
public List<TagChild> getTagsByTagTypeAndParentTagCode(String tagType, String parentTagCode)
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Join to the other tables we can filter on.
Join<TagEntity, TagTypeEntity> tagTypeEntityJoin = tagEntityRoot.join(TagEntity_.tagType);
// Get the columns.
Path<String> displayNameColumn = tagEntityRoot.get(TagEntity_.displayName);
// Create the standard restrictions (i.e. the standard where clauses).
List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(builder.upper(tagTypeEntityJoin.get(TagTypeEntity_.code)), tagType.toUpperCase()));
if (parentTagCode == null)
{
// Parent tag code is not specified, then return all tags with no parents, i.e. root tags.
predicates.add(builder.isNull(tagEntityRoot.get(TagEntity_.parentTagEntity)));
}
else
{
// Add a restriction for the parent tag code.
predicates.add(builder.equal(builder.upper(tagEntityRoot.get(TagEntity_.parentTagEntity).get(TagEntity_.tagCode)), parentTagCode.toUpperCase()));
}
// Add all clauses to the query.
criteria.select(tagEntityRoot).where(builder.and(predicates.toArray(new Predicate[predicates.size()]))).orderBy(builder.asc(displayNameColumn));
// Run the query to get a list of tag entities back.
List<TagEntity> tagEntities = entityManager.createQuery(criteria).getResultList();
// Populate tag child objects from the returned tag entities.
List<TagChild> tagChildren = new ArrayList<>();
for (TagEntity tagEntity : tagEntities)
{
boolean hasChildren = !tagEntity.getChildrenTagEntities().isEmpty();
tagChildren.add(new TagChild(new TagKey(tagEntity.getTagType().getCode(), tagEntity.getTagCode()), hasChildren));
}
return tagChildren;
}
@Override
public List<TagEntity> getTagsByTagTypeEntityAndParentTagCode(TagTypeEntity tagTypeEntity, String parentTagCode, Boolean isParentTagNull)
{
// Create the criteria builder and the criteria.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag entity.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Get the columns.
Path<String> displayNameColumn = tagEntityRoot.get(TagEntity_.displayName);
// Create the standard restrictions (i.e. the standard where clauses).
List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(tagEntityRoot.get(TagEntity_.tagType), tagTypeEntity));
if (StringUtils.isNotBlank(parentTagCode))
{
// Return all tags that are immediate children of the specified parent tag.
predicates.add(builder.equal(builder.upper(tagEntityRoot.get(TagEntity_.parentTagEntity).get(TagEntity_.tagCode)), parentTagCode.toUpperCase()));
}
else if (BooleanUtils.isTrue(isParentTagNull))
{
// The flag is non-null and true, return all tags with no parents, i.e. root tags.
predicates.add(builder.isNull(tagEntityRoot.get(TagEntity_.parentTagEntity)));
}
else if (BooleanUtils.isFalse(isParentTagNull))
{
// The flag is non-null and false, return all tags with parents.
predicates.add(builder.isNotNull(tagEntityRoot.get(TagEntity_.parentTagEntity)));
}
// Add all clauses to the query.
criteria.select(tagEntityRoot).where(builder.and(predicates.toArray(new Predicate[predicates.size()]))).orderBy(builder.asc(displayNameColumn));
// Run the query to get the results.
return entityManager.createQuery(criteria).getResultList();
}
@Override
public List<TagEntity> getPercentageOfAllTags(double percentage)
{
// Create the criteria builder and a tuple style criteria query.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Integer> criteria = builder.createQuery(Integer.class);
// The criteria root is the tag.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Get the columns.
Path<Integer> idColumn = tagEntityRoot.get(TagEntity_.id);
criteria.select(idColumn);
List<Integer> allTagIdsList = entityManager.createQuery(criteria).getResultList();
List<Integer> percentageOfTagIdsList = new ArrayList<>();
/*
* Gets a percentage of all tag entities.
* The percentage is randomly selected from all the tags.
*
* For each tag id in the list of all tag ids, get a random double value between 0 and 1.
* If that value is below the percentage double value, also a number between 0 and 1 (inclusive),
* then add the tag id to the list of tag ids that will be used to return a random percentage
* of tag entities retrieved from the database.
*/
allTagIdsList.forEach(id -> {
if (ThreadLocalRandom.current().nextDouble() < percentage)
{
percentageOfTagIdsList.add(id);
}
});
return getTagsByIds(percentageOfTagIdsList);
}
@Override
public List<TagEntity> getMostRecentTags(int numberOfResults)
{
// Create the criteria builder and a tuple style criteria query.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<TagEntity> criteria = builder.createQuery(TagEntity.class);
// The criteria root is the tag.
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
// Get the columns.
Path<Timestamp> tagUpdatedOnColumn = tagEntityRoot.get(TagEntity_.updatedOn);
// Select the tags and order descending by the updated on column
criteria.select(tagEntityRoot).orderBy(builder.desc(tagUpdatedOnColumn));
return entityManager.createQuery(criteria).setMaxResults(numberOfResults).getResultList();
}
@Override
public long getCountOfAllTags()
{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
Root<TagEntity> tagEntityRoot = criteria.from(TagEntity.class);
criteria.select(builder.count(tagEntityRoot));
return entityManager.createQuery(criteria).getSingleResult();
}
}