in src/OrchardCore.Modules/OrchardCore.Lucene/Services/LuceneIndexingService.cs [54:230]
public async Task ProcessContentItemsAsync(string indexName = default)
{
// TODO: Lock over the filesystem in case two instances get a command to rebuild the index concurrently.
var allIndices = new Dictionary<string, int>();
var lastTaskId = Int32.MaxValue;
IEnumerable<LuceneIndexSettings> indexSettingsList = null;
if (String.IsNullOrEmpty(indexName))
{
indexSettingsList = await _luceneIndexSettingsService.GetSettingsAsync();
if (!indexSettingsList.Any())
{
return;
}
// Find the lowest task id to process
foreach (var indexSetting in indexSettingsList)
{
var taskId = _indexingState.GetLastTaskId(indexSetting.IndexName);
lastTaskId = Math.Min(lastTaskId, taskId);
allIndices.Add(indexSetting.IndexName, taskId);
}
}
else
{
var settings = await _luceneIndexSettingsService.GetSettingsAsync(indexName);
if (settings == null)
{
return;
}
indexSettingsList = new LuceneIndexSettings[1] { settings }.AsEnumerable();
var taskId = _indexingState.GetLastTaskId(indexName);
lastTaskId = Math.Min(lastTaskId, taskId);
allIndices.Add(indexName, taskId);
}
if (allIndices.Count == 0)
{
return;
}
var batch = Array.Empty<IndexingTask>();
do
{
// Create a scope for the content manager
var shellScope = await _shellHost.GetScopeAsync(_shellSettings);
await shellScope.UsingAsync(async scope =>
{
// Load the next batch of tasks
batch = (await _indexingTaskManager.GetIndexingTasksAsync(lastTaskId, BatchSize)).ToArray();
if (!batch.Any())
{
return;
}
var contentManager = scope.ServiceProvider.GetRequiredService<IContentManager>();
var indexHandlers = scope.ServiceProvider.GetServices<IContentItemIndexHandler>();
// Pre-load all content items to prevent SELECT N+1
var updatedContentItemIds = batch
.Where(x => x.Type == IndexingTaskTypes.Update)
.Select(x => x.ContentItemId)
.ToArray();
var allPublished = await contentManager.GetAsync(updatedContentItemIds);
var allLatest = await contentManager.GetAsync(updatedContentItemIds, latest: true);
// Group all DocumentIndex by index to batch update them
var updatedDocumentsByIndex = new Dictionary<string, List<DocumentIndex>>();
foreach (var index in allIndices)
{
updatedDocumentsByIndex[index.Key] = new List<DocumentIndex>();
}
if (indexName != null)
{
indexSettingsList = indexSettingsList.Where(x => x.IndexName == indexName);
}
var needLatest = indexSettingsList.FirstOrDefault(x => x.IndexLatest) != null;
var needPublished = indexSettingsList.FirstOrDefault(x => !x.IndexLatest) != null;
var settingsByIndex = indexSettingsList.ToDictionary(x => x.IndexName, x => x);
foreach (var task in batch)
{
if (task.Type == IndexingTaskTypes.Update)
{
BuildIndexContext publishedIndexContext = null, latestIndexContext = null;
if (needPublished)
{
var contentItem = await contentManager.GetAsync(task.ContentItemId);
if (contentItem != null)
{
publishedIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId), contentItem, new string[] { contentItem.ContentType });
await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(publishedIndexContext), _logger);
}
}
if (needLatest)
{
var contentItem = await contentManager.GetAsync(task.ContentItemId, VersionOptions.Latest);
if (contentItem != null)
{
latestIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId), contentItem, new string[] { contentItem.ContentType });
await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(latestIndexContext), _logger);
}
}
// Update the document from the index if its lastIndexId is smaller than the current task id.
foreach (var index in allIndices)
{
if (index.Value >= task.Id || !settingsByIndex.TryGetValue(index.Key, out var settings))
{
continue;
}
var context = !settings.IndexLatest ? publishedIndexContext : latestIndexContext;
//We index only if we actually found a content item in the database
if (context == null)
{
//TODO purge these content items from IndexingTask table
continue;
}
bool ignoreIndexedCulture = settings.Culture == "any" ? false : context.ContentItem.Content?.LocalizationPart?.Culture != settings.Culture;
// Ignore if the content item content type or culture is not indexed in this index
if (!settings.IndexedContentTypes.Contains(context.ContentItem.ContentType) || ignoreIndexedCulture)
{
continue;
}
updatedDocumentsByIndex[index.Key].Add(context.DocumentIndex);
}
}
}
// Delete all the existing documents
foreach (var index in updatedDocumentsByIndex)
{
var deletedDocuments = updatedDocumentsByIndex[index.Key].Select(x => x.ContentItemId);
await _indexManager.DeleteDocumentsAsync(index.Key, deletedDocuments);
}
// Submits all the new documents to the index
foreach (var index in updatedDocumentsByIndex)
{
await _indexManager.StoreDocumentsAsync(index.Key, updatedDocumentsByIndex[index.Key]);
}
// Update task ids
lastTaskId = batch.Last().Id;
foreach (var indexStatus in allIndices)
{
if (indexStatus.Value < lastTaskId)
{
_indexingState.SetLastTaskId(indexStatus.Key, lastTaskId);
}
}
_indexingState.Update();
});
} while (batch.Length == BatchSize);
}