Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
576 changes: 303 additions & 273 deletions src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs

Large diffs are not rendered by default.

84 changes: 41 additions & 43 deletions src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs
Original file line number Diff line number Diff line change
@@ -1,65 +1,63 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.

using System;
using System.IO;
using Examine;
using Examine.Lucene.Directories;
using Examine.Lucene.Providers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Directory = Lucene.Net.Store.Directory;

namespace Umbraco.Cms.Infrastructure.Examine
namespace Umbraco.Cms.Infrastructure.Examine;

/// <summary>
/// An Examine directory factory implementation based on configured values
/// </summary>
public class ConfigurationEnabledDirectoryFactory : DirectoryFactoryBase
{
private readonly IApplicationRoot _applicationRoot;
private readonly IServiceProvider _services;
private readonly IndexCreatorSettings _settings;
private IDirectoryFactory? _directoryFactory;

public ConfigurationEnabledDirectoryFactory(
IServiceProvider services,
IOptions<IndexCreatorSettings> settings,
IApplicationRoot applicationRoot)
{
_services = services;
_applicationRoot = applicationRoot;
_settings = settings.Value;
}

protected override Directory CreateDirectory(LuceneIndex luceneIndex, bool forceUnlock)
{
_directoryFactory = CreateFactory();
return _directoryFactory.CreateDirectory(luceneIndex, forceUnlock);
}

/// <summary>
/// An Examine directory factory implementation based on configured values
/// Creates a directory factory based on the configured value and ensures that
/// </summary>
public class ConfigurationEnabledDirectoryFactory : DirectoryFactoryBase
private IDirectoryFactory CreateFactory()
{
private readonly IServiceProvider _services;
private readonly IApplicationRoot _applicationRoot;
private readonly IndexCreatorSettings _settings;
private IDirectoryFactory? _directoryFactory;

public ConfigurationEnabledDirectoryFactory(
IServiceProvider services,
IOptions<IndexCreatorSettings> settings,
IApplicationRoot applicationRoot)
{
_services = services;
_applicationRoot = applicationRoot;
_settings = settings.Value;
}
DirectoryInfo dirInfo = _applicationRoot.ApplicationRoot;

protected override Lucene.Net.Store.Directory CreateDirectory(LuceneIndex luceneIndex, bool forceUnlock)
if (!dirInfo.Exists)
{
_directoryFactory = CreateFactory();
return _directoryFactory.CreateDirectory(luceneIndex, forceUnlock);
System.IO.Directory.CreateDirectory(dirInfo.FullName);
}

/// <summary>
/// Creates a directory factory based on the configured value and ensures that
/// </summary>
private IDirectoryFactory CreateFactory()
switch (_settings.LuceneDirectoryFactory)
{
DirectoryInfo dirInfo = _applicationRoot.ApplicationRoot;

if (!dirInfo.Exists)
{
Directory.CreateDirectory(dirInfo.FullName);
}

switch (_settings.LuceneDirectoryFactory)
{
case LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory:
return _services.GetRequiredService<SyncedFileSystemDirectoryFactory>();
case LuceneDirectoryFactory.TempFileSystemDirectoryFactory:
return _services.GetRequiredService<TempEnvFileSystemDirectoryFactory>();
case LuceneDirectoryFactory.Default:
default:
return _services.GetRequiredService<FileSystemDirectoryFactory>();
}
case LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory:
return _services.GetRequiredService<SyncedFileSystemDirectoryFactory>();
case LuceneDirectoryFactory.TempFileSystemDirectoryFactory:
return _services.GetRequiredService<TempEnvFileSystemDirectoryFactory>();
case LuceneDirectoryFactory.Default:
default:
return _services.GetRequiredService<FileSystemDirectoryFactory>();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Examine;
using Examine.Lucene;
using Examine.Lucene.Analyzers;
Expand All @@ -8,58 +7,55 @@
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;

namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection
namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection;

/// <summary>
/// Configures the index options to construct the Examine indexes
/// </summary>
public sealed class ConfigureIndexOptions : IConfigureNamedOptions<LuceneDirectoryIndexOptions>
{
/// <summary>
/// Configures the index options to construct the Examine indexes
/// </summary>
public sealed class ConfigureIndexOptions : IConfigureNamedOptions<LuceneDirectoryIndexOptions>
private readonly IndexCreatorSettings _settings;
private readonly IUmbracoIndexConfig _umbracoIndexConfig;

public ConfigureIndexOptions(
IUmbracoIndexConfig umbracoIndexConfig,
IOptions<IndexCreatorSettings> settings)
{
private readonly IUmbracoIndexConfig _umbracoIndexConfig;
private readonly IndexCreatorSettings _settings;
_umbracoIndexConfig = umbracoIndexConfig;
_settings = settings.Value;
}

public ConfigureIndexOptions(
IUmbracoIndexConfig umbracoIndexConfig,
IOptions<IndexCreatorSettings> settings)
public void Configure(string name, LuceneDirectoryIndexOptions options)
{
switch (name)
{
_umbracoIndexConfig = umbracoIndexConfig;
_settings = settings.Value;
case Constants.UmbracoIndexes.InternalIndexName:
options.Analyzer = new CultureInvariantWhitespaceAnalyzer();
options.Validator = _umbracoIndexConfig.GetContentValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
case Constants.UmbracoIndexes.ExternalIndexName:
options.Analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion);
options.Validator = _umbracoIndexConfig.GetPublishedContentValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
case Constants.UmbracoIndexes.MembersIndexName:
options.Analyzer = new CultureInvariantWhitespaceAnalyzer();
options.Validator = _umbracoIndexConfig.GetMemberValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
}

public void Configure(string name, LuceneDirectoryIndexOptions options)
{
switch (name)
{
case Constants.UmbracoIndexes.InternalIndexName:
options.Analyzer = new CultureInvariantWhitespaceAnalyzer();
options.Validator = _umbracoIndexConfig.GetContentValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
case Constants.UmbracoIndexes.ExternalIndexName:
options.Analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion);
options.Validator = _umbracoIndexConfig.GetPublishedContentValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
case Constants.UmbracoIndexes.MembersIndexName:
options.Analyzer = new CultureInvariantWhitespaceAnalyzer();
options.Validator = _umbracoIndexConfig.GetMemberValueSetValidator();
options.FieldDefinitions = new UmbracoFieldDefinitionCollection();
break;
}

// ensure indexes are unlocked on startup
options.UnlockIndex = true;

if (_settings.LuceneDirectoryFactory == LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory)
{
// if this directory factory is enabled then a snapshot deletion policy is required
options.IndexDeletionPolicy = new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy());
}

// ensure indexes are unlocked on startup
options.UnlockIndex = true;

if (_settings.LuceneDirectoryFactory == LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory)
{
// if this directory factory is enabled then a snapshot deletion policy is required
options.IndexDeletionPolicy = new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy());
}

public void Configure(LuceneDirectoryIndexOptions options)
=> throw new NotImplementedException("This is never called and is just part of the interface");
}

public void Configure(LuceneDirectoryIndexOptions options)
=> throw new NotImplementedException("This is never called and is just part of the interface");
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,39 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Infrastructure.DependencyInjection;

namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection
namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection;

public static class UmbracoBuilderExtensions
{
public static class UmbracoBuilderExtensions
/// <summary>
/// Adds the Examine indexes for Umbraco
/// </summary>
/// <param name="umbracoBuilder"></param>
/// <returns></returns>
public static IUmbracoBuilder AddExamineIndexes(this IUmbracoBuilder umbracoBuilder)
{
/// <summary>
/// Adds the Examine indexes for Umbraco
/// </summary>
/// <param name="umbracoBuilder"></param>
/// <returns></returns>
public static IUmbracoBuilder AddExamineIndexes(this IUmbracoBuilder umbracoBuilder)
{
IServiceCollection services = umbracoBuilder.Services;
IServiceCollection services = umbracoBuilder.Services;

services.AddSingleton<IBackOfficeExamineSearcher, BackOfficeExamineSearcher>();
services.AddSingleton<IIndexDiagnosticsFactory, LuceneIndexDiagnosticsFactory>();
services.AddSingleton<IBackOfficeExamineSearcher, BackOfficeExamineSearcher>();
services.AddSingleton<IIndexDiagnosticsFactory, LuceneIndexDiagnosticsFactory>();

services.AddExamine();
services.AddExamine();

// Create the indexes
services
.AddExamineLuceneIndex<UmbracoContentIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes.InternalIndexName)
.AddExamineLuceneIndex<UmbracoContentIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes.ExternalIndexName)
.AddExamineLuceneIndex<UmbracoMemberIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes.MembersIndexName)
.ConfigureOptions<ConfigureIndexOptions>();
// Create the indexes
services
.AddExamineLuceneIndex<UmbracoContentIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes
.InternalIndexName)
.AddExamineLuceneIndex<UmbracoContentIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes
.ExternalIndexName)
.AddExamineLuceneIndex<UmbracoMemberIndex, ConfigurationEnabledDirectoryFactory>(Constants.UmbracoIndexes
.MembersIndexName)
.ConfigureOptions<ConfigureIndexOptions>();

