From 9da424a9f024a497735908b9678e481c1608d639 Mon Sep 17 00:00:00 2001 From: Jeremie Panzer Date: Tue, 26 Mar 2013 09:54:59 +0100 Subject: [PATCH] faster queries for categories --- .../backend/dao/FeedCategoryService.java | 13 +++ .../backend/dao/FeedEntryService.java | 27 ++++++ .../backend/dao/FeedSubscriptionService.java | 35 ------- .../frontend/rest/resources/EntriesREST.java | 93 +++++++++++-------- src/main/resources/META-INF/orm.xml | 13 ++- src/main/webapp/js/controllers.js | 2 +- 6 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/commafeed/backend/dao/FeedCategoryService.java b/src/main/java/com/commafeed/backend/dao/FeedCategoryService.java index b08a1314..938279f5 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedCategoryService.java +++ b/src/main/java/com/commafeed/backend/dao/FeedCategoryService.java @@ -11,6 +11,7 @@ import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.User; import com.commafeed.frontend.utils.ModelFactory.MF; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.uaihebert.model.EasyCriteria; @Stateless @@ -47,6 +48,18 @@ public class FeedCategoryService extends GenericDAO { return category; } + public List findAllChildrenCategories(User user, + FeedCategory parent) { + List list = Lists.newArrayList(); + List all = findAll(user); + for (FeedCategory cat : all) { + if (isChild(cat, parent)) { + list.add(cat); + } + } + return list; + } + public boolean isChild(FeedCategory child, FeedCategory parent) { if (parent == null) { return true; diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryService.java b/src/main/java/com/commafeed/backend/dao/FeedEntryService.java index 03f41694..5921d650 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryService.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryService.java @@ -11,6 +11,7 @@ import javax.persistence.TypedQuery; import org.apache.commons.lang.StringUtils; import com.commafeed.backend.model.Feed; +import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.User; import com.commafeed.frontend.utils.ModelFactory.MF; @@ -85,4 +86,30 @@ public class FeedEntryService extends GenericDAO { } return query.getResultList(); } + + public List getEntries(List categories, User user, + boolean read) { + return getEntries(categories, user, read, -1, -1); + } + + public List getEntries(List categories, User user, + boolean read, int offset, int limit) { + String queryName = null; + if (read) { + queryName = "Entry.readByCategories"; + } else { + queryName = "Entry.unreadByCategories"; + } + TypedQuery query = em.createNamedQuery(queryName, + FeedEntry.class); + query.setParameter("categories", categories); + query.setParameter("user", user); + if (offset > -1) { + query.setFirstResult(offset); + } + if (limit > -1) { + query.setMaxResults(limit); + } + return query.getResultList(); + } } diff --git a/src/main/java/com/commafeed/backend/dao/FeedSubscriptionService.java b/src/main/java/com/commafeed/backend/dao/FeedSubscriptionService.java index 4c30433b..264e02aa 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedSubscriptionService.java +++ b/src/main/java/com/commafeed/backend/dao/FeedSubscriptionService.java @@ -4,9 +4,6 @@ import java.util.List; import javax.ejb.Stateless; import javax.inject.Inject; -import javax.persistence.TypedQuery; - -import org.apache.commons.lang.ObjectUtils; import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.FeedCategory; @@ -14,7 +11,6 @@ import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.User; import com.commafeed.frontend.utils.ModelFactory.MF; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.uaihebert.factory.EasyCriteriaFactory; import com.uaihebert.model.EasyCriteria; @@ -51,35 +47,4 @@ public class FeedSubscriptionService extends GenericDAO return criteria.getResultList(); } - - public List findWithCategory(User user, - FeedCategory category) { - - List categories = Lists.newArrayList(); - for (FeedCategory c : feedCategoryService.findAll(user)) { - if (isChild(c, category)) { - categories.add(c); - } - } - - String query = "select s from FeedSubscription s where s.user=:user and s.category in :categories"; - TypedQuery typedQuery = em.createQuery(query, - FeedSubscription.class); - typedQuery.setParameter("user", user); - typedQuery.setParameter("categories", categories); - return typedQuery.getResultList(); - } - - private boolean isChild(FeedCategory c, FeedCategory category) { - boolean isChild = false; - while (c != null) { - if (ObjectUtils.equals(c.getId(), category.getId())) { - isChild = true; - break; - } - c = c.getParent(); - } - return isChild; - } - } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java b/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java index ac851159..df8234dc 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/EntriesREST.java @@ -1,17 +1,13 @@ package com.commafeed.frontend.rest.resources; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.List; +import java.util.Map; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; -import org.apache.commons.lang.ObjectUtils; - import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedEntry; @@ -19,8 +15,10 @@ import com.commafeed.backend.model.FeedEntryStatus; import com.commafeed.backend.model.FeedSubscription; import com.commafeed.frontend.model.Entries; import com.commafeed.frontend.model.Entry; +import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; @Path("entries") public class EntriesREST extends AbstractREST { @@ -58,33 +56,28 @@ public class EntriesREST extends AbstractREST { buildEntries(subscription, offset, limit, unreadOnly)); } else { - FeedCategory feedCategory = ALL.equals(id) ? null - : feedCategoryService.findById(getUser(), Long.valueOf(id)); - Collection subscriptions = ALL.equals(id) ? feedSubscriptionService - .findAll(getUser()) : feedSubscriptionService - .findWithCategory(getUser(), feedCategory); + FeedCategory feedCategory = null; + if (!ALL.equals(id)) { + feedCategory = new FeedCategory(); + feedCategory.setId(Long.valueOf(id)); + } + List childrenCategories = feedCategoryService + .findAllChildrenCategories(getUser(), feedCategory); + + Map subMapping = Maps.uniqueIndex( + feedSubscriptionService.findAll(getUser()), + new Function() { + public Long apply(FeedSubscription sub) { + return sub.getFeed().getId(); + } + }); entries.setName(ALL.equals(id) ? ALL : feedCategory.getName()); - for (FeedSubscription subscription : subscriptions) { - entries.getEntries().addAll( - buildEntries(subscription, offset, limit, unreadOnly)); - } + entries.getEntries().addAll( + buildEntries(childrenCategories, subMapping, offset, limit, + unreadOnly)); } - Collections.sort(entries.getEntries(), new Comparator() { - - @Override - public int compare(Entry e1, Entry e2) { - return ObjectUtils.compare(e2.getDate(), e1.getDate()); - } - }); - - if (limit > -1) { - int size = entries.getEntries().size(); - int to = Math.min(size, limit); - List subList = entries.getEntries().subList(0, to); - entries.setEntries(Lists.newArrayList(subList)); - } return entries; } @@ -96,22 +89,39 @@ public class EntriesREST extends AbstractREST { List unreadEntries = feedEntryService.getEntries( subscription.getFeed(), getUser(), true, offset, limit); for (FeedEntry feedEntry : unreadEntries) { - Entry entry = buildEntry(feedEntry); - entry.setFeedName(subscription.getTitle()); - entry.setFeedId(String.valueOf(subscription.getId())); - entry.setRead(true); - entries.add(entry); + entries.add(populateEntry(buildEntry(feedEntry), subscription, + true)); } } List readEntries = feedEntryService.getEntries( subscription.getFeed(), getUser(), false, offset, limit); for (FeedEntry feedEntry : readEntries) { - Entry entry = buildEntry(feedEntry); - entry.setFeedName(subscription.getTitle()); - entry.setFeedId(String.valueOf(subscription.getId())); - entry.setRead(false); - entries.add(entry); + entries.add(populateEntry(buildEntry(feedEntry), subscription, + false)); + } + return entries; + } + + private List buildEntries(List categories, + Map subMapping, int offset, int limit, + boolean unreadOnly) { + List entries = Lists.newArrayList(); + + if (!unreadOnly) { + List unreadEntries = feedEntryService.getEntries( + categories, getUser(), true, offset, limit); + for (FeedEntry feedEntry : unreadEntries) { + entries.add(populateEntry(buildEntry(feedEntry), + subMapping.get(feedEntry.getFeed().getId()), true)); + } + } + + List readEntries = feedEntryService.getEntries(categories, + getUser(), false, offset, limit); + for (FeedEntry feedEntry : readEntries) { + entries.add(populateEntry(buildEntry(feedEntry), + subMapping.get(feedEntry.getFeed().getId()), false)); } return entries; } @@ -127,6 +137,13 @@ public class EntriesREST extends AbstractREST { return entry; } + private Entry populateEntry(Entry entry, FeedSubscription sub, boolean read) { + entry.setFeedName(sub.getTitle()); + entry.setFeedId(String.valueOf(sub.getId())); + entry.setRead(read); + return entry; + } + @Path("mark") @GET public void mark(@QueryParam("type") Type type, diff --git a/src/main/resources/META-INF/orm.xml b/src/main/resources/META-INF/orm.xml index 99df6e8a..96d359bd 100644 --- a/src/main/resources/META-INF/orm.xml +++ b/src/main/resources/META-INF/orm.xml @@ -6,12 +6,19 @@ http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> - select e from FeedEntry e where e.guid in :guids + select e from FeedEntry e where e.guid in :guids order by e.updated desc - select e from FeedEntry e where e.feed=:feed and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) + select e from FeedEntry e where e.feed=:feed and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc - select e from FeedEntry e where e.feed=:feed and exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) + select e from FeedEntry e where e.feed=:feed and exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc + + + + select e from FeedEntry e where exists (select s from FeedSubscription s where s.user=:user and s.feed = e.feed and s.category in :categories) and not exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc + + + select e from FeedEntry e where exists (select s from FeedSubscription s where s.user=:user and s.feed = e.feed and s.category in :categories) and exists (select s from FeedEntryStatus s where s.entry = e and s.user =:user and s.read=true) order by e.updated desc \ No newline at end of file diff --git a/src/main/webapp/js/controllers.js b/src/main/webapp/js/controllers.js index cc8d2817..b9fd9649 100644 --- a/src/main/webapp/js/controllers.js +++ b/src/main/webapp/js/controllers.js @@ -102,7 +102,7 @@ module.controller('FeedListCtrl', function($scope, $routeParams, $http, $scope.refreshList(); }); - $scope.limit = 10; + $scope.limit = 20; $scope.busy = false; $scope.hasMore = true;