delete old statuses

This commit is contained in:
Athou
2013-07-23 15:27:56 +02:00
parent 074ecbf159
commit 150920e0c8
10 changed files with 120 additions and 37 deletions

View File

@@ -15,7 +15,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<production>false</production> <production>false</production>
<jpa.show_sql>false</jpa.show_sql> <jpa.show_sql>true</jpa.show_sql>
<jpa.datasource.name>java:openejb/Resource/My DataSource</jpa.datasource.name> <jpa.datasource.name>java:openejb/Resource/My DataSource</jpa.datasource.name>
<jpa.cache>false</jpa.cache> <jpa.cache>false</jpa.cache>
<jpa.table.encoding>utf8mb4</jpa.table.encoding> <jpa.table.encoding>utf8mb4</jpa.table.encoding>

View File

@@ -14,10 +14,11 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.Criteria; import org.hibernate.Criteria;
import org.hibernate.criterion.Disjunction; import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order; import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections; import org.hibernate.criterion.Projections;
@@ -80,8 +81,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
List<FeedEntryStatus> statuses = em.createQuery(query).getResultList(); List<FeedEntryStatus> statuses = em.createQuery(query).getResultList();
FeedEntryStatus status = Iterables.getFirst(statuses, null); FeedEntryStatus status = Iterables.getFirst(statuses, null);
if (status == null) { if (status == null) {
Date unreadThreshold = getUnreadThreshold();
boolean read = unreadThreshold == null ? false : entry
.getInserted().before(unreadThreshold);
status = new FeedEntryStatus(sub.getUser(), sub, entry); status = new FeedEntryStatus(sub.getUser(), sub, entry);
status.setRead(false); status.setRead(read);
status.setMarkable(!read);
} else {
status.setMarkable(true);
} }
return status; return status;
} }
@@ -134,30 +141,33 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
FeedEntry_.content.getName(), "content", FeedEntry_.content.getName(), "content",
JoinType.INNER_JOIN); JoinType.INNER_JOIN);
String joinedKeywords = StringUtils.join(keywords.toLowerCase() for (String keyword : keywords.split(" ")) {
.split(" "), "%");
joinedKeywords = "%" + joinedKeywords + "%";
Disjunction or = Restrictions.disjunction(); Disjunction or = Restrictions.disjunction();
or.add(Restrictions.ilike(FeedEntryContent_.content.getName(), or.add(Restrictions.ilike(FeedEntryContent_.content.getName(),
joinedKeywords)); keyword, MatchMode.ANYWHERE));
or.add(Restrictions.ilike(FeedEntryContent_.title.getName(), or.add(Restrictions.ilike(FeedEntryContent_.title.getName(),
joinedKeywords)); keyword, MatchMode.ANYWHERE));
contentJoin.add(or); contentJoin.add(or);
} }
}
if (unreadOnly) { if (unreadOnly) {
Criteria statusJoin = criteria.createCriteria(FeedEntry_.statuses Criteria statusJoin = criteria.createCriteria(FeedEntry_.statuses
.getName(), "status", JoinType.LEFT_OUTER_JOIN, .getName(), "status", JoinType.LEFT_OUTER_JOIN,
Restrictions.eq(FeedEntryStatus_.subscription.getName(), Restrictions.eq(FeedEntryStatus_.subscription.getName(),
sub)); sub));
Disjunction or = Restrictions.disjunction(); Disjunction or = Restrictions.disjunction();
or.add(Restrictions.isNull(FeedEntryStatus_.id.getName())); or.add(Restrictions.isNull(FeedEntryStatus_.read.getName()));
or.add(Restrictions.eq(FeedEntryStatus_.read.getName(), false)); or.add(Restrictions.eq(FeedEntryStatus_.read.getName(), false));
statusJoin.add(or); statusJoin.add(or);
Date unreadThreshold = getUnreadThreshold();
if (unreadThreshold != null) {
criteria.add(Restrictions.ge(FeedEntry_.inserted.getName(),
unreadThreshold));
}
} }
if (last != null) { if (last != null) {
@@ -175,11 +185,11 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
if (order != null) { if (order != null) {
Order o = null; Order o = null;
if (order == ReadingOrder.asc) { if (order == ReadingOrder.asc) {
o = Order.asc(FeedEntry_.updated.getName()); o = Order.asc(FeedFeedEntry_.entryUpdated.getName());
} else { } else {
o = Order.desc(FeedEntry_.updated.getName()); o = Order.desc(FeedFeedEntry_.entryUpdated.getName());
} }
criteria.addOrder(o); ffeJoin.addOrder(o);
} }
if (offset > -1) { if (offset > -1) {
criteria.setFirstResult(offset); criteria.setFirstResult(offset);
@@ -235,6 +245,13 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, results); return lazyLoadContent(includeContent, results);
} }
private Date getUnreadThreshold() {
int keepStatusDays = applicationSettingsService.get()
.getKeepStatusDays();
return keepStatusDays > 0 ? DateUtils.addMinutes(new Date(), -1
* keepStatusDays) : null;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Long getUnreadCount(FeedSubscription subscription) { public Long getUnreadCount(FeedSubscription subscription) {
Long count = null; Long count = null;

View File

@@ -35,6 +35,7 @@ public class ApplicationSettings extends AbstractModel {
private boolean imageProxyEnabled; private boolean imageProxyEnabled;
private int queryTimeout; private int queryTimeout;
private boolean crawlingPaused; private boolean crawlingPaused;
private int keepStatusDays = 0;
@Column(length = 255) @Column(length = 255)
private String announcement; private String announcement;
@@ -200,4 +201,12 @@ public class ApplicationSettings extends AbstractModel {
this.crawlingPaused = crawlingPaused; this.crawlingPaused = crawlingPaused;
} }
public int getKeepStatusDays() {
return keepStatusDays;
}
public void setKeepStatusDays(int keepStatusDays) {
this.keepStatusDays = keepStatusDays;
}
} }

View File

@@ -11,6 +11,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import javax.persistence.Transient;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy;
@@ -34,6 +35,9 @@ public class FeedEntryStatus extends AbstractModel {
private boolean read; private boolean read;
private boolean starred; private boolean starred;
@Transient
private boolean markable;
/** /**
* Denormalization starts here * Denormalization starts here
*/ */
@@ -52,7 +56,8 @@ public class FeedEntryStatus extends AbstractModel {
} }
public FeedEntryStatus(User user, FeedSubscription subscription, FeedEntry entry) { public FeedEntryStatus(User user, FeedSubscription subscription,
FeedEntry entry) {
setUser(user); setUser(user);
setSubscription(subscription); setSubscription(subscription);
setEntry(entry); setEntry(entry);
@@ -116,4 +121,12 @@ public class FeedEntryStatus extends AbstractModel {
this.user = user; this.user = user;
} }
public boolean isMarkable() {
return markable;
}
public void setMarkable(boolean markable) {
this.markable = markable;
}
} }

View File

@@ -0,0 +1,27 @@
package com.commafeed.backend.services;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
@Stateless
public class CleaningService {
@Inject
ApplicationSettingsService applicationSettingsService;
@PersistenceContext
EntityManager em;
// @Schedule(hour = "*")
protected void cleanOldStatuses() {
int keepStatusDays = applicationSettingsService.get()
.getKeepStatusDays();
if (keepStatusDays > 0) {
Query query = em.createNamedQuery("Statuses.deleteOld");
query.executeUpdate();
}
}
}

View File

@@ -33,6 +33,7 @@ public class Entry implements Serializable {
entry.setRead(status.isRead()); entry.setRead(status.isRead());
entry.setStarred(status.isStarred()); entry.setStarred(status.isStarred());
entry.setMarkable(status.isMarkable());
entry.setId(String.valueOf(feedEntry.getId())); entry.setId(String.valueOf(feedEntry.getId()));
entry.setGuid(feedEntry.getGuid()); entry.setGuid(feedEntry.getGuid());
@@ -124,6 +125,9 @@ public class Entry implements Serializable {
@ApiProperty("starred status") @ApiProperty("starred status")
private boolean starred; private boolean starred;
@ApiProperty("wether the entry is still markable (old entry statuses are discarded)")
private boolean markable;
public String getId() { public String getId() {
return id; return id;
} }
@@ -268,4 +272,12 @@ public class Entry implements Serializable {
this.insertedDate = insertedDate; this.insertedDate = insertedDate;
} }
public boolean isMarkable() {
return markable;
}
public void setMarkable(boolean markable) {
this.markable = markable;
}
} }

