using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Examine; using Examine.LuceneEngine; using Examine.LuceneEngine.Providers; using Examine.Providers; using Lucene.Net.Search; using Umbraco.Core; using Umbraco.Web.Search; using Umbraco.Web.WebApi; namespace Umbraco.Web.WebServices { public class ExamineManagementApiController : UmbracoAuthorizedApiController { /// /// Get the details for indexers /// /// public IEnumerable GetIndexerDetails() { return ExamineManager.Instance.IndexProviderCollection.Select(CreateModel); } /// /// Get the details for searchers /// /// public IEnumerable GetSearcherDetails() { var model = new List( ExamineManager.Instance.SearchProviderCollection.Cast().Select(searcher => { var indexerModel = new ExamineIndexerModel() { Name = searcher.Name }; var props = TypeHelper.CachedDiscoverableProperties(searcher.GetType(), mustWrite: false) //ignore these properties .Where(x => !new[] { "Description" }.InvariantContains(x.Name)) .OrderBy(x => x.Name); foreach (var p in props) { indexerModel.ProviderProperties.Add(p.Name, p.GetValue(searcher, null).ToString()); } return indexerModel; })); return model; } public ISearchResults GetSearchResults(string searcherName, string query, string queryType) { if (queryType == null) throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)); if (query.IsNullOrWhiteSpace()) return SearchResults.Empty(); LuceneSearcher searcher; var msg = ValidateLuceneSearcher(searcherName, out searcher); if (msg.IsSuccessStatusCode) { if (queryType.InvariantEquals("text")) { return searcher.Search(query, false); } if (queryType.InvariantEquals("lucene")) { return searcher.Search(searcher.CreateSearchCriteria().RawQuery(query)); } throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)); } throw new HttpResponseException(msg); } /// /// Optimizes an index /// public HttpResponseMessage PostOptimizeIndex(string indexerName) { LuceneIndexer indexer; var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { try { indexer.OptimizeIndex(); } catch (System.Exception ex) { return new HttpResponseMessage(HttpStatusCode.Conflict) { Content = new StringContent(string.Format("The index could not be optimized, most likely there is another thread currently writing to the index. Error: {0}", ex)), ReasonPhrase = "Could Not Optimize" }; } } return msg; } /// /// Rebuilds the index /// /// /// public HttpResponseMessage PostRebuildIndex(string indexerName) { LuceneIndexer indexer; var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { try { indexer.RebuildIndex(); } catch (System.Exception ex) { return new HttpResponseMessage(HttpStatusCode.Conflict) { Content = new StringContent(string.Format("The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {0}", ex)), ReasonPhrase = "Could Not Rebuild" }; } } return msg; } /// /// Check if the index has been rebuilt /// /// /// /// /// This is kind of rudementary since there's no way we can know that the index has rebuilt, we'll just check /// if the index is locked based on Lucene apis /// public ExamineIndexerModel PostCheckRebuildIndex(string indexerName) { LuceneIndexer indexer; var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { var isLocked = indexer.IsIndexLocked(); return isLocked ? null : CreateModel(indexer); } throw new HttpResponseException(msg); } /// /// Checks if the index is optimized /// /// /// public ExamineIndexerModel PostCheckOptimizeIndex(string indexerName) { LuceneIndexer indexer; var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { var isOptimized = indexer.IsIndexOptimized(); return !isOptimized ? null : CreateModel(indexer); } throw new HttpResponseException(msg); } private ExamineIndexerModel CreateModel(BaseIndexProvider indexer) { var indexerModel = new ExamineIndexerModel() { IndexCriteria = indexer.IndexerData, Name = indexer.Name }; var props = TypeHelper.CachedDiscoverableProperties(indexer.GetType(), mustWrite: false) //ignore these properties .Where(x => !new[] { "IndexerData", "Description", "WorkingFolder" }.InvariantContains(x.Name)) .OrderBy(x => x.Name); foreach (var p in props) { indexerModel.ProviderProperties.Add(p.Name, p.GetValue(indexer, null).ToString()); } var luceneIndexer = indexer as LuceneIndexer; if (luceneIndexer != null && luceneIndexer.IndexExists()) { indexerModel.IsLuceneIndex = true; indexerModel.DocumentCount = luceneIndexer.GetIndexDocumentCount(); indexerModel.FieldCount = luceneIndexer.GetIndexDocumentCount(); indexerModel.IsOptimized = luceneIndexer.IsIndexOptimized(); indexerModel.DeletionCount = luceneIndexer.GetDeletedDocumentsCount(); } return indexerModel; } private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher) { if (ExamineManager.Instance.SearchProviderCollection.Cast().Any(x => x.Name == searcherName)) { searcher = ExamineManager.Instance.SearchProviderCollection[searcherName] as LuceneSearcher; if (searcher == null) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(string.Format("The searcher {0} is not of type {1}", searcherName, typeof(LuceneSearcher))), ReasonPhrase = "Wrong Searcher Type" }; } //return Ok! return Request.CreateResponse(HttpStatusCode.OK); } searcher = null; return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(string.Format("No searcher found with name = {0}", searcherName)), ReasonPhrase = "Searcher Not Found" }; } private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out LuceneIndexer indexer) { if (ExamineManager.Instance.IndexProviderCollection.Any(x => x.Name == indexerName)) { indexer = ExamineManager.Instance.IndexProviderCollection[indexerName] as LuceneIndexer; if (indexer == null) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(string.Format("The indexer {0} is not of type {1}", indexerName, typeof(LuceneIndexer))), ReasonPhrase = "Wrong Indexer Type" }; } //return Ok! return Request.CreateResponse(HttpStatusCode.OK); } indexer = null; return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(string.Format("No indexer found with name = {0}", indexerName)), ReasonPhrase = "Indexer Not Found" }; } } }