package com.commafeed.backend.dao; import java.util.Date; import java.util.List; import javax.ejb.Stateless; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.SetJoin; import javax.persistence.metamodel.SingularAttribute; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.StringUtils; import com.commafeed.backend.feeds.FeedUtils; import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.FeedSubscription_; import com.commafeed.backend.model.Feed_; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @Stateless public class FeedDAO extends GenericDAO { @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public static class FeedCount { public String value; public List feeds; } private Predicate getUpdatablePredicate(Root root) { Predicate isNull = builder.isNull(root.get(Feed_.disabledUntil)); Predicate lessThan = builder.lessThan(root.get(Feed_.disabledUntil), new Date()); return builder.or(isNull, lessThan); } public Long getUpdatableCount() { CriteriaQuery query = builder.createQuery(Long.class); Root root = query.from(getType()); query.select(builder.count(root)); query.where(getUpdatablePredicate(root)); TypedQuery q = em.createQuery(query); return q.getSingleResult(); } public List findNextUpdatable(int count) { CriteriaQuery query = builder.createQuery(getType()); Root root = query.from(getType()); query.where(getUpdatablePredicate(root)); query.orderBy(builder.asc(root.get(Feed_.disabledUntil))); TypedQuery q = em.createQuery(query); q.setMaxResults(count); return q.getResultList(); } public Feed findByUrl(String url) { String normalized = FeedUtils.normalizeURL(url); List feeds = findByField(Feed_.normalizedUrlHash, DigestUtils.sha1Hex(normalized)); Feed feed = Iterables.getFirst(feeds, null); if (feed != null && StringUtils.equals(normalized, feed.getNormalizedUrl())) { return feed; } return null; } public List findByTopic(String topic) { return findByField(Feed_.pushTopicHash, DigestUtils.sha1Hex(topic)); } public int deleteWithoutSubscriptions(int max) { CriteriaQuery query = builder.createQuery(getType()); Root root = query.from(getType()); SetJoin join = root.join(Feed_.subscriptions, JoinType.LEFT); query.where(builder.isNull(join.get(FeedSubscription_.id))); TypedQuery q = em.createQuery(query); q.setMaxResults(max); List list = q.getResultList(); int deleted = list.size(); delete(list); return deleted; } @XmlRootElement public static enum DuplicateMode { NORMALIZED_URL(Feed_.normalizedUrlHash), LAST_CONTENT(Feed_.lastContentHash), PUSH_TOPIC(Feed_.pushTopicHash); private SingularAttribute path; private DuplicateMode(SingularAttribute path) { this.path = path; } public SingularAttribute getPath() { return path; } } public List findDuplicates(DuplicateMode mode, int offset, int limit, long minCount) { CriteriaQuery query = builder.createQuery(String.class); Root root = query.from(getType()); Path path = root.get(mode.getPath()); Expression count = builder.count(path); query.select(path); query.groupBy(path); query.having(builder.greaterThan(count, minCount)); TypedQuery q = em.createQuery(query); limit(q, offset, limit); List pathValues = q.getResultList(); List result = Lists.newArrayList(); for (String pathValue : pathValues) { FeedCount fc = new FeedCount(); fc.value = pathValue; fc.feeds = Lists.newArrayList(); for (Feed feed : findByField(mode.getPath(), pathValue)) { Feed f = new Feed(); f.setId(feed.getId()); f.setUrl(feed.getUrl()); fc.feeds.add(f); } result.add(fc); } return result; } }