From 24ee418f3f307bbc0e40dafbbdd682c01c8f482d Mon Sep 17 00:00:00 2001 From: Athou Date: Sun, 14 Jul 2013 05:48:47 +0200 Subject: [PATCH] denormalized fields on statuses for faster queries --- .../backend/dao/FeedEntryStatusDAO.java | 98 ++++++------------- .../backend/model/FeedEntryStatus.java | 54 ++++++++++ .../backend/services/FeedEntryService.java | 6 +- .../services/FeedSubscriptionService.java | 3 +- .../backend/services/FeedUpdateService.java | 7 +- .../resources/changelogs/db.changelog-1.1.xml | 32 ++++++ 6 files changed, 123 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java index fa03ca91..7a8d5a79 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java @@ -6,7 +6,6 @@ import java.util.Map; import javax.ejb.Stateless; import javax.inject.Inject; -import javax.persistence.NoResultException; import javax.persistence.Query; import javax.persistence.Tuple; import javax.persistence.TypedQuery; @@ -16,7 +15,6 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Selection; -import javax.persistence.criteria.SetJoin; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; @@ -54,29 +52,6 @@ public class FeedEntryStatusDAO extends GenericDAO { @Inject ApplicationSettingsService applicationSettingsService; - @SuppressWarnings("unchecked") - public FeedEntryStatus findById(User user, Long id) { - - CriteriaQuery query = builder.createQuery(getType()); - Root root = query.from(getType()); - - Join join = (Join) root - .fetch(FeedEntryStatus_.subscription); - - Predicate p1 = builder.equal(root.get(FeedEntryStatus_.id), id); - Predicate p2 = builder.equal(join.get(FeedSubscription_.user), user); - - query.where(p1, p2); - - FeedEntryStatus status = null; - try { - status = em.createQuery(query).getSingleResult(); - } catch (NoResultException e) { - status = null; - } - return status; - } - public FeedEntryStatus findByEntry(FeedEntry entry, FeedSubscription sub) { CriteriaQuery query = builder.createQuery(getType()); @@ -121,8 +96,7 @@ public class FeedEntryStatusDAO extends GenericDAO { for (FeedEntry entry : entries) { FeedEntryStatus s = existing.get(entry.getId()); if (s == null) { - s = new FeedEntryStatus(); - s.setEntry(entry); + s = new FeedEntryStatus(sub.getUser(), sub, entry); s.setSubscription(sub); s.setRead(true); } @@ -165,7 +139,7 @@ public class FeedEntryStatusDAO extends GenericDAO { predicates.add(builder.or(content, title)); query.where(predicates.toArray(new Predicate[0])); - orderBy(query, root, ReadingOrder.desc); + orderEntriesBy(query, root, ReadingOrder.desc); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -179,8 +153,7 @@ public class FeedEntryStatusDAO extends GenericDAO { FeedEntryStatus status = findByEntry(entry, subscription); if (status == null) { - status = new FeedEntryStatus(); - status.setEntry(entry); + status = new FeedEntryStatus(user, subscription, entry); status.setRead(true); status.setSubscription(subscription); } @@ -203,23 +176,17 @@ public class FeedEntryStatusDAO extends GenericDAO { List predicates = Lists.newArrayList(); - Join entryJoin = root - .join(FeedEntryStatus_.entry); - - Join subJoin = root - .join(FeedEntryStatus_.subscription); - predicates - .add(builder.equal(subJoin.get(FeedSubscription_.user), user)); + .add(builder.equal(root.get(FeedEntryStatus_.user), user)); predicates.add(builder.equal(root.get(FeedEntryStatus_.starred), true)); query.where(predicates.toArray(new Predicate[0])); if (newerThan != null) { predicates.add(builder.greaterThanOrEqualTo( - entryJoin.get(FeedEntry_.inserted), newerThan)); + root.get(FeedEntryStatus_.entryInserted), newerThan)); } - orderBy(query, entryJoin, order); + orderStatusesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -252,7 +219,7 @@ public class FeedEntryStatusDAO extends GenericDAO { } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, root, order); + orderEntriesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -266,8 +233,7 @@ public class FeedEntryStatusDAO extends GenericDAO { FeedEntryStatus status = findByEntry(entry, subscription); if (status == null) { - status = new FeedEntryStatus(); - status.setEntry(entry); + status = new FeedEntryStatus(user, subscription, entry); status.setRead(true); status.setSubscription(subscription); } @@ -289,22 +255,17 @@ public class FeedEntryStatusDAO extends GenericDAO { List predicates = Lists.newArrayList(); - Join entryJoin = root - .join(FeedEntryStatus_.entry); - Join subJoin = root - .join(FeedEntryStatus_.subscription); - predicates - .add(builder.equal(subJoin.get(FeedSubscription_.user), user)); + .add(builder.equal(root.get(FeedEntryStatus_.user), user)); predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); if (newerThan != null) { predicates.add(builder.greaterThanOrEqualTo( - entryJoin.get(FeedEntry_.inserted), newerThan)); + root.get(FeedEntryStatus_.entryInserted), newerThan)); } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, entryJoin, order); + orderStatusesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -332,7 +293,7 @@ public class FeedEntryStatusDAO extends GenericDAO { } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, root, order); + orderEntriesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -359,21 +320,18 @@ public class FeedEntryStatusDAO extends GenericDAO { List predicates = Lists.newArrayList(); - Join entryJoin = root - .join(FeedEntryStatus_.entry); - predicates.add(builder.equal(root.get(FeedEntryStatus_.subscription), subscription)); predicates.add(builder.isFalse(root.get(FeedEntryStatus_.read))); if (newerThan != null) { predicates.add(builder.greaterThanOrEqualTo( - entryJoin.get(FeedEntry_.inserted), newerThan)); + root.get(FeedEntryStatus_.entryInserted), newerThan)); } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, entryJoin, order); + orderStatusesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -413,7 +371,7 @@ public class FeedEntryStatusDAO extends GenericDAO { } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, root, order); + orderEntriesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -427,8 +385,7 @@ public class FeedEntryStatusDAO extends GenericDAO { FeedEntryStatus status = findByEntry(entry, subscription); if (status == null) { - status = new FeedEntryStatus(); - status.setEntry(entry); + status = new FeedEntryStatus(subscription.getUser(), subscription, entry); status.setSubscription(subscription); status.setRead(true); } @@ -455,8 +412,6 @@ public class FeedEntryStatusDAO extends GenericDAO { List predicates = Lists.newArrayList(); - Join entryJoin = root - .join(FeedEntryStatus_.entry); Join subJoin = root .join(FeedEntryStatus_.subscription); @@ -473,12 +428,12 @@ public class FeedEntryStatusDAO extends GenericDAO { if (newerThan != null) { predicates.add(builder.greaterThanOrEqualTo( - entryJoin.get(FeedEntry_.inserted), newerThan)); + root.get(FeedEntryStatus_.entryInserted), newerThan)); } query.where(predicates.toArray(new Predicate[0])); - orderBy(query, entryJoin, order); + orderStatusesBy(query, root, order); TypedQuery q = em.createQuery(query); limit(q, offset, limit); @@ -514,14 +469,23 @@ public class FeedEntryStatusDAO extends GenericDAO { return results; } - private void orderBy(CriteriaQuery query, Path entryJoin, + private void orderEntriesBy(CriteriaQuery query, Path entryJoin, + ReadingOrder order) { + orderBy(query, entryJoin.get(FeedEntry_.updated), order); + } + + private void orderStatusesBy(CriteriaQuery query, Path statusJoin, + ReadingOrder order) { + orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), order); + } + + private void orderBy(CriteriaQuery query, Path date, ReadingOrder order) { if (order != null) { - Path orderPath = entryJoin.get(FeedEntry_.updated); if (order == ReadingOrder.asc) { - query.orderBy(builder.asc(orderPath)); + query.orderBy(builder.asc(date)); } else { - query.orderBy(builder.desc(orderPath)); + query.orderBy(builder.desc(date)); } } } diff --git a/src/main/java/com/commafeed/backend/model/FeedEntryStatus.java b/src/main/java/com/commafeed/backend/model/FeedEntryStatus.java index 39d83749..57fa82b4 100644 --- a/src/main/java/com/commafeed/backend/model/FeedEntryStatus.java +++ b/src/main/java/com/commafeed/backend/model/FeedEntryStatus.java @@ -1,5 +1,7 @@ package com.commafeed.backend.model; +import java.util.Date; + import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; @@ -7,6 +9,8 @@ import javax.persistence.FetchType; 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 +34,32 @@ public class FeedEntryStatus extends AbstractModel { private boolean read; private boolean starred; + /** + * Denormalization starts here + */ + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(nullable = false) + private User user; + + @Temporal(TemporalType.TIMESTAMP) + private Date entryInserted; + + @Temporal(TemporalType.TIMESTAMP) + private Date entryUpdated; + + public FeedEntryStatus() { + + } + + public FeedEntryStatus(User user, FeedSubscription subscription, FeedEntry entry) { + setUser(user); + setSubscription(subscription); + setEntry(entry); + setEntryInserted(entry.getInserted()); + setEntryUpdated(entry.getUpdated()); + } + public FeedSubscription getSubscription() { return subscription; } @@ -62,4 +92,28 @@ public class FeedEntryStatus extends AbstractModel { this.starred = starred; } + public Date getEntryInserted() { + return entryInserted; + } + + public void setEntryInserted(Date entryInserted) { + this.entryInserted = entryInserted; + } + + public Date getEntryUpdated() { + return entryUpdated; + } + + public void setEntryUpdated(Date entryUpdated) { + this.entryUpdated = entryUpdated; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + } diff --git a/src/main/java/com/commafeed/backend/services/FeedEntryService.java b/src/main/java/com/commafeed/backend/services/FeedEntryService.java index 20aa0aed..583ba658 100644 --- a/src/main/java/com/commafeed/backend/services/FeedEntryService.java +++ b/src/main/java/com/commafeed/backend/services/FeedEntryService.java @@ -43,8 +43,7 @@ public class FeedEntryService { } } else { if (status == null) { - status = new FeedEntryStatus(); - status.setEntry(entry); + status = new FeedEntryStatus(user, sub, entry); status.setSubscription(sub); } status.setRead(false); @@ -78,8 +77,7 @@ public class FeedEntryService { } } else { if (status == null) { - status = new FeedEntryStatus(); - status.setEntry(entry); + status = new FeedEntryStatus(user, sub, entry); status.setSubscription(sub); status.setRead(true); } diff --git a/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java b/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java index d33d140d..8ebeae9e 100644 --- a/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java +++ b/src/main/java/com/commafeed/backend/services/FeedSubscriptionService.java @@ -93,8 +93,7 @@ public class FeedSubscriptionService { List allEntries = feedEntryDAO.findByFeed(feed, 0, 10); for (FeedEntry entry : allEntries) { - FeedEntryStatus status = new FeedEntryStatus(); - status.setEntry(entry); + FeedEntryStatus status = new FeedEntryStatus(user, sub, entry); status.setRead(false); status.setSubscription(sub); statuses.add(status); diff --git a/src/main/java/com/commafeed/backend/services/FeedUpdateService.java b/src/main/java/com/commafeed/backend/services/FeedUpdateService.java index 27ce54ee..cf7369cb 100644 --- a/src/main/java/com/commafeed/backend/services/FeedUpdateService.java +++ b/src/main/java/com/commafeed/backend/services/FeedUpdateService.java @@ -75,12 +75,11 @@ public class FeedUpdateService { List statusUpdateList = Lists.newArrayList(); List users = Lists.newArrayList(); for (FeedSubscription sub : subscriptions) { - FeedEntryStatus status = new FeedEntryStatus(); - status.setEntry(update); + User user = sub.getUser(); + FeedEntryStatus status = new FeedEntryStatus(user, sub, update); status.setSubscription(sub); statusUpdateList.add(status); - - users.add(sub.getUser()); + users.add(user); } cache.invalidateUserData(users.toArray(new User[0])); feedEntryDAO.saveOrUpdate(update); diff --git a/src/main/resources/changelogs/db.changelog-1.1.xml b/src/main/resources/changelogs/db.changelog-1.1.xml index cd7c5892..1dec7fcf 100644 --- a/src/main/resources/changelogs/db.changelog-1.1.xml +++ b/src/main/resources/changelogs/db.changelog-1.1.xml @@ -279,5 +279,37 @@ + + + + + + + + + + + + + update FEEDENTRYSTATUSES s SET s.entryUpdated = (select e.updated from FEEDENTRIES e where e.id = s.entry_id) + + + + update FEEDENTRYSTATUSES s SET s.user_id = (select sub.user_id from FEEDSUBSCRIPTIONS sub where sub.id = s.subscription_id) + + + + + + + + + + + + + + +