Skip to content

Commit

Permalink
Fix AISearch index prefix and index creation (#15723)
Browse files Browse the repository at this point in the history
Co-authored-by: Zoltán Lehóczky <zoltan.lehoczky@lombiq.com>
  • Loading branch information
MikeAlhayek and Piedone authored Apr 10, 2024
1 parent 4c29c61 commit d19d39d
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 55 deletions.
10 changes: 2 additions & 8 deletions src/OrchardCore.Modules/OrchardCore.Search.AzureAI/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OrchardCore.ContentTypes.Editors;
using OrchardCore.Deployment;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Modules;
using OrchardCore.Navigation;
using OrchardCore.Search.Abstractions;
Expand All @@ -16,15 +14,11 @@

namespace OrchardCore.Search.AzureAI;

public class Startup(ILogger<Startup> logger, IShellConfiguration shellConfiguration)
: StartupBase
public class Startup : StartupBase
{
private readonly ILogger _logger = logger;
private readonly IShellConfiguration _shellConfiguration = shellConfiguration;

public override void ConfigureServices(IServiceCollection services)
{
services.TryAddAzureAISearchServices(_shellConfiguration, _logger);
services.AddAzureAISearchServices();
services.AddScoped<INavigationProvider, AdminMenu>();
services.AddScoped<IDisplayDriver<ISite>, AzureAISearchDefaultSettingsDisplayDriver>();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OrchardCore.ContentManagement.Handlers;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Recipes;
using OrchardCore.Search.AzureAI.Handlers;
using OrchardCore.Search.AzureAI.Models;
Expand All @@ -16,22 +13,10 @@ namespace OrchardCore.Search.AzureAI;

public static class ServiceCollectionExtensions
{
public static bool TryAddAzureAISearchServices(this IServiceCollection services, IShellConfiguration configuration, ILogger logger)
public static IServiceCollection AddAzureAISearchServices(this IServiceCollection services)
{
var section = configuration.GetSection("OrchardCore_AzureAISearch");

var options = section.Get<AzureAISearchDefaultOptions>();
var configExists = true;
if (string.IsNullOrWhiteSpace(options?.Endpoint) || string.IsNullOrWhiteSpace(options?.Credential?.Key))
{
configExists = false;
logger.LogError("Azure AI Search module is enabled. However, the connection settings are not provided in configuration file.");
}

services.AddTransient<IConfigureOptions<AzureAISearchDefaultOptions>, AzureAISearchDefaultOptionsConfigurations>();

services.AddAzureClientsCore();

services.AddScoped<IPermissionProvider, Permissions>();
services.AddScoped<IContentHandler, AzureAISearchIndexingContentHandler>();
services.AddScoped<AzureAISearchIndexManager>();
Expand All @@ -44,6 +29,6 @@ public static bool TryAddAzureAISearchServices(this IServiceCollection services,
services.AddRecipeExecutionStep<AzureAISearchIndexResetStep>();
services.AddRecipeExecutionStep<AzureAISearchIndexSettingsStep>();

return configExists;
return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ public AzureAISearchDefaultOptionsConfigurations(

public async void Configure(AzureAISearchDefaultOptions options)
{
var fileOptions = _shellConfiguration.GetSection("OrchardCore_AzureAISearch").Get<AzureAISearchDefaultOptions>()
var fileOptions = _shellConfiguration.GetSection("OrchardCore_AzureAISearch")
.Get<AzureAISearchDefaultOptions>()
?? new AzureAISearchDefaultOptions();

// This should be called first to set whether or not the file configs are set or not.
// This should be called first determine whether the file configs are set or not.
options.SetFileConfigurationExists(HasConnectionInfo(fileOptions));

// The DisableUIConfiguration should always be set using the file options only.
// The 'DisableUIConfiguration' should always be set from the file-options.
options.DisableUIConfiguration = fileOptions.DisableUIConfiguration;

options.Analyzers = fileOptions.Analyzers == null || fileOptions.Analyzers.Length == 0
Expand Down Expand Up @@ -104,6 +105,7 @@ private static bool HasConnectionInfo(AzureAISearchDefaultOptions options)
return false;
}

return options.AuthenticationType != AzureAIAuthenticationType.ApiKey || !string.IsNullOrEmpty(options.Credential?.Key);
return options.AuthenticationType != AzureAIAuthenticationType.ApiKey ||
!string.IsNullOrEmpty(options.Credential?.Key);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Azure;
using Azure.Search.Documents.Indexes.Models;
Expand All @@ -15,27 +14,37 @@

namespace OrchardCore.Search.AzureAI.Services;

public class AzureAISearchIndexManager(
AzureAIClientFactory clientFactory,
ILogger<AzureAISearchIndexManager> logger,
IOptions<AzureAISearchDefaultOptions> azureAIOptions,
IEnumerable<IAzureAISearchIndexEvents> indexEvents,
IMemoryCache memoryCache,
ShellSettings shellSettings)
public class AzureAISearchIndexManager
{
public const string OwnerKey = "Content__ContentItem__Owner";
public const string AuthorKey = "Content__ContentItem__Author";
public const string FullTextKey = "Content__ContentItem__FullText";
public const string DisplayTextAnalyzedKey = "Content__ContentItem__DisplayText__Analyzed";

private const string _prefixCacheKey = "AzureAISearchIndexesPrefix";

private readonly AzureAIClientFactory _clientFactory = clientFactory;
private readonly ILogger _logger = logger;
private readonly IEnumerable<IAzureAISearchIndexEvents> _indexEvents = indexEvents;
private readonly IMemoryCache _memoryCache = memoryCache;
private readonly ShellSettings _shellSettings = shellSettings;
private readonly AzureAISearchDefaultOptions _azureAIOptions = azureAIOptions.Value;
private readonly AzureAIClientFactory _clientFactory;
private readonly ILogger _logger;
private readonly IEnumerable<IAzureAISearchIndexEvents> _indexEvents;
private readonly IMemoryCache _memoryCache;
private readonly ShellSettings _shellSettings;
private readonly AzureAISearchDefaultOptions _azureAIOptions;
private readonly string _prefixCacheKey;

public AzureAISearchIndexManager(
AzureAIClientFactory clientFactory,
ILogger<AzureAISearchIndexManager> logger,
IEnumerable<IAzureAISearchIndexEvents> indexEvents,
IMemoryCache memoryCache,
ShellSettings shellSettings,
IOptions<AzureAISearchDefaultOptions> azureAIOptions)
{
_clientFactory = clientFactory;
_logger = logger;
_indexEvents = indexEvents;
_memoryCache = memoryCache;
_shellSettings = shellSettings;
_azureAIOptions = azureAIOptions.Value;
_prefixCacheKey = $"AzureAISearchIndexesPrefix_{shellSettings.Name}";
}

public async Task<bool> CreateAsync(AzureAISearchIndexSettings settings)
{
Expand All @@ -54,15 +63,15 @@ public async Task<bool> CreateAsync(AzureAISearchIndexSettings settings)

var client = _clientFactory.CreateSearchIndexClient();

var response = client.CreateIndexAsync(searchIndex);
await client.CreateIndexAsync(searchIndex);

await _indexEvents.InvokeAsync((handler, ctx) => handler.CreatedAsync(ctx), context, _logger);

return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unable to create index in Azure AI Search.");
_logger.LogError(ex, "Unable to create index in Azure AI Search. Message: {Message}", ex.Message);
}

return false;
Expand Down Expand Up @@ -113,7 +122,7 @@ public async Task<bool> DeleteAsync(string indexName)

var client = _clientFactory.CreateSearchIndexClient();

var response = await client.DeleteIndexAsync(context.IndexFullName);
await client.DeleteIndexAsync(context.IndexFullName);

await _indexEvents.InvokeAsync((handler, ctx) => handler.RemovedAsync(ctx), context, _logger);

Expand Down Expand Up @@ -144,7 +153,7 @@ public async Task RebuildAsync(AzureAISearchIndexSettings settings)

var searchIndex = GetSearchIndex(context.IndexFullName, settings);

var response = await client.CreateIndexAsync(searchIndex);
await client.CreateIndexAsync(searchIndex);

await _indexEvents.InvokeAsync((handler, ctx) => handler.RebuiltAsync(ctx), context, _logger);
}
Expand All @@ -165,21 +174,22 @@ private string GetIndexPrefix()
{
if (!_memoryCache.TryGetValue<string>(_prefixCacheKey, out var value))
{
var builder = new StringBuilder();
var prefix = _shellSettings.Name.ToLowerInvariant();

if (!string.IsNullOrWhiteSpace(_azureAIOptions.IndexesPrefix))
{
builder.Append(_azureAIOptions.IndexesPrefix.ToLowerInvariant());
builder.Append('-');
prefix = $"{_azureAIOptions.IndexesPrefix.ToLowerInvariant()}-{prefix}";
}

builder.Append(_shellSettings.Name.ToLowerInvariant());

if (AzureAISearchIndexNamingHelper.TryGetSafePrefix(builder.ToString(), out var safePrefix))
if (AzureAISearchIndexNamingHelper.TryGetSafePrefix(prefix, out var safePrefix))
{
value = safePrefix;
_memoryCache.Set(_prefixCacheKey, safePrefix);
}
else
{
throw new InvalidOperationException($"Unable to create a safe index prefix for AI Search. Attempted to created a safe name using '{safePrefix}'.");
}
}

return value ?? string.Empty;
Expand Down

0 comments on commit d19d39d

Please sign in to comment.