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;
}
}
}