diff --git a/src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs b/src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs index 953853e88..443f8dad6 100644 --- a/src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs +++ b/src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs @@ -168,5 +168,18 @@ namespace Orchard.Tests.Indexing { Assert.That(searchBuilder.WithField("body", "hr").Search().Count(), Is.EqualTo(1)); Assert.That(searchBuilder.WithField("body", "hr").Search().First().Id, Is.EqualTo(1)); } + + [Test] public void ShouldAllowNullOrEmptyStrings() { + _provider.CreateIndex("default"); + _provider.Store("default", _provider.New(1).Add("body", null)); + _provider.Store("default", _provider.New(2).Add("body", "")); + _provider.Store("default", _provider.New(3).Add("body", "
", true)); + + var searchBuilder = _provider.CreateSearchBuilder("default"); + + Assert.That(searchBuilder.Get(1).Id, Is.EqualTo(1)); + Assert.That(searchBuilder.Get(2).Id, Is.EqualTo(2)); + Assert.That(searchBuilder.Get(3).Id, Is.EqualTo(3)); + } } } diff --git a/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs b/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs index 8c10ff43b..02eae5b1c 100644 --- a/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs +++ b/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs @@ -25,6 +25,11 @@ namespace Orchard.Core.Indexing.Lucene { public IIndexDocument Add(string name, string value, bool removeTags) { AppendPreviousField(); + + if(value == null) { + value = String.Empty; + } + if(removeTags) { value = value.RemoveTags(); } diff --git a/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs b/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs index d7df669a5..9a5e01174 100644 --- a/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs +++ b/src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; @@ -14,7 +16,7 @@ using Orchard.Logging; namespace Orchard.Core.Indexing.Lucene { /// - /// Represents the default implementation of an IIndexProvider based on Lucene + /// Represents the default implementation of an IIndexProvider, based on Lucene /// public class DefaultIndexProvider : IIndexProvider { private readonly IAppDataFolder _appDataFolder; @@ -73,40 +75,65 @@ namespace Orchard.Core.Indexing.Lucene { } public void Store(string indexName, IIndexDocument indexDocument) { - Store(indexName, (DefaultIndexDocument)indexDocument); + Store(indexName, new [] { (DefaultIndexDocument)indexDocument }); } - public void Store(string indexName, DefaultIndexDocument indexDocument) { + public void Store(string indexName, IEnumerable indexDocuments) { + Store(indexName, indexDocuments.Cast()); + } + + public void Store(string indexName, IEnumerable indexDocuments) { + if(indexDocuments.AsQueryable().Count() == 0) { + return; + } + var writer = new IndexWriter(GetDirectory(indexName), _analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED); + DefaultIndexDocument current = null; try { - var doc = CreateDocument(indexDocument); - writer.AddDocument(doc); - Logger.Debug("Document [{0}] indexed", indexDocument.Id); + foreach ( var indexDocument in indexDocuments ) { + current = indexDocument; + var doc = CreateDocument(indexDocument); + writer.AddDocument(doc); + Logger.Debug("Document [{0}] indexed", indexDocument.Id); + } } catch ( Exception ex ) { - Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", indexDocument.Id, indexName); + Logger.Error(ex, "An unexpected error occured while add the document [{0}] from the index [{1}].", current.Id, indexName); } finally { + writer.Optimize(); writer.Close(); } } - public void Delete(string indexName, int id) { - var reader = IndexReader.Open(GetDirectory(indexName), false); + public void Delete(string indexName, int documentId) { + Delete(indexName, new[] { documentId }); + } + + public void Delete(string indexName, IEnumerable documentIds) { + if ( documentIds.AsQueryable().Count() == 0 ) { + return; + } + + var reader = IndexReader.Open(GetDirectory(indexName), false); try { - var term = new Term("id", id.ToString()); - if ( reader.DeleteDocuments(term) != 0 ) { - Logger.Error("The document [{0}] could not be removed from the index [{1}]", id, indexName); + foreach (var id in documentIds) { + try { + var term = new Term("id", id.ToString()); + if (reader.DeleteDocuments(term) != 0) { + Logger.Error("The document [{0}] could not be removed from the index [{1}]", id, indexName); + } + else { + Logger.Debug("Document [{0}] removed from index", id); + } + } + catch (Exception ex) { + Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", id, indexName); + } } - else { - Logger.Debug("Document [{0}] removed from index", id); - } - } - catch ( Exception ex ) { - Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", id, indexName); } finally { reader.Close(); @@ -121,8 +148,5 @@ namespace Orchard.Core.Indexing.Lucene { return new DefaultSearchBuilder(GetDirectory(indexName)); } - public IIndexDocument Get(string indexName, int id) { - throw new NotImplementedException(); - } } } diff --git a/src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs b/src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs index e0bb9f54b..3cab96601 100644 --- a/src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs +++ b/src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs @@ -3,7 +3,12 @@ using Orchard.ContentManagement.Records; namespace Orchard.Core.Indexing.Models { public class IndexingTaskRecord { + + public const int Update = 0; + public const int Delete = 1; + public virtual int Id { get; set; } + public virtual int Action { get; set; } public virtual DateTime? CreatedUtc { get; set; } public virtual ContentItemRecord ContentItemRecord { get; set; } } diff --git a/src/Orchard.Web/Core/Indexing/Services/CreateIndexingTaskHandler.cs b/src/Orchard.Web/Core/Indexing/Services/CreateIndexingTaskHandler.cs index 7924afb75..4da37eddc 100644 --- a/src/Orchard.Web/Core/Indexing/Services/CreateIndexingTaskHandler.cs +++ b/src/Orchard.Web/Core/Indexing/Services/CreateIndexingTaskHandler.cs @@ -19,11 +19,11 @@ namespace Orchard.Core.Indexing.Services { } void CreateIndexingTask(PublishContentContext context, ContentPart part) { - _indexingTaskManager.CreateTask(context.ContentItem); + _indexingTaskManager.CreateUpdateIndexTask(context.ContentItem); } void RemoveIndexingTask(RemoveContentContext context, ContentPart part) { - _indexingTaskManager.DeleteTasks(context.ContentItem); + _indexingTaskManager.CreateDeleteIndexTask(context.ContentItem); } } diff --git a/src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs b/src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs index 7fa3d8a6c..1b4d53035 100644 --- a/src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs +++ b/src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs @@ -22,9 +22,9 @@ namespace Orchard.Core.Indexing.Services { private readonly IRepository _settings; private readonly IEnumerable _handlers; private IIndexProvider _indexProvider; - private IIndexManager _indexManager; + private readonly IIndexManager _indexManager; private readonly IContentManager _contentManager; - private const string SearchIndexName = "search"; + private const string SearchIndexName = "Search"; public IndexingTaskExecutor( IClock clock, @@ -46,7 +46,7 @@ namespace Orchard.Core.Indexing.Services { public void Sweep() { - if(!_indexManager.HasIndexProvider()) { + if ( !_indexManager.HasIndexProvider() ) { return; } @@ -55,8 +55,8 @@ namespace Orchard.Core.Indexing.Services { // retrieve last processed index time var settingsRecord = _settings.Table.FirstOrDefault(); - if (settingsRecord == null) { - _settings.Create(settingsRecord = new IndexingSettingsRecord { LatestIndexingUtc = new DateTime(1980, 1, 1)}); + if ( settingsRecord == null ) { + _settings.Create(settingsRecord = new IndexingSettingsRecord { LatestIndexingUtc = new DateTime(1980, 1, 1) }); } var lastIndexing = settingsRecord.LatestIndexingUtc; @@ -65,41 +65,76 @@ namespace Orchard.Core.Indexing.Services { // retrieved not yet processed tasks var taskRecords = _repository.Fetch(x => x.CreatedUtc >= lastIndexing) .ToArray(); - - if (taskRecords.Length == 0) + + if ( taskRecords.Length == 0 ) return; Logger.Information("Processing {0} indexing tasks", taskRecords.Length); - - if(!_indexProvider.Exists(SearchIndexName)) { + + if ( !_indexProvider.Exists(SearchIndexName) ) { _indexProvider.CreateIndex(SearchIndexName); } - foreach (var taskRecord in taskRecords) { + var updateIndexDocuments = new List(); + var deleteIndexDocuments = new List(); + + // process Delete tasks + foreach ( var taskRecord in taskRecords.Where(t => t.Action == IndexingTaskRecord.Delete) ) { + var task = new IndexingTask(_contentManager, taskRecord); + deleteIndexDocuments.Add(taskRecord.ContentItemRecord.Id); + + try { + _repository.Delete(taskRecord); + } + catch ( Exception ex ) { + Logger.Error(ex, "Could not delete task #{0}", taskRecord.Id); + } + } + + + try { + if ( deleteIndexDocuments.Count > 0 ) { + _indexProvider.Delete(SearchIndexName, deleteIndexDocuments); + } + } + catch ( Exception ex ) { + Logger.Warning(ex, "An error occured while remove a document from the index"); + } + + // process Update tasks + foreach ( var taskRecord in taskRecords.Where(t => t.Action == IndexingTaskRecord.Update) ) { + var task = new IndexingTask(_contentManager, taskRecord); try { - var task = new IndexingTask(_contentManager, taskRecord); var context = new IndexContentContext { - ContentItem = task.ContentItem, - IndexDocument = _indexProvider.New(task.ContentItem.Id) + ContentItem = task.ContentItem, + IndexDocument = _indexProvider.New(task.ContentItem.Id) }; // dispatch to handlers to retrieve index information - foreach (var handler in _handlers) { + foreach ( var handler in _handlers ) { handler.Indexing(context); } - _indexProvider.Store(SearchIndexName, context.IndexDocument); + updateIndexDocuments.Add(context.IndexDocument); foreach ( var handler in _handlers ) { handler.Indexed(context); } } - catch (Exception ex) { + catch ( Exception ex ) { Logger.Warning(ex, "Unable to process indexing task #{0}", taskRecord.Id); } + } + try { + if ( updateIndexDocuments.Count > 0 ) { + _indexProvider.Store(SearchIndexName, updateIndexDocuments); + } + } + catch ( Exception ex ) { + Logger.Warning(ex, "An error occured while adding a document to the index"); } _settings.Update(settingsRecord); diff --git a/src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs b/src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs index aa37a69e7..1dde1d257 100644 --- a/src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs +++ b/src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs @@ -27,34 +27,46 @@ namespace Orchard.Core.Indexing.Services { _clock = clock; _repository = repository; _contentManager = contentManager; + _settings = settings; Logger = NullLogger.Instance; } public ILogger Logger { get; set; } - public void CreateTask(ContentItem contentItem) { - if (contentItem == null) { + private void CreateTask(ContentItem contentItem, int action) { + if ( contentItem == null ) { throw new ArgumentNullException("contentItem"); } // remove previous tasks for the same content item var tasks = _repository - .Fetch(x => x.Id == contentItem.Id ) + .Fetch(x => x.ContentItemRecord.Id == contentItem.Id) .ToArray(); - foreach (var task in tasks) { + foreach ( var task in tasks ) { _repository.Delete(task); } var taskRecord = new IndexingTaskRecord { - CreatedUtc = _clock.UtcNow, - ContentItemRecord = contentItem.Record - }; + CreatedUtc = _clock.UtcNow, + ContentItemRecord = contentItem.Record, + Action = action + }; _repository.Create(taskRecord); + + } + public void CreateUpdateIndexTask(ContentItem contentItem) { + + CreateTask(contentItem, IndexingTaskRecord.Update); Logger.Information("Indexing task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id); + } + public void CreateDeleteIndexTask(ContentItem contentItem) { + + CreateTask(contentItem, IndexingTaskRecord.Delete); + Logger.Information("Deleting index task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id); } public IEnumerable GetTasks(DateTime? createdAfter) { diff --git a/src/Orchard/Indexing/IIndexProvider.cs b/src/Orchard/Indexing/IIndexProvider.cs index dcd44c06a..4045931e8 100644 --- a/src/Orchard/Indexing/IIndexProvider.cs +++ b/src/Orchard/Indexing/IIndexProvider.cs @@ -1,4 +1,6 @@ -namespace Orchard.Indexing { +using System.Collections.Generic; + +namespace Orchard.Indexing { public interface IIndexProvider : IDependency { /// /// Creates a new index @@ -15,11 +17,6 @@ /// void DeleteIndex(string name); - /// - /// Loads an existing document - /// - IIndexDocument Get(string indexName, int documentId); - /// /// Creates an empty document /// @@ -31,10 +28,20 @@ /// void Store(string indexName, IIndexDocument indexDocument); + /// + /// Adds a set of new document to the index + /// + void Store(string indexName, IEnumerable indexDocuments); + /// /// Removes an existing document from the index /// - void Delete(string indexName, int id); + void Delete(string indexName, int documentId); + + /// + /// Removes a set of existing document from the index + /// + void Delete(string indexName, IEnumerable documentIds); /// /// Creates a search builder for this provider diff --git a/src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs b/src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs index de913e167..cdb950e0f 100644 --- a/src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs +++ b/src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs @@ -4,9 +4,30 @@ using Orchard.ContentManagement; namespace Orchard.Tasks.Indexing { public interface IIndexingTaskManager : IDependency { - void CreateTask(ContentItem contentItem); + /// + /// Adds a new entry in the index task table in order to create an index for the specified content item. + /// + void CreateUpdateIndexTask(ContentItem contentItem); + + /// + /// Adds a new entry in the index task table in order to delete an existing index for the specified content item. + /// + void CreateDeleteIndexTask(ContentItem contentItem); + + /// + /// Loads all indexing tasks created after to a specific date and time + /// IEnumerable GetTasks(DateTime? createdAfter); + + /// + /// Deletes all indexing tasks previous to a specific date and time + /// void DeleteTasks(DateTime? createdBefore); + + /// + /// Deletes all indexing tasks assigned to a specific content item + /// + /// void DeleteTasks(ContentItem contentItem); } } \ No newline at end of file diff --git a/src/Orchard/Tasks/SweepGenerator.cs b/src/Orchard/Tasks/SweepGenerator.cs index b8fb5b64b..f811029b2 100644 --- a/src/Orchard/Tasks/SweepGenerator.cs +++ b/src/Orchard/Tasks/SweepGenerator.cs @@ -14,7 +14,7 @@ namespace Orchard.Tasks { _timer = new Timer(); _timer.Elapsed += Elapsed; Logger = NullLogger.Instance; - Interval = TimeSpan.FromMinutes(5); + Interval = TimeSpan.FromMinutes(1); } public ILogger Logger { get; set; }