exclude terms from search (fix #666)

This commit is contained in:
Athou
2014-11-07 10:53:49 +01:00
parent 9b2cdbbb18
commit 1c99929429
6 changed files with 82 additions and 29 deletions

View File

@@ -7,12 +7,13 @@ import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.hibernate.SessionFactory;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.FixedSizeSortedSet;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedEntryTag;
@@ -112,18 +113,21 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, statuses);
}
private HibernateQuery buildQuery(User user, FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset,
int limit, ReadingOrder order, Date last, String tag) {
private HibernateQuery buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords, Date newerThan,
int offset, int limit, ReadingOrder order, Date last, String tag) {
HibernateQuery query = newQuery().from(entry).where(entry.feed.eq(sub.getFeed()));
if (keywords != null) {
query.join(entry.content, content);
for (String keyword : StringUtils.split(keywords)) {
for (FeedEntryKeyword keyword : keywords) {
BooleanBuilder or = new BooleanBuilder();
or.or(content.content.containsIgnoreCase(keyword));
or.or(content.title.containsIgnoreCase(keyword));
or.or(content.content.containsIgnoreCase(keyword.getKeyword()));
or.or(content.title.containsIgnoreCase(keyword.getKeyword()));
if (keyword.getMode() == Mode.EXCLUDE) {
or.not();
}
query.where(or);
}
}
@@ -180,8 +184,9 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return query;
}
public List<FeedEntryStatus> findBySubscriptions(User user, List<FeedSubscription> subs, boolean unreadOnly, String keywords,
Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent, boolean onlyIds, String tag) {
public List<FeedEntryStatus> findBySubscriptions(User user, List<FeedSubscription> subs, boolean unreadOnly,
List<FeedEntryKeyword> keywords, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent,
boolean onlyIds, String tag) {
int capacity = offset + limit;
Comparator<FeedEntryStatus> comparator = order == ReadingOrder.desc ? STATUS_COMPARATOR_DESC : STATUS_COMPARATOR_ASC;
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator);

View File

@@ -0,0 +1,40 @@
package com.commafeed.backend.feed;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists;
/**
* A keyword used in a search query
*/
@Getter
@RequiredArgsConstructor
public class FeedEntryKeyword {
public static enum Mode {
INCLUDE, EXCLUDE;
}
private final String keyword;
private final Mode mode;
public static List<FeedEntryKeyword> fromQueryString(String keywords) {
List<FeedEntryKeyword> list = Lists.newArrayList();
if (keywords != null) {
for (String keyword : StringUtils.split(keywords)) {
boolean not = false;
if (keyword.startsWith("-") || keyword.startsWith("!")) {
not = true;
keyword = keyword.substring(1);
}
list.add(new FeedEntryKeyword(keyword, not ? Mode.EXCLUDE : Mode.INCLUDE));
}
}
return list;
}
}

View File

@@ -29,6 +29,7 @@ import org.mozilla.universalchardet.UniversalDetector;
import org.w3c.css.sac.InputSource;
import org.w3c.dom.css.CSSStyleDeclaration;
import com.commafeed.backend.feed.FeedEntryKeyword.Mode;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.frontend.model.Entry;
@@ -495,19 +496,20 @@ public class FeedUtils {
return rot13(new String(Base64.decodeBase64(code)));
}
public static void removeUnwantedFromSearch(List<Entry> entries, String keywords) {
if (StringUtils.isBlank(keywords)) {
return;
}
public static void removeUnwantedFromSearch(List<Entry> entries, List<FeedEntryKeyword> keywords) {
Iterator<Entry> it = entries.iterator();
while (it.hasNext()) {
Entry entry = it.next();
boolean keep = true;
for (String keyword : keywords.split(" ")) {
for (FeedEntryKeyword keyword : keywords) {
String title = Jsoup.parse(entry.getTitle()).text();
String content = Jsoup.parse(entry.getContent()).text();
if (!StringUtils.containsIgnoreCase(content, keyword) && !StringUtils.containsIgnoreCase(title, keyword)) {
boolean condition = !StringUtils.containsIgnoreCase(content, keyword.getKeyword())
&& !StringUtils.containsIgnoreCase(title, keyword.getKeyword());
if (keyword.getMode() == Mode.EXCLUDE) {
condition = !condition;
}
if (condition) {
keep = false;
break;
}
@@ -517,5 +519,4 @@ public class FeedUtils {
}
}
}
}

View File

@@ -12,6 +12,7 @@ import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedSubscription;
@@ -65,9 +66,9 @@ public class FeedEntryService {
feedEntryStatusDAO.saveOrUpdate(status);
}
public void markSubscriptionEntries(User user, List<FeedSubscription> subscriptions, Date olderThan, String keywords) {
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, keywords, null, -1, -1, null, false,
false, null);
public void markSubscriptionEntries(User user, List<FeedSubscription> subscriptions, Date olderThan, List<FeedEntryKeyword> keywords) {
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, keywords, null, -1, -1, null,
false, false, null);
markList(statuses, olderThan);
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
cache.invalidateUserRootCategory(user);

View File

@@ -36,6 +36,7 @@ import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedEntryStatus;
@@ -109,6 +110,7 @@ public class CategoryREST {
keywords = StringUtils.trimToNull(keywords);
Preconditions.checkArgument(keywords == null || StringUtils.length(keywords) >= 3);
List<FeedEntryKeyword> entryKeywords = FeedEntryKeyword.fromQueryString(keywords);
limit = Math.min(limit, 1000);
limit = Math.max(0, limit);
@@ -135,8 +137,8 @@ public class CategoryREST {
entries.setName(Optional.fromNullable(tag).or("All"));
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
removeExcludedSubscriptions(subs, excludedIds);
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, subs, unreadOnly, keywords, newerThanDate, offset,
limit + 1, order, true, onlyIds, tag);
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, subs, unreadOnly, entryKeywords, newerThanDate,
offset, limit + 1, order, true, onlyIds, tag);
for (FeedEntryStatus status : list) {
entries.getEntries().add(
@@ -158,7 +160,7 @@ public class CategoryREST {
List<FeedCategory> categories = feedCategoryDAO.findAllChildrenCategories(user, parent);
List<FeedSubscription> subs = feedSubscriptionDAO.findByCategories(user, categories);
removeExcludedSubscriptions(subs, excludedIds);
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, subs, unreadOnly, keywords, newerThanDate,
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, subs, unreadOnly, entryKeywords, newerThanDate,
offset, limit + 1, order, true, onlyIds, tag);
for (FeedEntryStatus status : list) {
@@ -180,7 +182,7 @@ public class CategoryREST {
entries.setTimestamp(System.currentTimeMillis());
entries.setIgnoredReadStatus(STARRED.equals(id) || keywords != null || tag != null);
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords);
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), entryKeywords);
return Response.ok(entries).build();
}
@@ -245,11 +247,12 @@ public class CategoryREST {
Date olderThan = req.getOlderThan() == null ? null : new Date(req.getOlderThan());
String keywords = req.getKeywords();
List<FeedEntryKeyword> entryKeywords = FeedEntryKeyword.fromQueryString(keywords);
if (ALL.equals(req.getId())) {
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
removeExcludedSubscriptions(subs, req.getExcludedSubscriptions());
feedEntryService.markSubscriptionEntries(user, subs, olderThan, keywords);
feedEntryService.markSubscriptionEntries(user, subs, olderThan, entryKeywords);
} else if (STARRED.equals(req.getId())) {
feedEntryService.markStarredEntries(user, olderThan);
} else {
@@ -257,7 +260,7 @@ public class CategoryREST {
List<FeedCategory> categories = feedCategoryDAO.findAllChildrenCategories(user, parent);
List<FeedSubscription> subs = feedSubscriptionDAO.findByCategories(user, categories);
removeExcludedSubscriptions(subs, req.getExcludedSubscriptions());
feedEntryService.markSubscriptionEntries(user, subs, olderThan, keywords);
feedEntryService.markSubscriptionEntries(user, subs, olderThan, entryKeywords);
}
return Response.ok().build();
}

View File

@@ -44,6 +44,7 @@ import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feed.FeedEntryKeyword;
import com.commafeed.backend.feed.FeedFetcher;
import com.commafeed.backend.feed.FeedQueues;
import com.commafeed.backend.feed.FeedUtils;
@@ -127,6 +128,7 @@ public class FeedREST {
keywords = StringUtils.trimToNull(keywords);
Preconditions.checkArgument(keywords == null || StringUtils.length(keywords) >= 3);
List<FeedEntryKeyword> entryKeywords = FeedEntryKeyword.fromQueryString(keywords);
limit = Math.min(limit, 1000);
limit = Math.max(0, limit);
@@ -146,8 +148,8 @@ public class FeedREST {
entries.setErrorCount(subscription.getFeed().getErrorCount());
entries.setFeedLink(subscription.getFeed().getLink());
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, Arrays.asList(subscription), unreadOnly, keywords,
newerThanDate, offset, limit + 1, order, true, onlyIds, null);
List<FeedEntryStatus> list = feedEntryStatusDAO.findBySubscriptions(user, Arrays.asList(subscription), unreadOnly,
entryKeywords, newerThanDate, offset, limit + 1, order, true, onlyIds, null);
for (FeedEntryStatus status : list) {
entries.getEntries().add(
@@ -166,7 +168,7 @@ public class FeedREST {
entries.setTimestamp(System.currentTimeMillis());
entries.setIgnoredReadStatus(keywords != null);
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords);
FeedUtils.removeUnwantedFromSearch(entries.getEntries(), entryKeywords);
return Response.ok(entries).build();
}
@@ -289,10 +291,11 @@ public class FeedREST {
Date olderThan = req.getOlderThan() == null ? null : new Date(req.getOlderThan());
String keywords = req.getKeywords();
List<FeedEntryKeyword> entryKeywords = FeedEntryKeyword.fromQueryString(keywords);
FeedSubscription subscription = feedSubscriptionDAO.findById(user, Long.valueOf(req.getId()));
if (subscription != null) {
feedEntryService.markSubscriptionEntries(user, Arrays.asList(subscription), olderThan, keywords);
feedEntryService.markSubscriptionEntries(user, Arrays.asList(subscription), olderThan, entryKeywords);
}
return Response.ok().build();
}