2010-06-02 15:56:54 -07:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
|
using Orchard.ContentManagement;
|
|
|
|
|
using Orchard.Data;
|
2011-03-03 14:19:02 -08:00
|
|
|
|
using Orchard.Environment.Configuration;
|
|
|
|
|
using Orchard.FileSystems.AppData;
|
|
|
|
|
using Orchard.FileSystems.LockFile;
|
2010-06-17 16:21:29 -07:00
|
|
|
|
using Orchard.Indexing.Models;
|
2010-06-30 12:38:21 -07:00
|
|
|
|
using Orchard.Indexing.Settings;
|
2010-06-02 15:56:54 -07:00
|
|
|
|
using Orchard.Logging;
|
|
|
|
|
using Orchard.Services;
|
|
|
|
|
|
2010-06-17 16:21:29 -07:00
|
|
|
|
namespace Orchard.Indexing.Services {
|
2010-06-02 15:56:54 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains the logic which is regularly executed to retrieve index information from multiple content handlers.
|
|
|
|
|
/// </summary>
|
2011-03-03 14:19:02 -08:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This class is synchronized using a lock file as both command line and web workers can potentially use it,
|
|
|
|
|
/// and singleton locks would not be shared accross those two.
|
|
|
|
|
/// </remarks>
|
2010-06-02 15:56:54 -07:00
|
|
|
|
[UsedImplicitly]
|
2011-03-04 11:05:19 -08:00
|
|
|
|
public class IndexingTaskExecutor : IIndexNotifierHandler, IIndexStatisticsProvider {
|
2010-06-02 15:56:54 -07:00
|
|
|
|
private readonly IRepository<IndexingTaskRecord> _repository;
|
|
|
|
|
private IIndexProvider _indexProvider;
|
2010-06-03 13:12:07 -07:00
|
|
|
|
private readonly IIndexManager _indexManager;
|
2010-06-02 15:56:54 -07:00
|
|
|
|
private readonly IContentManager _contentManager;
|
2011-03-03 14:19:02 -08:00
|
|
|
|
private readonly IAppDataFolder _appDataFolder;
|
|
|
|
|
private readonly ShellSettings _shellSettings;
|
|
|
|
|
private readonly ILockFileManager _lockFileManager;
|
2011-03-04 11:05:19 -08:00
|
|
|
|
private readonly IClock _clock;
|
|
|
|
|
private const int ContentItemsPerLoop = 100;
|
|
|
|
|
private IndexingStatus _indexingStatus = IndexingStatus.Idle;
|
2010-06-30 12:38:21 -07:00
|
|
|
|
|
2010-06-02 15:56:54 -07:00
|
|
|
|
public IndexingTaskExecutor(
|
|
|
|
|
IRepository<IndexingTaskRecord> repository,
|
|
|
|
|
IIndexManager indexManager,
|
2010-06-17 16:21:29 -07:00
|
|
|
|
IContentManager contentManager,
|
2011-03-03 14:19:02 -08:00
|
|
|
|
IAppDataFolder appDataFolder,
|
|
|
|
|
ShellSettings shellSettings,
|
2011-03-04 11:05:19 -08:00
|
|
|
|
ILockFileManager lockFileManager,
|
|
|
|
|
IClock clock) {
|
2010-06-02 15:56:54 -07:00
|
|
|
|
_repository = repository;
|
|
|
|
|
_indexManager = indexManager;
|
|
|
|
|
_contentManager = contentManager;
|
2011-03-03 14:19:02 -08:00
|
|
|
|
_appDataFolder = appDataFolder;
|
|
|
|
|
_shellSettings = shellSettings;
|
|
|
|
|
_lockFileManager = lockFileManager;
|
2011-03-04 11:05:19 -08:00
|
|
|
|
_clock = clock;
|
2010-06-02 15:56:54 -07:00
|
|
|
|
Logger = NullLogger.Instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ILogger Logger { get; set; }
|
|
|
|
|
|
2010-06-04 17:42:20 -07:00
|
|
|
|
public void UpdateIndex(string indexName) {
|
2011-03-03 14:19:02 -08:00
|
|
|
|
ILockFile lockFile = null;
|
|
|
|
|
var settingsFilename = GetSettingsFileName(indexName);
|
|
|
|
|
var lockFilename = settingsFilename + ".lock";
|
2010-06-02 15:56:54 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// acquire a lock file on the index
|
2011-03-03 14:19:02 -08:00
|
|
|
|
if (!_lockFileManager.TryAcquireLock(lockFilename, ref lockFile)) {
|
2011-03-04 11:05:19 -08:00
|
|
|
|
Logger.Information("Index was requested but is already running");
|
2010-06-02 15:56:54 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
using (lockFile)
|
|
|
|
|
{
|
2010-06-04 17:42:20 -07:00
|
|
|
|
if (!_indexManager.HasIndexProvider()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-06-02 15:56:54 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// load index settings to know what is the current state of indexing
|
|
|
|
|
var indexSettings = LoadSettings(indexName);
|
|
|
|
|
|
2010-06-04 17:42:20 -07:00
|
|
|
|
_indexProvider = _indexManager.GetSearchIndexProvider();
|
2011-03-04 11:05:19 -08:00
|
|
|
|
|
|
|
|
|
// should the index be rebuilt
|
|
|
|
|
if (!_indexProvider.Exists(indexName)) {
|
|
|
|
|
_indexProvider.CreateIndex(indexName);
|
|
|
|
|
indexSettings = new IndexSettings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// execute indexing commands by batch of [ContentItemsPerLoop] content items
|
|
|
|
|
for (; ; ){
|
|
|
|
|
var addToIndex = new List<IDocumentIndex>();
|
|
|
|
|
var deleteFromIndex = new List<int>();
|
|
|
|
|
|
|
|
|
|
// Rebuilding the index ?
|
|
|
|
|
if (indexSettings.Mode == IndexingMode.Rebuild) {
|
|
|
|
|
Logger.Information("Rebuilding index");
|
|
|
|
|
_indexingStatus = IndexingStatus.Rebuilding;
|
|
|
|
|
|
|
|
|
|
// store the last inserted task
|
|
|
|
|
var lastIndexId = _repository
|
|
|
|
|
.Fetch(x => true)
|
|
|
|
|
.OrderByDescending(x => x.Id)
|
|
|
|
|
.Select(x => x.Id)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
// load all content items
|
|
|
|
|
var contentItemIds = _contentManager
|
|
|
|
|
.Query(VersionOptions.Published)
|
|
|
|
|
.List()
|
|
|
|
|
.Where(x => x.Id > indexSettings.LastContentId)
|
|
|
|
|
.OrderBy(x => x.Id)
|
|
|
|
|
.Select(x => x.Id)
|
|
|
|
|
.Distinct()
|
|
|
|
|
.Take(ContentItemsPerLoop)
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
indexSettings.LastIndexedId = lastIndexId;
|
|
|
|
|
|
|
|
|
|
// if no more elements to index, switch to update mode
|
|
|
|
|
if (contentItemIds.Length == 0) {
|
|
|
|
|
indexSettings.Mode = IndexingMode.Update;
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
2011-03-04 11:05:19 -08:00
|
|
|
|
|
|
|
|
|
foreach (var id in contentItemIds) {
|
|
|
|
|
try {
|
|
|
|
|
IDocumentIndex documentIndex = ExtractDocumentIndex(id);
|
|
|
|
|
|
|
|
|
|
if (documentIndex != null && documentIndex.IsDirty) {
|
|
|
|
|
addToIndex.Add(documentIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store the last processed element
|
|
|
|
|
indexSettings.LastContentId = contentItemIds.LastOrDefault();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
Logger.Warning(ex, "Unable to index content item #{0} during rebuild", id);
|
|
|
|
|
}
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-06-03 13:12:07 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
if (indexSettings.Mode == IndexingMode.Update) {
|
|
|
|
|
Logger.Information("Updating index");
|
|
|
|
|
_indexingStatus = IndexingStatus.Updating;
|
2010-06-02 15:56:54 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// load next content items to index, by filtering and ordering on the task id
|
|
|
|
|
var lastIndexId = _repository
|
|
|
|
|
.Fetch(x => x.Id > indexSettings.LastIndexedId)
|
|
|
|
|
.OrderByDescending(x => x.Id)
|
|
|
|
|
.Select(x => x.Id)
|
|
|
|
|
.FirstOrDefault();
|
2010-06-02 15:56:54 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
var contentItemIds = _repository
|
|
|
|
|
.Fetch(x => x.Id > indexSettings.LastIndexedId)
|
|
|
|
|
.OrderBy(x => x.Id)
|
|
|
|
|
.Take(ContentItemsPerLoop)
|
|
|
|
|
.Select(x => x.ContentItemRecord.Id)
|
|
|
|
|
.Distinct() // don't process the same content item twice
|
|
|
|
|
.ToArray();
|
2010-06-30 12:38:21 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
indexSettings.LastIndexedId = lastIndexId;
|
2010-06-03 13:12:07 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
foreach (var id in contentItemIds) {
|
|
|
|
|
try {
|
|
|
|
|
IDocumentIndex documentIndex = ExtractDocumentIndex(id);
|
2010-06-03 13:12:07 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
if (documentIndex == null) {
|
|
|
|
|
deleteFromIndex.Add(id);
|
|
|
|
|
}
|
|
|
|
|
else if (documentIndex.IsDirty) {
|
|
|
|
|
addToIndex.Add(documentIndex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
Logger.Warning(ex, "Unable to index content item #{0} during rebuild", id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-07-30 19:19:53 -07:00
|
|
|
|
}
|
2010-06-02 15:56:54 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// save current state of the index
|
|
|
|
|
indexSettings.LastIndexedUtc = _clock.UtcNow;
|
|
|
|
|
_appDataFolder.CreateFile(settingsFilename, indexSettings.ToString());
|
2010-06-04 17:42:20 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
if (deleteFromIndex.Count == 0 && addToIndex.Count == 0) {
|
|
|
|
|
// nothing more to do
|
|
|
|
|
_indexingStatus = IndexingStatus.Idle;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-06-30 12:38:21 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// save new and updated documents to the index
|
2010-06-04 17:42:20 -07:00
|
|
|
|
try {
|
2011-03-04 11:05:19 -08:00
|
|
|
|
if (addToIndex.Count > 0) {
|
|
|
|
|
_indexProvider.Store(indexName, addToIndex);
|
|
|
|
|
Logger.Information("Added content items to index: {0}", addToIndex.Count);
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
2011-03-04 11:05:19 -08:00
|
|
|
|
Logger.Warning(ex, "An error occured while adding a document to the index");
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
// removing documents from the index
|
2010-06-04 17:42:20 -07:00
|
|
|
|
try {
|
2011-03-04 11:05:19 -08:00
|
|
|
|
if (deleteFromIndex.Count > 0) {
|
|
|
|
|
_indexProvider.Delete(indexName, deleteFromIndex);
|
|
|
|
|
Logger.Information("Added content items to index: {0}", addToIndex.Count);
|
|
|
|
|
}
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
2011-03-04 11:05:19 -08:00
|
|
|
|
Logger.Warning(ex, "An error occured while removing a document from the index");
|
2010-06-04 17:42:20 -07:00
|
|
|
|
}
|
2010-06-04 16:29:50 -07:00
|
|
|
|
}
|
2010-06-03 13:12:07 -07:00
|
|
|
|
}
|
2010-06-02 15:56:54 -07:00
|
|
|
|
}
|
2010-06-30 12:38:21 -07:00
|
|
|
|
|
2011-03-04 11:05:19 -08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Loads the settings file or create a new default one if it doesn't exist
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IndexSettings LoadSettings(string indexName) {
|
|
|
|
|
var indexSettings = new IndexSettings();
|
|
|
|
|
var settingsFilename = GetSettingsFileName(indexName);
|
|
|
|
|
if (_appDataFolder.FileExists(settingsFilename)) {
|
|
|
|
|
var content = _appDataFolder.ReadFile(settingsFilename);
|
|
|
|
|
indexSettings = IndexSettings.Parse(content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return indexSettings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a IDocumentIndex instance for a specific content item id. If the content
|
|
|
|
|
/// item is no more published, it returns null.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private IDocumentIndex ExtractDocumentIndex(int id) {
|
|
|
|
|
var contentItem = _contentManager.Get(id, VersionOptions.Published);
|
|
|
|
|
|
|
|
|
|
// ignore deleted or unpublished items
|
|
|
|
|
if(contentItem == null || !contentItem.IsPublished()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// skip items from types which are not indexed
|
|
|
|
|
var settings = GetTypeIndexingSettings(contentItem);
|
|
|
|
|
if (!settings.Included)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var documentIndex = _indexProvider.New(contentItem.Id);
|
|
|
|
|
|
|
|
|
|
// call all handlers to add content to index
|
|
|
|
|
_contentManager.Index(contentItem, documentIndex);
|
|
|
|
|
return documentIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-30 12:38:21 -07:00
|
|
|
|
static TypeIndexing GetTypeIndexingSettings(ContentItem contentItem) {
|
|
|
|
|
if (contentItem == null ||
|
|
|
|
|
contentItem.TypeDefinition == null ||
|
|
|
|
|
contentItem.TypeDefinition.Settings == null) {
|
|
|
|
|
return new TypeIndexing { Included = false };
|
|
|
|
|
}
|
|
|
|
|
return contentItem.TypeDefinition.Settings.GetModel<TypeIndexing>();
|
|
|
|
|
}
|
2011-03-03 14:19:02 -08:00
|
|
|
|
|
|
|
|
|
private string GetSettingsFileName(string indexName) {
|
|
|
|
|
return _appDataFolder.Combine("Sites", _shellSettings.Name, indexName + ".settings.xml");
|
|
|
|
|
}
|
2011-03-04 11:05:19 -08:00
|
|
|
|
|
|
|
|
|
public DateTime GetLastIndexedUtc(string indexName) {
|
|
|
|
|
var indexSettings = LoadSettings(indexName);
|
|
|
|
|
return indexSettings.LastIndexedUtc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IndexingStatus GetIndexingStatus(string indexName) {
|
|
|
|
|
return _indexingStatus;
|
|
|
|
|
}
|
2010-06-02 15:56:54 -07:00
|
|
|
|
}
|
|
|
|
|
}
|