Files
git.stella-ops.org/src/AdvisoryAI/StellaOps.AdvisoryAI/UnifiedSearch/EntityAliasService.cs

95 lines
3.3 KiB
C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Npgsql;
using StellaOps.AdvisoryAI.KnowledgeSearch;
namespace StellaOps.AdvisoryAI.UnifiedSearch;
internal sealed class EntityAliasService : IEntityAliasService
{
private readonly KnowledgeSearchOptions _options;
private readonly ILogger<EntityAliasService> _logger;
private readonly Lazy<NpgsqlDataSource?> _dataSource;
public EntityAliasService(
IOptions<KnowledgeSearchOptions> options,
ILogger<EntityAliasService> logger)
{
ArgumentNullException.ThrowIfNull(options);
_options = options.Value ?? new KnowledgeSearchOptions();
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_dataSource = new Lazy<NpgsqlDataSource?>(() =>
{
if (!_options.Enabled || string.IsNullOrWhiteSpace(_options.ConnectionString))
{
return null;
}
return new NpgsqlDataSourceBuilder(_options.ConnectionString).Build();
}, isThreadSafe: true);
}
public async Task<IReadOnlyList<(string EntityKey, string EntityType)>> ResolveAliasesAsync(
string alias,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(alias) || _dataSource.Value is null)
{
return [];
}
const string sql = """
SELECT entity_key, entity_type
FROM advisoryai.entity_alias
WHERE lower(alias) = lower(@alias)
ORDER BY entity_key, entity_type;
""";
await using var command = _dataSource.Value.CreateCommand(sql);
command.CommandTimeout = 10;
command.Parameters.AddWithValue("alias", alias.Trim());
var results = new List<(string, string)>();
await using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
results.Add((reader.GetString(0), reader.GetString(1)));
}
return results;
}
public async Task RegisterAliasAsync(
string entityKey,
string entityType,
string alias,
string source,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(entityKey) ||
string.IsNullOrWhiteSpace(entityType) ||
string.IsNullOrWhiteSpace(alias) ||
_dataSource.Value is null)
{
return;
}
const string sql = """
INSERT INTO advisoryai.entity_alias (alias, entity_key, entity_type, source, created_at)
VALUES (@alias, @entity_key, @entity_type, @source, NOW())
ON CONFLICT (alias, entity_key) DO UPDATE SET
entity_type = EXCLUDED.entity_type,
source = EXCLUDED.source;
""";
await using var command = _dataSource.Value.CreateCommand(sql);
command.CommandTimeout = 10;
command.Parameters.AddWithValue("alias", alias.Trim());
command.Parameters.AddWithValue("entity_key", entityKey.Trim());
command.Parameters.AddWithValue("entity_type", entityType.Trim());
command.Parameters.AddWithValue("source", source.Trim());
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
}