diff --git a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java index e6f615e7..cc3d50b5 100644 --- a/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java +++ b/src/main/java/com/commafeed/backend/dao/FeedEntryStatusDAO.java @@ -14,6 +14,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.ObjectUtils; import org.hibernate.Criteria; import org.hibernate.criterion.Disjunction; @@ -131,7 +132,7 @@ public class FeedEntryStatusDAO extends GenericDAO { if (keywords != null) { Criteria contentJoin = criteria.createCriteria(FeedEntry_.content.getName(), "content", JoinType.INNER_JOIN); - for (String keyword : keywords.split(" ")) { + for (String keyword : StringUtils.split(keywords)) { Disjunction or = Restrictions.disjunction(); or.add(Restrictions.ilike(FeedEntryContent_.content.getName(), keyword, MatchMode.ANYWHERE)); or.add(Restrictions.ilike(FeedEntryContent_.title.getName(), keyword, MatchMode.ANYWHERE)); diff --git a/src/main/java/com/commafeed/backend/feeds/FeedUtils.java b/src/main/java/com/commafeed/backend/feeds/FeedUtils.java index 05928970..e24ca314 100644 --- a/src/main/java/com/commafeed/backend/feeds/FeedUtils.java +++ b/src/main/java/com/commafeed/backend/feeds/FeedUtils.java @@ -5,6 +5,7 @@ import java.io.StringReader; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; @@ -29,6 +30,7 @@ import org.w3c.dom.css.CSSStyleDeclaration; import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedSubscription; +import com.commafeed.frontend.model.Entry; import com.google.common.collect.Lists; import com.google.gwt.i18n.client.HasDirection.Direction; import com.google.gwt.i18n.shared.BidiUtils; @@ -447,4 +449,27 @@ public class FeedUtils { return rot13(new String(Base64.decodeBase64(code))); } + public static void removeUnwantedFromSearch(List entries, String keywords) { + if (StringUtils.isBlank(keywords)) { + return; + } + + Iterator it = entries.iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + boolean keep = true; + for (String keyword : keywords.split(" ")) { + String title = Jsoup.parse(entry.getTitle()).text(); + String content = Jsoup.parse(entry.getContent()).text(); + if (!StringUtils.containsIgnoreCase(content, keyword) && !StringUtils.containsIgnoreCase(title, keyword)) { + keep = false; + break; + } + } + if (!keep) { + it.remove(); + } + } + } + } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java index 331434dd..d4b0d045 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/CategoryREST.java @@ -27,6 +27,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.feeds.FeedUtils; import com.commafeed.backend.model.FeedCategory; import com.commafeed.backend.model.FeedEntryStatus; import com.commafeed.backend.model.FeedSubscription; @@ -84,7 +85,7 @@ public class CategoryREST extends AbstractREST { @Inject CacheService cache; - + @Inject ApplicationSettingsService applicationSettingsService; @@ -103,9 +104,15 @@ public class CategoryREST extends AbstractREST { @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam( value = "limit for paging, default 20, maximum 50") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam( value = "date ordering", - allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order) { + allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order, @ApiParam( + value = "keywords separated by spaces, 3 characters minimum", + required = true) @QueryParam("keywords") String keywords) { Preconditions.checkNotNull(readType); + + keywords = StringUtils.trimToNull(keywords); + Preconditions.checkArgument(keywords == null || StringUtils.length(keywords) >= 3); + limit = Math.min(limit, 50); limit = Math.max(0, limit); @@ -122,7 +129,7 @@ public class CategoryREST extends AbstractREST { if (ALL.equals(id)) { entries.setName("All"); List subscriptions = feedSubscriptionDAO.findAll(getUser()); - List list = feedEntryStatusDAO.findBySubscriptions(subscriptions, unreadOnly, null, newerThanDate, offset, + List list = feedEntryStatusDAO.findBySubscriptions(subscriptions, unreadOnly, keywords, newerThanDate, offset, limit + 1, order, true); for (FeedEntryStatus status : list) { entries.getEntries().add( @@ -143,7 +150,7 @@ public class CategoryREST extends AbstractREST { if (parent != null) { List categories = feedCategoryDAO.findAllChildrenCategories(getUser(), parent); List subs = feedSubscriptionDAO.findByCategories(getUser(), categories); - List list = feedEntryStatusDAO.findBySubscriptions(subs, unreadOnly, null, newerThanDate, offset, + List list = feedEntryStatusDAO.findBySubscriptions(subs, unreadOnly, keywords, newerThanDate, offset, limit + 1, order, true); for (FeedEntryStatus status : list) { entries.getEntries().add( @@ -162,6 +169,7 @@ public class CategoryREST extends AbstractREST { } entries.setTimestamp(System.currentTimeMillis()); + FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords); return Response.ok(entries).build(); } @@ -180,7 +188,7 @@ public class CategoryREST extends AbstractREST { int offset = 0; int limit = 20; - Entries entries = (Entries) getCategoryEntries(id, readType, null, offset, limit, order).getEntity(); + Entries entries = (Entries) getCategoryEntries(id, readType, null, offset, limit, order, null).getEntity(); SyndFeed feed = new SyndFeedImpl(); feed.setFeedType("rss_2.0"); diff --git a/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java b/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java index 84145c84..f31d253e 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/EntryREST.java @@ -1,29 +1,15 @@ package com.commafeed.frontend.rest.resources; -import java.util.Iterator; -import java.util.List; - import javax.inject.Inject; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.apache.commons.lang.StringUtils; -import org.jsoup.Jsoup; - import com.commafeed.backend.dao.FeedEntryStatusDAO; import com.commafeed.backend.dao.FeedSubscriptionDAO; -import com.commafeed.backend.model.FeedEntryStatus; -import com.commafeed.backend.model.FeedSubscription; -import com.commafeed.backend.model.UserSettings.ReadingOrder; import com.commafeed.backend.services.ApplicationSettingsService; import com.commafeed.backend.services.FeedEntryService; -import com.commafeed.frontend.model.Entries; -import com.commafeed.frontend.model.Entry; import com.commafeed.frontend.model.request.MarkRequest; import com.commafeed.frontend.model.request.MultipleMarkRequest; import com.commafeed.frontend.model.request.StarRequest; @@ -87,63 +73,6 @@ public class EntryREST extends AbstractREST { return Response.ok(Status.OK).build(); } - @Path("/search") - @GET - @ApiOperation( - value = "Search for entries", - notes = "Look through title and content of entries by keywords", - responseClass = "com.commafeed.frontend.model.Entries") - public Response searchEntries( - @ApiParam(value = "keywords separated by spaces, 3 characters minimum", required = true) @QueryParam("keywords") String keywords, - @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam( - value = "limit for paging") @DefaultValue("-1") @QueryParam("limit") int limit) { - keywords = StringUtils.trimToEmpty(keywords); - limit = Math.min(limit, 50); - Preconditions.checkArgument(StringUtils.length(keywords) >= 3); - Entries entries = new Entries(); - entries.setOffset(offset); - entries.setLimit(limit); - - List subs = feedSubscriptionDAO.findAll(getUser()); - List entriesStatus = feedEntryStatusDAO.findBySubscriptions(subs, false, keywords, null, offset, limit + 1, - ReadingOrder.desc, true); - for (FeedEntryStatus status : entriesStatus) { - entries.getEntries().add( - Entry.build(status, applicationSettingsService.get().getPublicUrl(), applicationSettingsService.get() - .isImageProxyEnabled())); - } - - boolean hasMore = entries.getEntries().size() > limit; - if (hasMore) { - entries.setHasMore(true); - entries.getEntries().remove(entries.getEntries().size() - 1); - } - - removeUnwanted(entries.getEntries(), keywords); - - entries.setName("Search for : " + keywords); - entries.setTimestamp(System.currentTimeMillis()); - return Response.ok(entries).build(); - } - - private void removeUnwanted(List entries, String keywords) { - Iterator it = entries.iterator(); - while (it.hasNext()) { - Entry entry = it.next(); - boolean keep = true; - for (String keyword : keywords.split(" ")) { - String title = Jsoup.parse(entry.getTitle()).text(); - String content = Jsoup.parse(entry.getContent()).text(); - if (!StringUtils.containsIgnoreCase(content, keyword) && !StringUtils.containsIgnoreCase(title, keyword)) { - keep = false; - break; - } - } - if (!keep) { - it.remove(); - } - } - } } diff --git a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java index 2868bce9..a5be05ef 100644 --- a/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java +++ b/src/main/java/com/commafeed/frontend/rest/resources/FeedREST.java @@ -125,7 +125,7 @@ public class FeedREST extends AbstractREST { @Context private HttpServletRequest request; - + @Inject ApplicationSettingsService applicationSettingsService; @@ -140,10 +140,15 @@ public class FeedREST extends AbstractREST { @ApiParam(value = "offset for paging") @DefaultValue("0") @QueryParam("offset") int offset, @ApiParam( value = "limit for paging, default 20, maximum 50") @DefaultValue("20") @QueryParam("limit") int limit, @ApiParam( value = "date ordering", - allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order) { + allowableValues = "asc,desc") @QueryParam("order") @DefaultValue("desc") ReadingOrder order, @ApiParam( + value = "keywords separated by spaces, 3 characters minimum", + required = true) @QueryParam("keywords") String keywords) { Preconditions.checkNotNull(id); Preconditions.checkNotNull(readType); + + keywords = StringUtils.trimToNull(keywords); + Preconditions.checkArgument(keywords == null || StringUtils.length(keywords) >= 3); limit = Math.min(limit, 50); limit = Math.max(0, limit); @@ -163,7 +168,7 @@ public class FeedREST extends AbstractREST { entries.setErrorCount(subscription.getFeed().getErrorCount()); entries.setFeedLink(subscription.getFeed().getLink()); - List list = feedEntryStatusDAO.findBySubscriptions(Arrays.asList(subscription), unreadOnly, null, + List list = feedEntryStatusDAO.findBySubscriptions(Arrays.asList(subscription), unreadOnly, keywords, newerThanDate, offset, limit + 1, order, true); for (FeedEntryStatus status : list) { @@ -180,6 +185,7 @@ public class FeedREST extends AbstractREST { } entries.setTimestamp(System.currentTimeMillis()); + FeedUtils.removeUnwantedFromSearch(entries.getEntries(), keywords); return Response.ok(entries).build(); } @@ -197,7 +203,7 @@ public class FeedREST extends AbstractREST { int offset = 0; int limit = 20; - Entries entries = (Entries) getFeedEntries(id, readType, null, offset, limit, order).getEntity(); + Entries entries = (Entries) getFeedEntries(id, readType, null, offset, limit, order, null).getEntity(); SyndFeed feed = new SyndFeedImpl(); feed.setFeedType("rss_2.0"); diff --git a/src/main/webapp/js/controllers.js b/src/main/webapp/js/controllers.js index 69b9cd86..ca2a2f13 100644 --- a/src/main/webapp/js/controllers.js +++ b/src/main/webapp/js/controllers.js @@ -21,6 +21,10 @@ module.run(['$rootScope', function($rootScope) { // args.all $rootScope.$broadcast('reload', args || {}); }); + $rootScope.$on('emitEntrySearch', function(event, args) { + // args.keywords + $rootScope.$broadcast('entrySearch', args); + }); $rootScope.$on('emitFeedSearch', function(event, args) { $rootScope.$broadcast('feedSearch'); }); @@ -440,6 +444,7 @@ module.controller('ToolbarCtrl', [ return ($http.pendingRequests.length + $.active); } + $scope.keywords = $location.search().q; $scope.session = ProfileService.get(); $scope.ServerService = ServerService.get(); $scope.settingsService = SettingsService; @@ -509,15 +514,11 @@ module.controller('ToolbarCtrl', [ markAll(new Date().getTime() - 1209600000); }; - $scope.keywords = $stateParams._keywords; $scope.search = function() { - if ($scope.keywords == $stateParams._keywords) { - $scope.refresh(); - } else { - $state.transitionTo('feeds.search', { - _keywords : $scope.keywords - }); - } + $location.search('q', $scope.keywords); + $scope.$emit('emitEntrySearch', { + keywords : $scope.keywords + }); }; $scope.showButtons = function() { return !$stateParams._keywords; @@ -664,19 +665,21 @@ module.controller('FeedListCtrl', [ '$route', '$state', '$window', + '$location', 'EntryService', 'SettingsService', 'FeedService', 'CategoryService', 'AnalyticsService', - function($scope, $stateParams, $http, $route, $state, $window, EntryService, SettingsService, FeedService, CategoryService, + function($scope, $stateParams, $http, $route, $state, $window, $location, EntryService, SettingsService, FeedService, CategoryService, AnalyticsService) { AnalyticsService.track(); + $scope.keywords = $location.search().q; + $scope.selectedType = $stateParams._type; $scope.selectedId = $stateParams._id; - $scope.keywords = $stateParams._keywords; $scope.name = null; $scope.message = null; @@ -738,22 +741,16 @@ module.controller('FeedListCtrl', [ $scope.hasMore = data.hasMore; $scope.feedLink = data.feedLink; }; - if (!$scope.keywords) { - var service = $scope.selectedType == 'feed' ? FeedService : CategoryService; - service.entries({ - id : $scope.selectedId, - readType : $scope.settingsService.settings.readingMode, - order : $scope.settingsService.settings.readingOrder, - offset : offset, - limit : limit - }, callback); - } else { - EntryService.search({ - keywords : $scope.keywords, - offset : $scope.entries.length, - limit : limit - }, callback); - } + + var service = $scope.selectedType == 'feed' ? FeedService : CategoryService; + service.entries({ + id : $scope.selectedId, + readType : $scope.keywords ? 'all' : $scope.settingsService.settings.readingMode, + order : $scope.settingsService.settings.readingOrder, + offset : offset, + limit : limit, + keywords : $scope.keywords + }, callback); }; $scope.goToFeed = function(id) { @@ -1152,7 +1149,9 @@ module.controller('FeedListCtrl', [ $scope.markAll(args.olderThan); }); - $scope.$on('reload', function(event, args) { + var reload = function(all, keywords) { + $scope.keywords = keywords; + $location.search('q', keywords); delete $scope.current; $scope.name = null; $scope.entries = []; @@ -1163,13 +1162,21 @@ module.controller('FeedListCtrl', [ $scope.hasMore = true; $scope.loadMoreEntries(); - if (args.all) { + if (all) { FeedService.refreshAll(); } else if ($scope.selectedType == 'feed') { FeedService.refresh({ id : $stateParams._id }); } + }; + + $scope.$on('entrySearch', function(event, args) { + reload(null, args.keywords); + }); + + $scope.$on('reload', function(event, args) { + reload(args.all, null); }); }]);