using System; using System.Collections.Generic; using System.Text; using ScrewTurn.Wiki.PluginFramework; using ScrewTurn.Wiki.SearchEngine; namespace ScrewTurn.Wiki { /// /// Implements tools for searching through the wiki. /// public static class SearchTools { /// /// Searches for pages with name or title similar to a specified value. /// /// The name to look for (null for the root). /// The namespace to search into. /// The similar pages, if any. public static PageInfo[] SearchSimilarPages(string name, string nspace) { if(string.IsNullOrEmpty(nspace)) nspace = null; SearchResultCollection searchResults = Search(name, false, false, SearchOptions.AtLeastOneWord); List result = new List(20); foreach(SearchResult res in searchResults) { PageDocument pageDoc = res.Document as PageDocument; if(pageDoc != null) { string pageNamespace = NameTools.GetNamespace(pageDoc.PageInfo.FullName); if(string.IsNullOrEmpty(pageNamespace)) pageNamespace = null; if(pageNamespace == nspace) { result.Add(pageDoc.PageInfo); } } } // Search page names for matches List allPages = Pages.GetPages(Pages.FindNamespace(nspace)); PageNameComparer comp = new PageNameComparer(); string currentName = name.ToLowerInvariant(); foreach(PageInfo page in allPages) { if(NameTools.GetLocalName(page.FullName).ToLowerInvariant().Contains(currentName)) { if(result.Find(delegate(PageInfo p) { return comp.Compare(p, page) == 0; }) == null) { result.Add(page); } } } return result.ToArray(); } /// /// Performs a search in the wiki. /// /// The search query. /// A value indicating whether to perform a full-text search. /// A value indicating whether to search through files and attachments. /// The search options. /// The results collection. public static SearchResultCollection Search(string query, bool fullText, bool searchFilesAndAttachments, SearchOptions options) { // First, search regular page content... List allCollections = new List(3); foreach(IPagesStorageProviderV30 prov in Collectors.PagesProviderCollector.AllProviders) { SearchResultCollection currentResults = prov.PerformSearch(new SearchParameters(query, options)); if(!fullText) { // All non title-related matches must be removed SearchResultCollection filteredResults = new SearchResultCollection(10); foreach(SearchResult res in currentResults) { foreach(WordInfo word in res.Matches) { if(word.Location == WordLocation.Title) { filteredResults.Add(res); break; } } } allCollections.Add(filteredResults); } else allCollections.Add(currentResults); } // ... normalize relevance based on the number of providers float providerNormalizationFactor = 1F / (float)Collectors.PagesProviderCollector.AllProviders.Length; foreach(SearchResultCollection coll in allCollections) { foreach(SearchResult result in coll) { result.Relevance.NormalizeAfterFinalization(providerNormalizationFactor); } } if(searchFilesAndAttachments) { // ... then build a temporary index for files and attachments... StandardIndex temporaryIndex = new StandardIndex(); uint tempDocumentId = 1; uint tempWordId = 1; temporaryIndex.IndexChanged += delegate(object sender, IndexChangedEventArgs e) { if(e.Change == IndexChangeType.DocumentAdded) { List ids = null; if(e.ChangeData.Words != null) { ids = new List(20); foreach(DumpedWord d in e.ChangeData.Words) { ids.Add(new WordId(d.Text, tempWordId)); tempWordId++; } } e.Result = new IndexStorerResult(tempDocumentId, ids); tempDocumentId++; } }; temporaryIndex.SetBuildDocumentDelegate(DetectFileOrAttachment); foreach(IFilesStorageProviderV30 prov in Collectors.FilesProviderCollector.AllProviders) { TraverseDirectories(temporaryIndex, prov, null); string[] pagesWithAttachments = prov.GetPagesWithAttachments(); foreach(string page in pagesWithAttachments) { // Store attachments for the current page in the index PageInfo pageInfo = Pages.FindPage(page); // pageInfo can be null if the index is corrupted if(pageInfo != null) { foreach(string attachment in prov.ListPageAttachments(pageInfo)) { FileDetails details = prov.GetPageAttachmentDetails(pageInfo, attachment); temporaryIndex.StoreDocument(new PageAttachmentDocument(pageInfo, attachment, prov.GetType().FullName, details.LastModified), new string[0], "", null); } } } } // ... then search in the temporary index and normalize relevance SearchResultCollection filesAndAttachments = temporaryIndex.Search(new SearchParameters(query, options)); providerNormalizationFactor = 1F / (float)Collectors.FilesProviderCollector.AllProviders.Length; foreach(SearchResult result in filesAndAttachments) { result.Relevance.NormalizeAfterFinalization(providerNormalizationFactor); } allCollections.Add(filesAndAttachments); } return CombineCollections(allCollections); } /// /// Detects the document in a dumped instance for files and attachments. /// /// The dumped document instance. /// The proper document instance. private static IDocument DetectFileOrAttachment(DumpedDocument doc) { if(doc.TypeTag == FileDocument.StandardTypeTag) { return new FileDocument(doc); } else if(doc.TypeTag == PageAttachmentDocument.StandardTypeTag) { return new PageAttachmentDocument(doc); } else throw new NotSupportedException(); } /// /// Traverses a directory tree, indexing all files. /// /// The output index. /// The provider. /// The current directory. private static void TraverseDirectories(InMemoryIndexBase index, IFilesStorageProviderV30 provider, string currentDir) { // Store files in the index foreach(string file in provider.ListFiles(currentDir)) { FileDetails details = provider.GetFileDetails(file); index.StoreDocument(new FileDocument(file, provider.GetType().FullName, details.LastModified), new string[0], "", null); } // Recursively process all sub-directories foreach(string directory in provider.ListDirectories(currentDir)) { TraverseDirectories(index, provider, directory); } } /// /// Combines a set of s into a single object. /// /// The collections. /// The resulting . private static SearchResultCollection CombineCollections(List collections) { List tempResults = new List(100); foreach(SearchResultCollection coll in collections) { tempResults.AddRange(coll); } tempResults.Sort(delegate(SearchResult x, SearchResult y) { return y.Relevance.Value.CompareTo(x.Relevance.Value); }); SearchResultCollection resultCollection = new SearchResultCollection(50); foreach(SearchResult singleResult in tempResults) { resultCollection.Add(singleResult); } return resultCollection; } } }