mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
Implemented RebuildIndex plus UpdateIndex signal to backgroung tasks. Adapted UI.
--HG-- branch : dev
This commit is contained in:
@@ -58,7 +58,11 @@ namespace Orchard.Tests.Indexing {
|
||||
[Test]
|
||||
public void IndexProviderShouldOverwriteAlreadyExistingIndex() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", null));
|
||||
Assert.That(_provider.IsEmpty("default"), Is.False);
|
||||
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -193,5 +197,24 @@ namespace Orchard.Tests.Indexing {
|
||||
_provider.SetLastIndexUtc("default", new DateTime(1901, 1, 1, 1, 1, 1, 1));
|
||||
Assert.That(_provider.GetLastIndexUtc("default"), Is.EqualTo(DefaultIndexProvider.DefaultMinDateTime));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeTrueForNoneExistingIndexes() {
|
||||
_provider.IsEmpty("dummy");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeTrueForJustNewIndexes() {
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(_provider.IsEmpty("default"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyShouldBeFalseWhenThereIsADocument() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", null));
|
||||
Assert.That(_provider.IsEmpty("default"), Is.False);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -70,6 +70,21 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
return new DirectoryInfo(_appDataFolder.MapPath(Path.Combine(_basePath, indexName))).Exists;
|
||||
}
|
||||
|
||||
public bool IsEmpty(string indexName) {
|
||||
if(!Exists(indexName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), true);
|
||||
|
||||
try {
|
||||
return reader.NumDocs() == 0;
|
||||
}
|
||||
finally {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateIndex(string indexName) {
|
||||
var writer = new IndexWriter(GetDirectory(indexName), _analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
|
||||
writer.Close();
|
||||
@@ -118,7 +133,6 @@ namespace Orchard.Core.Indexing.Lucene {
|
||||
writer.Optimize();
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Delete(string indexName, int documentId) {
|
||||
|
@@ -10,47 +10,104 @@ using Orchard.Logging;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks;
|
||||
using Orchard.Core.Indexing.Models;
|
||||
using Orchard.Tasks.Indexing;
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Services {
|
||||
/// <summary>
|
||||
/// Contains the logic which is regularly executed to retrieve index information from multiple content handlers.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class IndexingTaskExecutor : IBackgroundTask {
|
||||
public class IndexingTaskExecutor : IBackgroundTask, IIndexNotifierHandler {
|
||||
private readonly IClock _clock;
|
||||
private readonly IRepository<IndexingTaskRecord> _repository;
|
||||
private readonly IEnumerable<IContentHandler> _handlers;
|
||||
private IIndexProvider _indexProvider;
|
||||
private readonly IIndexManager _indexManager;
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string SearchIndexName = "Search";
|
||||
|
||||
private readonly object _synLock = new object();
|
||||
|
||||
public IndexingTaskExecutor(
|
||||
IClock clock,
|
||||
IRepository<IndexingTaskRecord> repository,
|
||||
IEnumerable<IContentHandler> handlers,
|
||||
IIndexManager indexManager,
|
||||
IIndexingTaskManager indexingTaskManager,
|
||||
IContentManager contentManager) {
|
||||
_clock = clock;
|
||||
_repository = repository;
|
||||
_indexManager = indexManager;
|
||||
_handlers = handlers;
|
||||
_indexingTaskManager = indexingTaskManager;
|
||||
_contentManager = contentManager;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void UpdateIndex(string indexName) {
|
||||
if (indexName == SearchIndexName) {
|
||||
Sweep();
|
||||
}
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
|
||||
if ( !System.Threading.Monitor.TryEnter(_synLock) ) {
|
||||
Logger.Information("Index was requested but was already running");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (!_indexManager.HasIndexProvider()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_indexProvider = _indexManager.GetSearchIndexProvider();
|
||||
var updateIndexDocuments = new List<IIndexDocument>();
|
||||
var lastIndexing = DateTime.UtcNow;
|
||||
|
||||
// Do we need to rebuild the full index (first time module is used, or rebuild index requested) ?
|
||||
if (_indexProvider.IsEmpty(SearchIndexName)) {
|
||||
Logger.Information("Rebuild index started");
|
||||
|
||||
// mark current last task, as we should process older ones (in case of rebuild index only)
|
||||
lastIndexing = _indexingTaskManager.GetLastTaskDateTime();
|
||||
|
||||
// get every existing content item to index it
|
||||
foreach (var contentItem in _contentManager.Query(VersionOptions.Published).List()) {
|
||||
try {
|
||||
var context = new IndexContentContext {
|
||||
ContentItem = contentItem,
|
||||
IndexDocument = _indexProvider.New(contentItem.Id)
|
||||
};
|
||||
|
||||
// dispatch to handlers to retrieve index information
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexing(context);
|
||||
}
|
||||
|
||||
updateIndexDocuments.Add(context.IndexDocument);
|
||||
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexed(context);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to index content item #{0} during rebuild", contentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// retrieve last processed index time
|
||||
var lastIndexing = _indexProvider.GetLastIndexUtc(SearchIndexName);
|
||||
lastIndexing = _indexProvider.GetLastIndexUtc(SearchIndexName);
|
||||
}
|
||||
|
||||
_indexProvider.SetLastIndexUtc(SearchIndexName, _clock.UtcNow);
|
||||
|
||||
// retrieve not yet processed tasks
|
||||
@@ -66,8 +123,6 @@ namespace Orchard.Core.Indexing.Services {
|
||||
_indexProvider.CreateIndex(SearchIndexName);
|
||||
}
|
||||
|
||||
var updateIndexDocuments = new List<IIndexDocument>();
|
||||
|
||||
// process Delete tasks
|
||||
try {
|
||||
_indexProvider.Delete(SearchIndexName, taskRecords.Where(t => t.Action == IndexingTaskRecord.Delete).Select(t => t.Id));
|
||||
@@ -111,5 +166,9 @@ namespace Orchard.Core.Indexing.Services {
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(_synLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -59,12 +59,8 @@ namespace Orchard.Core.Indexing.Services {
|
||||
Logger.Information("Deleting index task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
}
|
||||
|
||||
public IEnumerable<IIndexingTask> GetTasks(DateTime? createdAfter) {
|
||||
return _repository
|
||||
.Fetch(x => x.CreatedUtc > createdAfter)
|
||||
.Select(x => new IndexingTask(_contentManager, x))
|
||||
.Cast<IIndexingTask>()
|
||||
.ToReadOnlyCollection();
|
||||
public DateTime GetLastTaskDateTime() {
|
||||
return _repository.Table.Max(t => t.CreatedUtc) ?? DateTime.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Web.Mvc;
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Search.Services;
|
||||
using Orchard.Search.ViewModels;
|
||||
@@ -18,7 +19,7 @@ namespace Orchard.Search.Controllers {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index() {
|
||||
var viewModel = new SearchIndexViewModel {HasIndexToManage = _searchService.HasIndexToManage};
|
||||
var viewModel = new SearchIndexViewModel {HasIndexToManage = _searchService.HasIndexToManage, IndexUpdatedUtc = _searchService.GetIndexUpdatedUtc()};
|
||||
|
||||
if (!viewModel.HasIndexToManage)
|
||||
Services.Notifier.Information(T("There is not search index to manage for this site."));
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Search.Services {
|
||||
@@ -7,5 +8,6 @@ namespace Orchard.Search.Services {
|
||||
IEnumerable<ISearchHit> Query(string term);
|
||||
void RebuildIndex();
|
||||
void UpdateIndex();
|
||||
DateTime GetIndexUpdatedUtc();
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Localization;
|
||||
@@ -8,12 +9,14 @@ namespace Orchard.Search.Services
|
||||
{
|
||||
public class SearchService : ISearchService
|
||||
{
|
||||
private const string SearchIndexName = "search";
|
||||
private const string SearchIndexName = "Search";
|
||||
private readonly IIndexManager _indexManager;
|
||||
private readonly IEnumerable<IIndexNotifierHandler> _indexNotifierHandlers;
|
||||
|
||||
public SearchService(IOrchardServices services, IIndexManager indexManager) {
|
||||
public SearchService(IOrchardServices services, IIndexManager indexManager, IEnumerable<IIndexNotifierHandler> indexNotifierHandlers) {
|
||||
Services = services;
|
||||
_indexManager = indexManager;
|
||||
_indexNotifierHandlers = indexNotifierHandlers;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
@@ -49,10 +52,20 @@ namespace Orchard.Search.Services
|
||||
}
|
||||
|
||||
public void UpdateIndex() {
|
||||
//todo: this
|
||||
//if (_indexManager.HasIndexProvider())
|
||||
// _indexManager.GetSearchIndexProvider().UpdateIndex(SearchIndexName);
|
||||
|
||||
foreach(var handler in _indexNotifierHandlers) {
|
||||
handler.UpdateIndex(SearchIndexName);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(T("The search index has been updated."));
|
||||
}
|
||||
|
||||
public DateTime GetIndexUpdatedUtc() {
|
||||
if(!HasIndexToManage) {
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
return _indexManager.GetSearchIndexProvider().GetLastIndexUtc(SearchIndexName);
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ using (Html.BeginForm("update", "admin", FormMethod.Post, new {area = "Orchard.S
|
||||
}
|
||||
using (Html.BeginForm("rebuild", "admin", FormMethod.Post, new {area = "Orchard.Search"})) { %>
|
||||
<fieldset>
|
||||
<p><%=T("Rebuild the search index for a fresh start. <button type=\"submit\" title=\"Rebuild the search index.\">Rebuld</button>") %></p>
|
||||
<p><%=T("Rebuild the search index for a fresh start. <button type=\"submit\" title=\"Rebuild the search index.\">Rebuild</button>") %></p>
|
||||
<%=Html.AntiForgeryTokenOrchard() %>
|
||||
</fieldset><%
|
||||
} %>
|
5
src/Orchard/Indexing/IIndexNotifierHandler.cs
Normal file
5
src/Orchard/Indexing/IIndexNotifierHandler.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.Indexing {
|
||||
public interface IIndexNotifierHandler : IEvents {
|
||||
void UpdateIndex(string indexName);
|
||||
}
|
||||
}
|
@@ -18,6 +18,11 @@ namespace Orchard.Indexing {
|
||||
/// </summary>
|
||||
void DeleteIndex(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Whether an index is empty or not
|
||||
/// </summary>
|
||||
bool IsEmpty(string indexName);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty document
|
||||
/// </summary>
|
||||
|
@@ -349,6 +349,7 @@
|
||||
<Compile Include="Environment\State\IShellStateManager.cs" />
|
||||
<Compile Include="Environment\State\ShellStateCoordinator.cs" />
|
||||
<Compile Include="IDependency.cs" />
|
||||
<Compile Include="Indexing\IIndexNotifierHandler.cs" />
|
||||
<Compile Include="Localization\Services\DefaultCultureManager.cs" />
|
||||
<Compile Include="Localization\Services\DefaultResourceManager.cs" />
|
||||
<Compile Include="Indexing\DefaultIndexManager.cs" />
|
||||
|
@@ -15,9 +15,9 @@ namespace Orchard.Tasks.Indexing {
|
||||
void CreateDeleteIndexTask(ContentItem contentItem);
|
||||
|
||||
/// <summary>
|
||||
/// Loads all indexing tasks created after to a specific date and time
|
||||
/// Returns the Date Time of the last task created
|
||||
/// </summary>
|
||||
IEnumerable<IIndexingTask> GetTasks(DateTime? createdAfter);
|
||||
DateTime GetLastTaskDateTime();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all indexing tasks assigned to a specific content item
|
||||
|
Reference in New Issue
Block a user