View File

@@ -5,20 +5,8 @@
http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
<named-query name="EntryStatus.unreadCounts"> <named-query name="Statuses.deleteOld">
<query>select s.subscription.id, count(s) from FeedEntryStatus s where s.user=:user and s.read=false group by s.subscription.id</query> <query>delete from FeedEntryStatus s where s.entryInserted &lt; :date and s.starred = false</query>
</named-query>
<named-query name="EntryStatus.existing">
<query>select new com.commafeed.backend.dao.FeedEntryDAO$EntryWithFeed(e, f) FROM FeedEntry e LEFT JOIN e.feedRelationships f WITH f.feed.id = :feedId where e.guidHash = :guidHash and e.url = :url</query>
</named-query>
<named-query name="EntryStatus.deleteByIds">
<query>delete from FeedEntryStatus s where s.id in :ids</query>
</named-query>
<named-query name="Feed.deleteEntryRelationships">
<query>delete from FeedFeedEntry ffe where ffe.feed.id = :feedId</query>
</named-query> </named-query>
</entity-mappings> </entity-mappings>

View File

@@ -346,4 +346,13 @@
<column name="subscription_id" /> <column name="subscription_id" />
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="athou" id="add-trim-status-setting">
<addColumn tableName="APPLICATIONSETTINGS">
<column name="keepStatusDays" type="INT" />
</addColumn>
<update tableName="APPLICATIONSETTINGS">
<column name="keepStatusDays" valueNumeric="0"></column>
</update>
</changeSet>
</databaseChangeLog> </databaseChangeLog>

View File

@@ -163,6 +163,14 @@
ng-model="settings.queryTimeout" /> ng-model="settings.queryTimeout" />
</div> </div>
</div> </div>
<div class="control-group">
<label class="control-label" for="keepStatusDays">Keep read status for (days)</label>
<div class="controls">
<input type="number" name="keepStatusDays" class="input-block-level"
ng-model="settings.keepStatusDays" />
<span class="help-inline">0 = keep forever</span>
</div>
</div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="crawlingPaused">Pause crawling</label> <label class="control-label" for="crawlingPaused">Pause crawling</label>
<div class="controls"> <div class="controls">

View File

@@ -79,7 +79,7 @@
<i ng-class="{'icon-star icon-star-yellow': entry.starred, 'icon-star-empty': !entry.starred}" <i ng-class="{'icon-star icon-star-yellow': entry.starred, 'icon-star-empty': !entry.starred}"
class="pointer"></i> class="pointer"></i>
</span> </span>
<label class="checkbox inline"> <label class="checkbox inline" ui-if="entry.markable">
<input type="checkbox" ng-checked="!entry.read" ng-click="mark(entry, !entry.read)" class="mousetrap"></input> <input type="checkbox" ng-checked="!entry.read" ng-click="mark(entry, !entry.read)" class="mousetrap"></input>
${view.keep_unread} ${view.keep_unread}
</label> </label>