services.AddSingleton<IApplicationRoot, UmbracoApplicationRoot>();
services.AddSingleton<ILockFactory, UmbracoLockFactory>();
services.AddSingleton<ConfigurationEnabledDirectoryFactory>();
services.AddSingleton<IApplicationRoot, UmbracoApplicationRoot>();
services.AddSingleton<ILockFactory, UmbracoLockFactory>();
services.AddSingleton<ConfigurationEnabledDirectoryFactory>();

return umbracoBuilder;
}
return umbracoBuilder;
}
}
95 changes: 43 additions & 52 deletions src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,66 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Examine;
using Examine.Lucene.Providers;
using Lucene.Net.Analysis.Core;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers.Classic;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Runtime;
using Lucene.Net.Search;
using Umbraco.Cms.Infrastructure.Examine;

namespace Umbraco.Extensions
namespace Umbraco.Extensions;

/// <summary>
/// Extension methods for the LuceneIndex
/// </summary>
public static class ExamineExtensions
{
internal static bool TryParseLuceneQuery(string query)
{
// TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
// also do this rudimentary check
if (!query.Contains(":"))
{
return false;
}

try
{
//This will pass with a plain old string without any fields, need to figure out a way to have it properly parse
Query? parsed = new QueryParser(LuceneInfo.CurrentVersion, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query);
return true;
}
catch (ParseException)
{
return false;
}
catch (Exception)
{
return false;
}
}

/// <summary>
/// Extension methods for the LuceneIndex
/// Checks if the index can be read/opened
/// </summary>
public static class ExamineExtensions
/// <param name="indexer"></param>
/// <param name="ex">The exception returned if there was an error</param>
/// <returns></returns>
public static bool IsHealthy(this LuceneIndex indexer, [MaybeNullWhen(true)] out Exception ex)
{
internal static bool TryParseLuceneQuery(string query)
try
{
// TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
// also do this rudimentary check
if (!query.Contains(":"))
using (indexer.IndexWriter.IndexWriter.GetReader(false))
{
return false;
}

try
{
//This will pass with a plain old string without any fields, need to figure out a way to have it properly parse
var parsed = new QueryParser(LuceneInfo.CurrentVersion, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query);
ex = null;
return true;
}
catch (ParseException)
{
return false;
}
catch (Exception)
{
return false;
}
}

/// <summary>
/// Checks if the index can be read/opened
/// </summary>
/// <param name="indexer"></param>
/// <param name="ex">The exception returned if there was an error</param>
/// <returns></returns>
public static bool IsHealthy(this LuceneIndex indexer, [MaybeNullWhen(true)] out Exception ex)
catch (Exception e)
{
try
{
using (indexer.IndexWriter.IndexWriter.GetReader(false))
{
ex = null;
return true;
}
}
catch (Exception e)
{
ex = e;
return false;
}
ex = e;
return false;
}

}
}
Loading