public void Search( ) { // Basic integration test: search algorithms are already extensively tested with InMemoryIndexBase MockRepository mocks = new MockRepository( ); IIndexConnector conn = mocks.StrictMock <IIndexConnector>( ); IWordFetcher fetcher = mocks.StrictMock <IWordFetcher>( ); Word dummy; Expect.Call(fetcher.TryGetWord("test", out dummy)).Return(false); Expect.Call(fetcher.TryGetWord("query", out dummy)).Return(false); fetcher.Dispose( ); LastCall.On(fetcher); Expect.Call(conn.GetWordFetcher( )).Return(fetcher); mocks.ReplayAll( ); SqlIndex index = new SqlIndex(conn); Assert.AreEqual(0, index.Search(new SearchParameters("test query")).Count, "Wrong search result count"); mocks.VerifyAll( ); }
/// <summary> /// Performs a search in the index. /// </summary> /// <param name="query">The search query.</param> /// <param name="documentTypeTags">The document type tags to include in the search.</param> /// <param name="filterDocumentType"><c>true</c> to apply the filter on the document type.</param> /// <param name="options">The search options.</param> /// <param name="fetcher">An object that is able to fetch words.</param> /// <returns>The results.</returns> /// <exception cref="ArgumentNullException">If <paramref name="query"/> or <paramref name="fetcher"/> are <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="query"/> is empty.</exception> /// <exception cref="ArgumentNullException">If <paramref name="filterDocumentType"/> is <c>true</c> and <paramref name="documentTypeTags"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="filterDocumentType"/> is <c>true</c> and <paramref name="documentTypeTags"/> is empty.</exception> public static SearchResultCollection SearchInternal(string query, string[] documentTypeTags, bool filterDocumentType, SearchOptions options, IWordFetcher fetcher) { if (query == null) { throw new ArgumentNullException("query"); } if (query.Length == 0) { throw new ArgumentException("Query cannot be empty", "query"); } if (filterDocumentType && documentTypeTags == null) { throw new ArgumentNullException("documentTypeTags"); } if (filterDocumentType && documentTypeTags.Length == 0) { throw new ArgumentException("documentTypeTags cannot be empty", "documentTypeTags"); } if (fetcher == null) { throw new ArgumentNullException("fetcher"); } SearchResultCollection results = new SearchResultCollection(); query = query.ToLowerInvariant(); string[] queryWords = query.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); float totalRelevance = 0; Word word = null; foreach (string q in queryWords) { if (fetcher.TryGetWord(q, out word)) { foreach (IDocument doc in word.Occurrences.Keys) { // Skip documents with excluded tags if (filterDocumentType && !IsDocumentTypeTagIncluded(doc.TypeTag, documentTypeTags)) { continue; } foreach (BasicWordInfo info in word.Occurrences[doc]) { // If a search result is already present, add a new match to it, // otherwise create a new search result object WordInfo mi = new WordInfo(q, info.FirstCharIndex, info.WordIndex, info.Location); SearchResult res = results.GetSearchResult(doc); if (res == null) { res = new SearchResult(doc); res.Relevance.SetValue(info.Location.RelativeRelevance); res.Matches.Add(mi); results.Add(res); } else { // Avoid adding duplicate matches (happens when query contains the same word multiple times) if (!res.Matches.ContainsOccurrence(mi.Text, mi.FirstCharIndex)) { res.Matches.Add(mi); } res.Relevance.SetValue(res.Relevance.Value + info.Location.RelativeRelevance); } totalRelevance += info.Location.RelativeRelevance; } } } } if (options == SearchOptions.AllWords) { totalRelevance -= PurgeResultsForAllWords(results, queryWords); } else if (options == SearchOptions.ExactPhrase) { totalRelevance -= PurgeResultsForExactPhrase(results, queryWords); } else if (options == SearchOptions.AtLeastOneWord) { // Nothing to do } else { throw new InvalidOperationException("Unsupported SearchOptions"); } // Finalize relevance values for (int i = 0; i < results.Count; i++) { results[i].Relevance.Finalize(totalRelevance); } return(results); }
/// <summary> /// Performs a search in the index. /// </summary> /// <param name="query">The search query.</param> /// <param name="documentTypeTags">The document type tags to include in the search.</param> /// <param name="filterDocumentType"><c>true</c> to apply the filter on the document type.</param> /// <param name="options">The search options.</param> /// <param name="fetcher">An object that is able to fetch words.</param> /// <returns>The results.</returns> /// <exception cref="ArgumentNullException">If <paramref name="query"/> or <paramref name="fetcher"/> are <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="query"/> is empty.</exception> /// <exception cref="ArgumentNullException">If <paramref name="filterDocumentType"/> is <c>true</c> and <paramref name="documentTypeTags"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="filterDocumentType"/> is <c>true</c> and <paramref name="documentTypeTags"/> is empty.</exception> public static SearchResultCollection SearchInternal(string query, string[] documentTypeTags, bool filterDocumentType, SearchOptions options, IWordFetcher fetcher) { if(query == null) throw new ArgumentNullException("query"); if(query.Length == 0) throw new ArgumentException("Query cannot be empty", "query"); if(filterDocumentType && documentTypeTags == null) throw new ArgumentNullException("documentTypeTags"); if(filterDocumentType && documentTypeTags.Length == 0) throw new ArgumentException("documentTypeTags cannot be empty", "documentTypeTags"); if(fetcher == null) throw new ArgumentNullException("fetcher"); SearchResultCollection results = new SearchResultCollection(); query = query.ToLowerInvariant(); string[] queryWords = query.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); float totalRelevance = 0; Word word = null; foreach(string q in queryWords) { if(fetcher.TryGetWord(q, out word)) { foreach(IDocument doc in word.Occurrences.Keys) { // Skip documents with excluded tags if(filterDocumentType && !IsDocumentTypeTagIncluded(doc.TypeTag, documentTypeTags)) continue; foreach(BasicWordInfo info in word.Occurrences[doc]) { // If a search result is already present, add a new match to it, // otherwise create a new search result object WordInfo mi = new WordInfo(q, info.FirstCharIndex, info.WordIndex, info.Location); SearchResult res = results.GetSearchResult(doc); if(res == null) { res = new SearchResult(doc); res.Relevance.SetValue(info.Location.RelativeRelevance); res.Matches.Add(mi); results.Add(res); } else { // Avoid adding duplicate matches (happens when query contains the same word multiple times) if(!res.Matches.ContainsOccurrence(mi.Text, mi.FirstCharIndex)) { res.Matches.Add(mi); } res.Relevance.SetValue(res.Relevance.Value + info.Location.RelativeRelevance); } totalRelevance += info.Location.RelativeRelevance; } } } } if(options == SearchOptions.AllWords) { totalRelevance -= PurgeResultsForAllWords(results, queryWords); } else if(options == SearchOptions.ExactPhrase) { totalRelevance -= PurgeResultsForExactPhrase(results, queryWords); } else if(options == SearchOptions.AtLeastOneWord) { // Nothing to do } else throw new InvalidOperationException("Unsupported SearchOptions"); // Finalize relevance values for(int i = 0; i < results.Count; i++) { results[i].Relevance.Finalize(totalRelevance); } return results; }