diff --git a/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java b/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java index 8e953a24..ef18f600 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java @@ -29,6 +29,7 @@ import org.apache.commons.collections4.CollectionUtils; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -238,19 +239,43 @@ public class FeedEntryStatusDAO extends GenericDAO { } } - public UnreadCount getUnreadCount(FeedSubscription sub) { + public Map getUnreadCounts(List subs) { + if (subs.isEmpty()) { + return Map.of(); + } + + Map> subsByFeedId = + subs.stream().collect(Collectors.groupingBy(s -> s.getFeed().getId())); + JPAQuery query = - query().select(ENTRY.count(), ENTRY.published.max()) + query().select(ENTRY.feed.id, ENTRY.count(), ENTRY.published.max()) .from(ENTRY) .leftJoin(ENTRY.statuses, STATUS) - .on(STATUS.subscription.eq(sub)) - .where(ENTRY.feed.eq(sub.getFeed())) - .where(buildUnreadPredicate()); + .on(STATUS.subscription.in(subs)) + .where(ENTRY.feed.id.in(subsByFeedId.keySet())) + .where(buildUnreadPredicate()) + .groupBy(ENTRY.feed.id); - Tuple tuple = query.fetchOne(); - Long count = tuple.get(ENTRY.count()); - Instant published = tuple.get(ENTRY.published.max()); - return new UnreadCount(sub.getId(), count == null ? 0 : count, published); + setTimeout(query, config.database().queryTimeout()); + + Map result = new HashMap<>(); + for (Tuple tuple : query.fetch()) { + Long feedId = tuple.get(ENTRY.feed.id); + Long count = tuple.get(ENTRY.count()); + Instant published = tuple.get(ENTRY.published.max()); + for (FeedSubscription sub : subsByFeedId.get(feedId)) { + result.put( + sub.getId(), + new UnreadCount(sub.getId(), count == null ? 0 : count, published)); + } + } + + // feeds with no unread entries are absent from the query result, default them to zero + for (FeedSubscription sub : subs) { + result.putIfAbsent(sub.getId(), new UnreadCount(sub.getId(), 0, null)); + } + + return result; } private BooleanBuilder buildUnreadPredicate() { diff --git a/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java b/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java index 0f99d6a6..3f939857 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/service/FeedSubscriptionService.java @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; import java.time.Instant; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Slf4j @Singleton @@ -115,10 +114,7 @@ public class FeedSubscriptionService { } public Map getUnreadCount(User user) { - return feedSubscriptionDAO.findAll(user).stream() - .collect( - Collectors.toMap( - FeedSubscription::getId, feedEntryStatusDAO::getUnreadCount)); + return feedEntryStatusDAO.getUnreadCounts(feedSubscriptionDAO.findAll(user)); } @SuppressWarnings("serial")