/// <summary> /// Search for NewsItems, that match a provided criteria collection within a optional search scope. /// </summary> /// <param name="criteria">SearchCriteriaCollection containing the defined search criteria</param> /// <param name="scope">Search scope: an array of NewsFeed</param> /// <param name="tag">optional object to be used by the caller to identify this search</param> /// <param name="cultureName">Name of the culture.</param> /// <param name="returnFullItemText">if set to <c>true</c>, full item texts are returned instead of the summery.</param> public void SearchNewsItems(SearchCriteriaCollection criteria, INewsFeed[] scope, object tag, string cultureName, bool returnFullItemText) { // if scope is an empty array: search all, else search only in spec. feeds int feedmatches = 0; int itemmatches = 0; IList <INewsItem> unreturnedMatchItems = new List <INewsItem>(); var fiList = new FeedInfoList(String.Empty); Exception ex; bool valid = SearchHandler.ValidateSearchCriteria(criteria, cultureName, out ex); if (ex != null) // report always any error (warnings) { // render the error in-line (search result): fiList.Add(FeedSource.CreateHelpNewsItemFromException(ex).FeedDetails); feedmatches = fiList.Count; unreturnedMatchItems = fiList.GetAllNewsItems(); itemmatches = unreturnedMatchItems.Count; } if (valid) { try { // do the search (using lucene): LuceneSearch.Result r = SearchHandler.ExecuteSearch(criteria, scope, this.Sources.Select(entry => entry.Source), cultureName); // we iterate r.ItemsMatched to build a // NewsItemIdentifier and ArrayList list with items, that // match the read status (if this was a search criteria) // then call FindNewsItems(NewsItemIdentifier[]) to get also // the FeedInfoList. // Raise ONE event, instead of two to return all (counters, lists) SearchCriteriaProperty criteriaProperty = null; foreach (ISearchCriteria sc in criteria) { criteriaProperty = sc as SearchCriteriaProperty; if (criteriaProperty != null && PropertyExpressionKind.Unread == criteriaProperty.WhatKind) { break; } } ItemReadState readState = ItemReadState.Ignore; if (criteriaProperty != null) { readState = criteriaProperty.BeenRead ? ItemReadState.BeenRead : ItemReadState.Unread; } if (r != null && r.ItemMatchCount > 0) { /* append results */ var nids = new SearchHitNewsItem[r.ItemsMatched.Count]; r.ItemsMatched.CopyTo(nids, 0); //look in every feed source to find source feed for matching news items IEnumerable <FeedInfoList> results = Sources.Select(entry => entry.Source.FindNewsItems(nids, readState, returnFullItemText)); foreach (FeedInfoList fil in results) { fiList.AddRange(fil); } feedmatches = fiList.Count; unreturnedMatchItems = fiList.GetAllNewsItems(); itemmatches = unreturnedMatchItems.Count; } } catch (Exception searchEx) { // render the error in-line (search result): fiList.Add(FeedSource.CreateHelpNewsItemFromException(searchEx).FeedDetails); feedmatches = fiList.Count; unreturnedMatchItems = fiList.GetAllNewsItems(); itemmatches = unreturnedMatchItems.Count; } } RaiseSearchFinishedEvent(tag, fiList, unreturnedMatchItems, feedmatches, itemmatches); }
/// <summary> /// Executes a search. /// </summary> /// <param name="criteria">The criteria.</param> /// <param name="scope">The scope.</param> /// <param name="feedSources">The news handlers.</param> /// <param name="cultureName">Name of the culture.</param> /// <returns></returns> public Result ExecuteSearch(SearchCriteriaCollection criteria, INewsFeed[] scope, IEnumerable <FeedSource> feedSources, string cultureName) { if (!UseIndex) { return(null); } Query q = BuildLuceneQuery(criteria, scope, GetAnalyzer(cultureName)); if (q == null) // not validated { return(new Result(0, 0, GetList <SearchHitNewsItem> .Empty, GetArrayList.Empty)); } //TODO: to be fixed - // next line causes issues with concurrent thread access to the search index: IndexSearcher searcher = new IndexSearcher(this._settings.GetIndexDirectory()); Hits hits = null; while (hits == null) { try { DateTime start = DateTime.Now; hits = searcher.Search(q, Sort.RELEVANCE); TimeSpan timeRequired = TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks); _log.Info(String.Format("Found {0} document(s) that matched query '{1}' (time required: {2})", hits.Length(), q, timeRequired)); } catch (BooleanQuery.TooManyClauses) { BooleanQuery.SetMaxClauseCount(BooleanQuery.GetMaxClauseCount() * 2); _log.Info(String.Format("Search failed with error 'BooleanQuery.TooManyClauses'. Retry with BooleanQuery.MaxClauseCount == {0}", BooleanQuery.GetMaxClauseCount())); } } List <SearchHitNewsItem> items = new List <SearchHitNewsItem>(hits.Length()); HybridDictionary matchedFeeds = new HybridDictionary(); for (int i = 0; i < hits.Length(); i++) { Document doc = hits.Doc(i); INewsFeed f = null; string feedLink = doc.Get(Keyword.FeedLink); if (matchedFeeds.Contains(feedLink)) { f = (INewsFeed)matchedFeeds[feedLink]; } if (f == null) { foreach (FeedSource h in feedSources) { if (h.IsSubscribed(feedLink)) { f = h.GetFeeds()[feedLink]; break; } } } if (f == null) { continue; } SearchHitNewsItem item = new SearchHitNewsItem(f, doc.Get(Keyword.ItemTitle), doc.Get(Keyword.ItemLink), doc.Get(IndexDocument.ItemSummary), doc.Get(Keyword.ItemAuthor), new DateTime(DateTools.StringToTime(doc.Get(Keyword.ItemDate))), LuceneNewsItemSearch.NewsItemIDFromUID(doc.Get(IndexDocument.ItemID))); items.Add(item); if (!matchedFeeds.Contains(feedLink)) { matchedFeeds.Add(feedLink, f); } } return(new Result(items.Count, matchedFeeds.Count, items, new ArrayList(matchedFeeds.Values))); }