From 079345b2e06fe505b7d10807690fa898bca20dc9 Mon Sep 17 00:00:00 2001 From: Athou Date: Fri, 19 Jul 2013 11:17:19 +0200 Subject: [PATCH] refactored the way entries and statuses are fetched --- .../commafeed/backend/FixedSizeSortedSet.java | 58 ++ .../backend/dao/FeedEntryStatusDAO.java | 517 ++++++------------ .../backend/dao/FeedSubscriptionDAO.java | 23 + .../commafeed/backend/model/FeedEntry.java | 15 + .../backend/model/FeedFeedEntry.java | 15 + .../backend/services/FeedEntryService.java | 12 +- .../pages/NextUnreadRedirectPage.java | 21 +- .../frontend/rest/resources/CategoryREST.java | 50 +- .../frontend/rest/resources/EntryREST.java | 12 +- .../frontend/rest/resources/FeedREST.java | 15 +- .../resources/changelogs/db.changelog-1.1.xml | 19 +- 11 files changed, 362 insertions(+), 395 deletions(-) create mode 100644 src/main/java/com/commafeed/backend/FixedSizeSortedSet.java diff --git a/src/main/java/com/commafeed/backend/FixedSizeSortedSet.java b/src/main/java/com/commafeed/backend/FixedSizeSortedSet.java new file mode 100644 index 00000000..de5391bd --- /dev/null +++ b/src/main/java/com/commafeed/backend/FixedSizeSortedSet.java @@ -0,0 +1,58 @@ +package com.commafeed.backend; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.TreeSet; + +import org.apache.commons.collections.CollectionUtils; + +import com.google.common.collect.Lists; + +public class FixedSizeSortedSet extends TreeSet { + + private static final long serialVersionUID = 1L; + + private final Comparator comparator; + private final int maxSize; + + public FixedSizeSortedSet(int maxSize, Comparator comparator) { + super(comparator); + this.maxSize = maxSize; + this.comparator = comparator; + } + + @Override + public boolean add(E e) { + if (size() == maxSize) { + E last = last(); + int comparison = comparator.compare(e, last); + if (comparison < 0) { + remove(last); + return super.add(e); + } else { + return false; + } + } else { + return super.add(e); + } + } + + @Override + public boolean addAll(Collection c) { + if (CollectionUtils.isEmpty(c)) { + return false; + } + + boolean success = true; + for (E e : c) { + success &= add(e); + } + return success; + } + + @SuppressWarnings("unchecked") + public List asList() { + return (List) Lists.newArrayList(toArray()); + } +} \ No newline at end of file diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java index 7a8d5a79..ec288f08 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java @@ -1,5 +1,6 @@ package com.commafeed.backend.dao; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Map; @@ -7,22 +8,18 @@ import java.util.Map; import javax.ejb.Stateless; import javax.inject.Inject; import javax.persistence.Query; -import javax.persistence.Tuple; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import javax.persistence.criteria.Selection; -import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.commafeed.backend.model.Feed; -import com.commafeed.backend.model.FeedCategory; +import com.commafeed.backend.FixedSizeSortedSet; import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent_; @@ -32,13 +29,10 @@ import com.commafeed.backend.model.FeedEntry_; import com.commafeed.backend.model.FeedFeedEntry; import com.commafeed.backend.model.FeedFeedEntry_; import com.commafeed.backend.model.FeedSubscription; -import com.commafeed.backend.model.FeedSubscription_; -import com.commafeed.backend.model.Feed_; import com.commafeed.backend.model.Models; import com.commafeed.backend.model.User; import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.backend.services.ApplicationSettingsService; -import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -49,10 +43,38 @@ public class FeedEntryStatusDAO extends GenericDAO { protected static Logger log = LoggerFactory .getLogger(FeedEntryStatusDAO.class); + private static final Comparator ENTRY_COMPARATOR_DESC = new Comparator() { + @Override + public int compare(FeedEntry o1, FeedEntry o2) { + return o2.getUpdated().compareTo(o1.getUpdated()); + }; + }; + + private static final Comparator ENTRY_COMPARATOR_ASC = new Comparator() { + @Override + public int compare(FeedEntry o1, FeedEntry o2) { + return o1.getUpdated().compareTo(o2.getUpdated()); + }; + }; + + private static final Comparator STATUS_COMPARATOR_DESC = new Comparator() { + @Override + public int compare(FeedEntryStatus o1, FeedEntryStatus o2) { + return o2.getEntryUpdated().compareTo(o1.getEntryUpdated()); + }; + }; + + private static final Comparator STATUS_COMPARATOR_ASC = new Comparator() { + @Override + public int compare(FeedEntryStatus o1, FeedEntryStatus o2) { + return o2.getEntryUpdated().compareTo(o1.getEntryUpdated()); + }; + }; + @Inject ApplicationSettingsService applicationSettingsService; - public FeedEntryStatus findByEntry(FeedEntry entry, FeedSubscription sub) { + public FeedEntryStatus getStatus(FeedSubscription sub, FeedEntry entry) { CriteriaQuery query = builder.createQuery(getType()); Root root = query.from(getType()); @@ -64,108 +86,12 @@ public class FeedEntryStatusDAO extends GenericDAO { query.where(p1, p2); List statuses = em.createQuery(query).getResultList(); - return Iterables.getFirst(statuses, null); - } - - public List findByEntries(List entries, - FeedSubscription sub) { - List results = Lists.newArrayList(); - - if (CollectionUtils.isEmpty(entries)) { - return results; + FeedEntryStatus status = Iterables.getFirst(statuses, null); + if (status == null) { + status = new FeedEntryStatus(sub.getUser(), sub, entry); + status.setRead(true); } - - CriteriaQuery query = builder.createQuery(getType()); - Root root = query.from(getType()); - - Predicate p1 = root.get(FeedEntryStatus_.entry).in(entries); - Predicate p2 = builder.equal(root.get(FeedEntryStatus_.subscription), - sub); - - query.where(p1, p2); - - Map existing = Maps.uniqueIndex( - em.createQuery(query).getResultList(), - new Function() { - @Override - public Long apply(FeedEntryStatus input) { - return input.getEntry().getId(); - } - }); - - for (FeedEntry entry : entries) { - FeedEntryStatus s = existing.get(entry.getId()); - if (s == null) { - s = new FeedEntryStatus(sub.getUser(), sub, entry); - s.setSubscription(sub); - s.setRead(true); - } - results.add(s); - } - return results; - } - - public List findByKeywords(User user, String keywords, - int offset, int limit) { - - String joinedKeywords = StringUtils.join( - keywords.toLowerCase().split(" "), "%"); - joinedKeywords = "%" + joinedKeywords + "%"; - - CriteriaQuery query = builder.createTupleQuery(); - Root root = query.from(FeedEntry.class); - - Join feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed); - Join subJoin = feedJoin - .join(Feed_.subscriptions); - Join contentJoin = root - .join(FeedEntry_.content); - - Selection entryAlias = root.alias("entry"); - Selection subAlias = subJoin.alias("subscription"); - query.multiselect(entryAlias, subAlias); - - List predicates = Lists.newArrayList(); - - predicates - .add(builder.equal(subJoin.get(FeedSubscription_.user), user)); - - Predicate content = builder.like( - builder.lower(contentJoin.get(FeedEntryContent_.content)), - joinedKeywords); - Predicate title = builder.like( - builder.lower(contentJoin.get(FeedEntryContent_.title)), - joinedKeywords); - predicates.add(builder.or(content, title)); - - query.where(predicates.toArray(new Predicate[0])); - orderEntriesBy(query, root, ReadingOrder.desc); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - - List list = q.getResultList(); - List results = Lists.newArrayList(); - for (Tuple tuple : list) { - FeedEntry entry = tuple.get(entryAlias); - FeedSubscription subscription = tuple.get(subAlias); - - FeedEntryStatus status = findByEntry(entry, subscription); - if (status == null) { - status = new FeedEntryStatus(user, subscription, entry); - status.setRead(true); - status.setSubscription(subscription); - } - results.add(status); - } - - return lazyLoadContent(true, results); - } - - public List findStarred(User user, ReadingOrder order, - boolean includeContent) { - return findStarred(user, null, -1, -1, order, includeContent); + return status; } public List findStarred(User user, Date newerThan, @@ -176,8 +102,7 @@ public class FeedEntryStatusDAO extends GenericDAO { List predicates = Lists.newArrayList(); - predicates - .add(builder.equal(root.get(FeedEntryStatus_.user), user)); + predicates.add(builder.equal(root.get(FeedEntryStatus_.user), user)); predicates.add(builder.equal(root.get(FeedEntryStatus_.starred), true)); query.where(predicates.toArray(new Predicate[0])); @@ -194,251 +119,150 @@ public class FeedEntryStatusDAO extends GenericDAO { return lazyLoadContent(includeContent, q.getResultList()); } - public List findAll(User user, Date newerThan, int offset, - int limit, ReadingOrder order, boolean includeContent) { + public List findBySubscriptions( + List subscriptions, String keywords, + Date newerThan, int offset, int limit, ReadingOrder order, + boolean includeContent) { - CriteriaQuery query = builder.createTupleQuery(); - Root root = query.from(FeedEntry.class); + int capacity = offset + limit; + Comparator comparator = order == ReadingOrder.desc ? ENTRY_COMPARATOR_DESC + : ENTRY_COMPARATOR_ASC; + FixedSizeSortedSet set = new FixedSizeSortedSet( + capacity < 0 ? Integer.MAX_VALUE : capacity, comparator); + for (FeedSubscription sub : subscriptions) { + CriteriaQuery query = builder + .createQuery(FeedEntry.class); + Root root = query.from(FeedEntry.class); + Join ffeJoin = root + .join(FeedEntry_.feedRelationships); - Join feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed); - Join subJoin = feedJoin - .join(Feed_.subscriptions); + List predicates = Lists.newArrayList(); + predicates.add(builder.equal(ffeJoin.get(FeedFeedEntry_.feed), + sub.getFeed())); - Selection entryAlias = root.alias("entry"); - Selection subAlias = subJoin.alias("subscription"); - query.multiselect(entryAlias, subAlias); + if (newerThan != null) { + predicates.add(builder.greaterThanOrEqualTo( + root.get(FeedEntry_.inserted), newerThan)); + } - List predicates = Lists.newArrayList(); + if (keywords != null) { + Join contentJoin = root + .join(FeedEntry_.content); - predicates - .add(builder.equal(subJoin.get(FeedSubscription_.user), user)); + String joinedKeywords = StringUtils.join(keywords.toLowerCase() + .split(" "), "%"); + joinedKeywords = "%" + joinedKeywords + "%"; - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntry_.inserted), newerThan)); + Predicate content = builder.like(builder.lower(contentJoin + .get(FeedEntryContent_.content)), joinedKeywords); + Predicate title = builder + .like(builder.lower(contentJoin + .get(FeedEntryContent_.title)), joinedKeywords); + predicates.add(builder.or(content, title)); + } + + if (order != null && !set.isEmpty()) { + Predicate filter = null; + FeedEntry last = set.last(); + if (order == ReadingOrder.desc) { + filter = builder.greaterThan( + ffeJoin.get(FeedFeedEntry_.entryUpdated), + last.getUpdated()); + } else { + filter = builder.lessThan( + ffeJoin.get(FeedFeedEntry_.entryUpdated), + last.getUpdated()); + } + predicates.add(filter); + } + query.where(predicates.toArray(new Predicate[0])); + orderEntriesBy(query, ffeJoin, order); + TypedQuery q = em.createQuery(query); + limit(q, 0, capacity); + setTimeout(q); + + List list = q.getResultList(); + for (FeedEntry entry : list) { + entry.setSubscription(sub); + } + set.addAll(list); } - query.where(predicates.toArray(new Predicate[0])); - orderEntriesBy(query, root, order); + List entries = set.asList(); + int size = entries.size(); + if (size < offset) { + return Lists.newArrayList(); + } - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); + entries = entries.subList(Math.max(offset, 0), size); - List list = q.getResultList(); List results = Lists.newArrayList(); - for (Tuple tuple : list) { - FeedEntry entry = tuple.get(entryAlias); - FeedSubscription subscription = tuple.get(subAlias); - - FeedEntryStatus status = findByEntry(entry, subscription); - if (status == null) { - status = new FeedEntryStatus(user, subscription, entry); - status.setRead(true); - status.setSubscription(subscription); - } - results.add(status); + for (FeedEntry entry : entries) { + FeedSubscription subscription = entry.getSubscription(); + results.add(getStatus(subscription, entry)); } return lazyLoadContent(includeContent, results); } - public List findAllUnread(User user, ReadingOrder order, - boolean includeContent) { - return findAllUnread(user, null, -1, -1, order, includeContent); - } - - public List findAllUnread(User user, Date newerThan, - int offset, int limit, ReadingOrder order, boolean includeContent) { - CriteriaQuery query = builder.createQuery(getType()); - Root root = query.from(getType()); - - List predicates = Lists.newArrayList(); - - predicates - .add(builder.equal(root.get(FeedEntryStatus_.user), user)); - predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); - - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntryStatus_.entryInserted), newerThan)); - } - - query.where(predicates.toArray(new Predicate[0])); - orderStatusesBy(query, root, order); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - return lazyLoadContent(includeContent, q.getResultList()); - } - - public List findBySubscription( - FeedSubscription subscription, Date newerThan, int offset, + public List findUnreadBySubscriptions( + List subscriptions, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) { - CriteriaQuery query = builder.createQuery(FeedEntry.class); - Root root = query.from(FeedEntry.class); + int capacity = offset + limit; + Comparator comparator = order == ReadingOrder.desc ? STATUS_COMPARATOR_DESC + : STATUS_COMPARATOR_ASC; + FixedSizeSortedSet set = new FixedSizeSortedSet( + capacity < 0 ? Integer.MAX_VALUE : capacity, comparator); + for (FeedSubscription sub : subscriptions) { + CriteriaQuery query = builder + .createQuery(getType()); + Root root = query.from(getType()); - Join ffeJoin = root.join(FeedEntry_.feedRelationships); + List predicates = Lists.newArrayList(); - List predicates = Lists.newArrayList(); + predicates.add(builder.equal( + root.get(FeedEntryStatus_.subscription), sub)); + predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); - predicates.add(builder.equal(ffeJoin.get(FeedFeedEntry_.feed), - subscription.getFeed())); - - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntry_.inserted), newerThan)); - } - - query.where(predicates.toArray(new Predicate[0])); - orderEntriesBy(query, root, order); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - - List list = q.getResultList(); - return lazyLoadContent(includeContent, - findByEntries(list, subscription)); - } - - public List findUnreadBySubscription( - FeedSubscription subscription, ReadingOrder order, - boolean includeContent) { - return findUnreadBySubscription(subscription, null, -1, -1, order, - includeContent); - } - - public List findUnreadBySubscription( - FeedSubscription subscription, Date newerThan, int offset, - int limit, ReadingOrder order, boolean includeContent) { - - CriteriaQuery query = builder.createQuery(getType()); - Root root = query.from(getType()); - - List predicates = Lists.newArrayList(); - - predicates.add(builder.equal(root.get(FeedEntryStatus_.subscription), - subscription)); - predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); - - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntryStatus_.entryInserted), newerThan)); - } - - query.where(predicates.toArray(new Predicate[0])); - - orderStatusesBy(query, root, order); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - return lazyLoadContent(includeContent, q.getResultList()); - } - - public List findByCategories( - List categories, Date newerThan, int offset, - int limit, ReadingOrder order, boolean includeContent) { - - CriteriaQuery query = builder.createTupleQuery(); - Root root = query.from(FeedEntry.class); - - Join feedJoin = root.join(FeedEntry_.feedRelationships).join(FeedFeedEntry_.feed); - Join subJoin = feedJoin - .join(Feed_.subscriptions); - - Selection entryAlias = root.alias("entry"); - Selection subAlias = subJoin.alias("subscription"); - query.multiselect(entryAlias, subAlias); - - List predicates = Lists.newArrayList(); - - if (categories.size() == 1) { - predicates.add(builder.equal(subJoin - .get(FeedSubscription_.category), categories.iterator() - .next())); - } else { - predicates.add(subJoin.get(FeedSubscription_.category).in( - categories)); - } - - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntry_.inserted), newerThan)); - } - - query.where(predicates.toArray(new Predicate[0])); - orderEntriesBy(query, root, order); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - - List list = q.getResultList(); - List results = Lists.newArrayList(); - for (Tuple tuple : list) { - FeedEntry entry = tuple.get(entryAlias); - FeedSubscription subscription = tuple.get(subAlias); - - FeedEntryStatus status = findByEntry(entry, subscription); - if (status == null) { - status = new FeedEntryStatus(subscription.getUser(), subscription, entry); - status.setSubscription(subscription); - status.setRead(true); + if (newerThan != null) { + predicates.add(builder.greaterThanOrEqualTo( + root.get(FeedEntryStatus_.entryInserted), newerThan)); } - results.add(status); + + if (order != null && !set.isEmpty()) { + Predicate filter = null; + FeedEntryStatus last = set.last(); + if (order == ReadingOrder.desc) { + filter = builder.greaterThan( + root.get(FeedEntryStatus_.entryUpdated), + last.getEntryUpdated()); + } else { + filter = builder.lessThan( + root.get(FeedEntryStatus_.entryUpdated), + last.getEntryUpdated()); + } + predicates.add(filter); + } + query.where(predicates.toArray(new Predicate[0])); + orderStatusesBy(query, root, order); + + TypedQuery q = em.createQuery(query); + limit(q, -1, limit); + setTimeout(q); + + List list = q.getResultList(); + set.addAll(list); } - return lazyLoadContent(includeContent, results); - - } - - public List findUnreadByCategories( - List categories, ReadingOrder order, - boolean includeContent) { - return findUnreadByCategories(categories, null, -1, -1, order, - includeContent); - } - - public List findUnreadByCategories( - List categories, Date newerThan, int offset, - int limit, ReadingOrder order, boolean includeContent) { - - CriteriaQuery query = builder.createQuery(getType()); - Root root = query.from(getType()); - - List predicates = Lists.newArrayList(); - - Join subJoin = root - .join(FeedEntryStatus_.subscription); - - if (categories.size() == 1) { - predicates.add(builder.equal(subJoin - .get(FeedSubscription_.category), categories.iterator() - .next())); - } else { - predicates.add(subJoin.get(FeedSubscription_.category).in( - categories)); + List entries = set.asList(); + int size = entries.size(); + if (size < offset) { + return Lists.newArrayList(); } - predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); - - if (newerThan != null) { - predicates.add(builder.greaterThanOrEqualTo( - root.get(FeedEntryStatus_.entryInserted), newerThan)); - } - - query.where(predicates.toArray(new Predicate[0])); - - orderStatusesBy(query, root, order); - - TypedQuery q = em.createQuery(query); - limit(q, offset, limit); - setTimeout(q); - return lazyLoadContent(includeContent, q.getResultList()); + entries = entries.subList(Math.max(offset, 0), size); + return lazyLoadContent(includeContent, entries); } /** @@ -469,13 +293,13 @@ public class FeedEntryStatusDAO extends GenericDAO { return results; } - private void orderEntriesBy(CriteriaQuery query, Path entryJoin, - ReadingOrder order) { - orderBy(query, entryJoin.get(FeedEntry_.updated), order); + private void orderEntriesBy(CriteriaQuery query, + Path ffeJoin, ReadingOrder order) { + orderBy(query, ffeJoin.get(FeedFeedEntry_.entryUpdated), order); } - private void orderStatusesBy(CriteriaQuery query, Path statusJoin, - ReadingOrder order) { + private void orderStatusesBy(CriteriaQuery query, + Path statusJoin, ReadingOrder order) { orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), order); } @@ -494,27 +318,16 @@ public class FeedEntryStatusDAO extends GenericDAO { setTimeout(query, applicationSettingsService.get().getQueryTimeout()); } - public void markSubscriptionEntries(FeedSubscription subscription, + public void markSubscriptionEntries(List subscriptions, Date olderThan) { - List statuses = findUnreadBySubscription(subscription, - null, false); - markList(statuses, olderThan); - } - - public void markCategoryEntries(User user, List categories, - Date olderThan) { - List statuses = findUnreadByCategories(categories, - null, false); + List statuses = findUnreadBySubscriptions( + subscriptions, null, -1, -1, null, false); markList(statuses, olderThan); } public void markStarredEntries(User user, Date olderThan) { - List statuses = findStarred(user, null, false); - markList(statuses, olderThan); - } - - public void markAllEntries(User user, Date olderThan) { - List statuses = findAllUnread(user, null, false); + List statuses = findStarred(user, null, -1, -1, null, + false); markList(statuses, olderThan); } diff --git a/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java b/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java index f56695c7..2bfafa3f 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java +++ b/src/main/java/com/commafeed/backend/dao/FeedSubscriptionDAO.java @@ -17,7 +17,9 @@ import com.commafeed.backend.model.Feed_; import com.commafeed.backend.model.Models; import com.commafeed.backend.model.User; import com.commafeed.backend.model.User_; +import com.google.common.base.Function; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; @Stateless public class FeedSubscriptionDAO extends GenericDAO { @@ -117,6 +119,27 @@ public class FeedSubscriptionDAO extends GenericDAO { initRelations(list); return list; } + + public List findByCategories(User user, + List categories) { + + List categoryIds = Lists.transform(categories, + new Function() { + @Override + public Long apply(FeedCategory input) { + return input.getId(); + } + }); + + List subscriptions = Lists.newArrayList(); + for (FeedSubscription sub : findAll(user)) { + if (sub.getCategory() != null + && categoryIds.contains(sub.getCategory().getId())) { + subscriptions.add(sub); + } + } + return subscriptions; + } private void initRelations(List list) { for (FeedSubscription sub : list) { diff --git a/src/main/java/com/commafeed/backend/model/FeedEntry.java b/src/main/java/com/commafeed/backend/model/FeedEntry.java index ac56a48d..486d42b1 100644 --- a/src/main/java/com/commafeed/backend/model/FeedEntry.java +++ b/src/main/java/com/commafeed/backend/model/FeedEntry.java @@ -14,6 +14,7 @@ import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -53,6 +54,12 @@ public class FeedEntry extends AbstractModel { @OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE) private Set statuses; + /** + * useful placeholder for the subscription, not persisted + */ + @Transient + private FeedSubscription subscription; + public String getGuid() { return guid; } @@ -125,4 +132,12 @@ public class FeedEntry extends AbstractModel { this.feedRelationships = feedRelationships; } + public FeedSubscription getSubscription() { + return subscription; + } + + public void setSubscription(FeedSubscription subscription) { + this.subscription = subscription; + } + } diff --git a/src/main/java/com/commafeed/backend/model/FeedFeedEntry.java b/src/main/java/com/commafeed/backend/model/FeedFeedEntry.java index ddd0f47d..cd76d8f2 100644 --- a/src/main/java/com/commafeed/backend/model/FeedFeedEntry.java +++ b/src/main/java/com/commafeed/backend/model/FeedFeedEntry.java @@ -1,6 +1,7 @@ package com.commafeed.backend.model; import java.io.Serializable; +import java.util.Date; import javax.persistence.Cacheable; import javax.persistence.Entity; @@ -9,6 +10,8 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -30,6 +33,9 @@ public class FeedFeedEntry implements Serializable { @JoinColumn(name = "FEEDENTRY_ID") private FeedEntry entry; + @Temporal(TemporalType.TIMESTAMP) + private Date entryUpdated; + public FeedFeedEntry() { } @@ -37,6 +43,7 @@ public class FeedFeedEntry implements Serializable { public FeedFeedEntry(Feed feed, FeedEntry entry) { this.feed = feed; this.entry = entry; + this.entryUpdated = entry.getUpdated(); } public Feed getFeed() { @@ -55,4 +62,12 @@ public class FeedFeedEntry implements Serializable { this.entry = entry; } + public Date getEntryUpdated() { + return entryUpdated; + } + + public void setEntryUpdated(Date entryUpdated) { + this.entryUpdated = entryUpdated; + } + } diff --git a/src/main/java/com/commafeed/backend/services/FeedEntryService.java b/src/main/java/com/commafeed/backend/services/FeedEntryService.java index 583ba658..10bbfeea 100644 --- a/src/main/java/com/commafeed/backend/services/FeedEntryService.java +++ b/src/main/java/com/commafeed/backend/services/FeedEntryService.java @@ -30,10 +30,10 @@ public class FeedEntryService { FeedEntry entry = new FeedEntry(); entry.setId(entryId); - FeedEntryStatus status = feedEntryStatusDAO.findByEntry(entry, sub); + FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry); if (read) { - if (status != null) { + if (status.getId() != null) { if (status.isStarred()) { status.setRead(true); feedEntryStatusDAO.saveOrUpdate(status); @@ -42,7 +42,7 @@ public class FeedEntryService { } } } else { - if (status == null) { + if (status.getId() == null) { status = new FeedEntryStatus(user, sub, entry); status.setSubscription(sub); } @@ -64,10 +64,10 @@ public class FeedEntryService { FeedEntry entry = new FeedEntry(); entry.setId(entryId); - FeedEntryStatus status = feedEntryStatusDAO.findByEntry(entry, sub); + FeedEntryStatus status = feedEntryStatusDAO.getStatus(sub, entry); if (!starred) { - if (status != null) { + if (status.getId() != null) { if (!status.isRead()) { status.setStarred(false); feedEntryStatusDAO.saveOrUpdate(status); @@ -76,7 +76,7 @@ public class FeedEntryService { } } } else { - if (status == null) { + if (status.getId() == null) { status = new FeedEntryStatus(user, sub, entry); status.setSubscription(sub); status.setRead(true); diff --git a/src/main/java/com/commafeed/frontend/pages/NextUnreadRedirectPage.java b/src/main/java/com/commafeed/frontend/pages/NextUnreadRedirectPage.java index edb146a2..b03b5331 100755 --- a/src/main/java/com/commafeed/frontend/pages/NextUnreadRedirectPage.java +++ b/src/main/java/com/commafeed/frontend/pages/NextUnreadRedirectPage.java @@ -12,8 +12,10 @@ import org.apache.wicket.request.mapper.parameter.PageParameters; import com.commafeed.backend.dao.FeedCategoryDAO; import com.commafeed.backend.dao.FeedEntryStatusDAO; +import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedEntryStatus; +import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.User; import com.commafeed.backend.model.UserRole.Role; import com.commafeed.backend.model.UserSettings.ReadingOrder; @@ -35,13 +37,16 @@ public class NextUnreadRedirectPage extends WebPage { @Inject FeedEntryStatusDAO feedEntryStatusDAO; + @Inject + FeedSubscriptionDAO feedSubscriptionDAO; + public NextUnreadRedirectPage(PageParameters params) { String categoryId = params.get(PARAM_CATEGORYID).toString(); String orderParam = params.get(PARAM_READINGORDER).toString(); - + User user = CommaFeedSession.get().getUser(); ReadingOrder order = ReadingOrder.desc; - + if (StringUtils.equals(orderParam, "asc")) { order = ReadingOrder.asc; } @@ -49,16 +54,20 @@ public class NextUnreadRedirectPage extends WebPage { List statuses = null; if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) { - statuses = feedEntryStatusDAO.findAllUnread(user, null, 0, 1, - order, true); + List subscriptions = feedSubscriptionDAO + .findAll(user); + statuses = feedEntryStatusDAO.findBySubscriptions(subscriptions, + null, null, 0, 1, order, true); } else { FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId)); if (category != null) { List children = feedCategoryDAO .findAllChildrenCategories(user, category); - statuses = feedEntryStatusDAO.findUnreadByCategories(children, - null, 0, 1, order, true); + List subscriptions = feedSubscriptionDAO + .findByCategories(user, children); + statuses = feedEntryStatusDAO.findUnreadBySubscriptions( + subscriptions, null, 0, 1, order, true); } } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java index 557902ee..345f2f5f 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java @@ -107,12 +107,15 @@ public class CategoryREST extends AbstractResourceREST { if (ALL.equals(id)) { entries.setName("All"); List list = null; + List subscriptions = feedSubscriptionDAO + .findAll(getUser()); if (unreadOnly) { - list = feedEntryStatusDAO.findAllUnread(getUser(), - newerThanDate, offset, limit + 1, order, true); + list = feedEntryStatusDAO.findUnreadBySubscriptions( + subscriptions, newerThanDate, offset, limit + 1, order, + true); } else { - list = feedEntryStatusDAO.findAll(getUser(), newerThanDate, - offset, limit + 1, order, true); + list = feedEntryStatusDAO.findBySubscriptions(subscriptions, + null, newerThanDate, offset, limit + 1, order, true); } for (FeedEntryStatus status : list) { entries.getEntries().add( @@ -132,20 +135,20 @@ public class CategoryREST extends AbstractResourceREST { .get().isImageProxyEnabled())); } } else { - FeedCategory feedCategory = feedCategoryDAO.findById(getUser(), + FeedCategory parent = feedCategoryDAO.findById(getUser(), Long.valueOf(id)); - if (feedCategory != null) { - List childrenCategories = feedCategoryDAO - .findAllChildrenCategories(getUser(), feedCategory); + if (parent != null) { + List categories = feedCategoryDAO + .findAllChildrenCategories(getUser(), parent); + List subs = feedSubscriptionDAO + .findByCategories(getUser(), categories); List list = null; if (unreadOnly) { - list = feedEntryStatusDAO.findUnreadByCategories( - childrenCategories, newerThanDate, offset, - limit + 1, order, true); + list = feedEntryStatusDAO.findUnreadBySubscriptions(subs, + newerThanDate, offset, limit + 1, order, true); } else { - list = feedEntryStatusDAO.findByCategories( - childrenCategories, newerThanDate, offset, - limit + 1, order, true); + list = feedEntryStatusDAO.findBySubscriptions(subs, null, + newerThanDate, offset, limit + 1, order, true); } for (FeedEntryStatus status : list) { entries.getEntries().add( @@ -154,7 +157,7 @@ public class CategoryREST extends AbstractResourceREST { applicationSettingsService.get() .isImageProxyEnabled())); } - entries.setName(feedCategory.getName()); + entries.setName(parent.getName()); } } @@ -223,17 +226,20 @@ public class CategoryREST extends AbstractResourceREST { req.getOlderThan()); if (ALL.equals(req.getId())) { - feedEntryStatusDAO.markAllEntries(getUser(), olderThan); + List subscriptions = feedSubscriptionDAO + .findAll(getUser()); + feedEntryStatusDAO + .markSubscriptionEntries(subscriptions, olderThan); } else if (STARRED.equals(req.getId())) { feedEntryStatusDAO.markStarredEntries(getUser(), olderThan); } else { + FeedCategory parent = feedCategoryDAO.findById(getUser(), + Long.valueOf(req.getId())); List categories = feedCategoryDAO - .findAllChildrenCategories( - getUser(), - feedCategoryDAO.findById(getUser(), - Long.valueOf(req.getId()))); - feedEntryStatusDAO.markCategoryEntries(getUser(), categories, - olderThan); + .findAllChildrenCategories(getUser(), parent); + List subs = feedSubscriptionDAO.findByCategories( + getUser(), categories); + feedEntryStatusDAO.markSubscriptionEntries(subs, olderThan); } cache.invalidateUserData(getUser()); return Response.ok(Status.OK).build(); diff --git a/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java index fe3caaa6..f3cbea79 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java @@ -15,7 +15,10 @@ import org.apache.commons.lang.StringUtils; import com.commafeed.backend.cache.CacheService; import com.commafeed.backend.dao.FeedEntryStatusDAO; +import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.model.FeedEntryStatus; +import com.commafeed.backend.model.FeedSubscription; +import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.backend.services.FeedEntryService; import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entry; @@ -38,6 +41,9 @@ public class EntryREST extends AbstractResourceREST { @Inject FeedEntryStatusDAO feedEntryStatusDAO; + @Inject + FeedSubscriptionDAO feedSubscriptionDAO; + @Inject CacheService cache; @@ -67,7 +73,7 @@ public class EntryREST extends AbstractResourceREST { for (MarkRequest r : req.getRequests()) { markFeedEntry(r); } - + return Response.ok(Status.OK).build(); } @@ -100,8 +106,10 @@ public class EntryREST extends AbstractResourceREST { Entries entries = new Entries(); List list = Lists.newArrayList(); + List subs = feedSubscriptionDAO.findAll(getUser()); List entriesStatus = feedEntryStatusDAO - .findByKeywords(getUser(), keywords, offset, limit); + .findBySubscriptions(subs, keywords, null, offset, limit, + ReadingOrder.desc, true); for (FeedEntryStatus status : entriesStatus) { list.add(Entry.build(status, applicationSettingsService.get() .getPublicUrl(), applicationSettingsService.get() diff --git a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java index 2acb8f81..79f6fc6e 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java @@ -2,6 +2,7 @@ package com.commafeed.frontend.rest.resources; import java.io.StringWriter; import java.net.URI; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; @@ -153,12 +154,13 @@ public class FeedREST extends AbstractResourceREST { List list = null; if (unreadOnly) { - list = feedEntryStatusDAO.findUnreadBySubscription( - subscription, newerThanDate, offset, limit + 1, order, - true); + list = feedEntryStatusDAO.findUnreadBySubscriptions( + Arrays.asList(subscription), newerThanDate, offset, + limit + 1, order, true); } else { - list = feedEntryStatusDAO.findBySubscription(subscription, - newerThanDate, offset, limit + 1, order, true); + list = feedEntryStatusDAO.findBySubscriptions( + Arrays.asList(subscription), null, newerThanDate, + offset, limit + 1, order, true); } for (FeedEntryStatus status : list) { @@ -292,7 +294,8 @@ public class FeedREST extends AbstractResourceREST { FeedSubscription subscription = feedSubscriptionDAO.findById(getUser(), Long.valueOf(req.getId())); if (subscription != null) { - feedEntryStatusDAO.markSubscriptionEntries(subscription, olderThan); + feedEntryStatusDAO.markSubscriptionEntries( + Arrays.asList(subscription), olderThan); } cache.invalidateUserData(getUser()); return Response.ok(Status.OK).build(); diff --git a/src/main/resources/changelogs/db.changelog-1.1.xml b/src/main/resources/changelogs/db.changelog-1.1.xml index cab63b96..21ce607c 100644 --- a/src/main/resources/changelogs/db.changelog-1.1.xml +++ b/src/main/resources/changelogs/db.changelog-1.1.xml @@ -321,5 +321,22 @@ - + + + + + + + + + update FEED_FEEDENTRIES SET entryUpdated = (select e.updated from FEEDENTRIES e where e.id = FEED_FEEDENTRIES.feedentry_id) + + + + + + + + +