Skip to content

Commit

Permalink
Merge pull request #133 from Geta/feature/add-content-url-history-index
Browse files Browse the repository at this point in the history
Add content URL history hash index
  • Loading branch information
jevgenijsp authored Oct 1, 2024
2 parents 9529c5b + dd04d60 commit bf2d510
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
using Geta.NotFoundHandler.Optimizely.Core.AutomaticRedirects;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Security.Cryptography;
using System.Text;

namespace Geta.NotFoundHandler.Optimizely.Data
{
public class SqlContentUrlHistoryRepository : IRepository<ContentUrlHistory>, IContentUrlHistoryLoader
{
private const string ContentUrlHistoryTable = "[dbo].[NotFoundHandler.ContentUrlHistory]";
private const string AllFields = "Id, ContentKey, Urls, CreatedUtc";
private const string AllFields = "Id, ContentKey, Urls, CreatedUtc, md5_ContentKey";
private readonly IDataExecutor _dataExecutor;

public SqlContentUrlHistoryRepository(IDataExecutor dataExecutor)
Expand All @@ -35,33 +37,46 @@ private static JsonSerializerSettings JsonSettings
return settings;
}
}

private static byte[] CalculateMd5Hash(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.Unicode.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);

return hashBytes;
}

public bool IsRegistered(ContentUrlHistory entity)
{
var sqlCommand = $@"SELECT TOP 1 {AllFields}
FROM {ContentUrlHistoryTable}
WHERE ContentKey = @contentKey
WHERE ContentKey = @contentKey AND md5_ContentKey = @contentKeyHash
ORDER BY CreatedUtc DESC";

var dataTable = _dataExecutor.ExecuteQuery(
sqlCommand,
_dataExecutor.CreateStringParameter("contentKey", entity.ContentKey));
_dataExecutor.CreateStringParameter("contentKey", entity.ContentKey),
_dataExecutor.CreateBinaryParameter("contentKeyHash", CalculateMd5Hash(entity.ContentKey))
);

var last = ToContentUrlHistory(dataTable).FirstOrDefault();

return last != null && last.Urls.Count == entity.Urls.Count && last.Urls.All(entity.Urls.Contains);
var result = last != null && last.Urls.Count == entity.Urls.Count && last.Urls.All(entity.Urls.Contains);

return result;
}

public IEnumerable<(string contentKey, IReadOnlyCollection<ContentUrlHistory> histories)> GetAllMoved()
{
var sqlCommand = $@"SELECT h.Id, h.ContentKey, h.Urls, h.CreatedUtc
var sqlCommand = $@"SELECT h.Id, h.ContentKey, h.Urls, h.CreatedUtc, h.md5_ContentKey
FROM {ContentUrlHistoryTable} h
INNER JOIN
(SELECT ContentKey
(SELECT ContentKey, md5_ContentKey
FROM {ContentUrlHistoryTable}
GROUP BY ContentKey
GROUP BY ContentKey, md5_ContentKey
HAVING COUNT(*) > 1) k
ON h.ContentKey = k.ContentKey
ON h.ContentKey = k.ContentKey AND h.md5_ContentKey = k.md5_ContentKey
ORDER BY h.ContentKey, h.CreatedUtc DESC";

var dataTable = _dataExecutor.ExecuteQuery(sqlCommand);
Expand All @@ -73,18 +88,24 @@ HAVING COUNT(*) > 1) k

public IReadOnlyCollection<ContentUrlHistory> GetMoved(string contentKey)
{
var sqlCommand = $@"SELECT h.Id, h.ContentKey, h.Urls, h.CreatedUtc
var contentKeyHash = CalculateMd5Hash(contentKey);

var sqlCommand = $@"SELECT h.Id, h.ContentKey, h.Urls, h.CreatedUtc, h.md5_ContentKey
FROM {ContentUrlHistoryTable} h
INNER JOIN
(SELECT ContentKey
FROM {ContentUrlHistoryTable}
WHERE md5_ContentKey = @contentKeyHash
GROUP BY ContentKey
HAVING COUNT(*) > 1) k
ON h.ContentKey = k.ContentKey
WHERE h.ContentKey = @contentKey
WHERE h.ContentKey = @contentKey AND h.md5_ContentKey = @contentKeyHash
ORDER BY h.ContentKey, h.CreatedUtc DESC";

var dataTable = _dataExecutor.ExecuteQuery(sqlCommand, _dataExecutor.CreateStringParameter("contentKey", contentKey));
var dataTable = _dataExecutor.ExecuteQuery(sqlCommand,
_dataExecutor.CreateStringParameter("contentKey", contentKey),
_dataExecutor.CreateBinaryParameter("contentKeyHash", contentKeyHash)
);

var histories = ToContentUrlHistory(dataTable);

Expand Down Expand Up @@ -124,7 +145,8 @@ private void Create(ContentUrlHistory entity)
_dataExecutor.CreateGuidParameter("id", entity.Id),
_dataExecutor.CreateStringParameter("contentKey", entity.ContentKey),
_dataExecutor.CreateStringParameter("urls", ToJson(entity.Urls), -1),
_dataExecutor.CreateDateTimeParameter("createdUtc", entity.CreatedUtc));
_dataExecutor.CreateDateTimeParameter("createdUtc", entity.CreatedUtc)
);
}

private void Update(ContentUrlHistory entity)
Expand All @@ -145,7 +167,8 @@ private void Update(ContentUrlHistory entity)
_dataExecutor.CreateGuidParameter("id", entity.Id),
_dataExecutor.CreateStringParameter("contentKey", entity.ContentKey),
_dataExecutor.CreateStringParameter("urls", ToJson(entity.Urls), -1),
_dataExecutor.CreateDateTimeParameter("createdUtc", entity.CreatedUtc));
_dataExecutor.CreateDateTimeParameter("createdUtc", entity.CreatedUtc)
);
}

private static string ToJson(ICollection<TypedUrl> urls)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Geta.NotFoundHandler.Optimizely.Infrastructure.Configuration
{
public class OptimizelyNotFoundHandlerOptions
{
public const int CurrentDbVersion = 1;
public const int CurrentDbVersion = 2;

public bool AutomaticRedirectsEnabled { get; set; }
public RedirectType AutomaticRedirectType { get; set; } = RedirectType.Temporary;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under Apache-2.0. See the LICENSE file in the project root for more information

using Geta.NotFoundHandler.Data;
using Geta.NotFoundHandler.Infrastructure.Configuration;
using Geta.NotFoundHandler.Optimizely.Infrastructure.Configuration;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -50,9 +49,14 @@ public void Start()
/// </summary>
private void Create()
{
var created = CreateContentUrlHistoryTable();
var result = CreateContentUrlHistoryTable();

if (created)
if (result)
{
result = CreateContentKeyMd5Column();
}

if (result)
{
CreateVersionNumberSp();
}
Expand All @@ -76,14 +80,19 @@ CONSTRAINT [PK_ContentUrlHistory] PRIMARY KEY CLUSTERED ([Id] ASC) ON [PRIMARY]

private void Upgrade()
{
UpdateVersionNumber();
var result = CreateContentKeyMd5Column();

if (result)
{
UpdateVersionNumber();
}
}

private void CreateVersionNumberSp()
{
_logger.LogInformation("Create Optimizely NotFoundHandler version SP START");
var versionSp =
$@"CREATE PROCEDURE {VersionProcedure} AS RETURN {NotFoundHandlerOptions.CurrentDbVersion}";
$@"CREATE PROCEDURE {VersionProcedure} AS RETURN {OptimizelyNotFoundHandlerOptions.CurrentDbVersion}";

var created = _dataExecutor.ExecuteNonQuery(versionSp);

Expand All @@ -105,8 +114,35 @@ private int GetVersionNumber()
private void UpdateVersionNumber()
{
var versionSp =
$@"ALTER PROCEDURE {VersionProcedure} AS RETURN {NotFoundHandlerOptions.CurrentDbVersion}";
$@"ALTER PROCEDURE {VersionProcedure} AS RETURN {OptimizelyNotFoundHandlerOptions.CurrentDbVersion}";
_dataExecutor.ExecuteNonQuery(versionSp);
}

private bool CreateContentKeyMd5Column()
{
var command = $@"
BEGIN TRY
BEGIN TRANSACTION;
IF NOT EXISTS (
SELECT 1
FROM sys.columns
WHERE object_id = OBJECT_ID('{ContentUrlHistoryTable}')
AND name = 'md5_ContentKey'
)
BEGIN
ALTER TABLE {ContentUrlHistoryTable} ADD md5_ContentKey AS HASHBYTES('MD5', [ContentKey]);
CREATE INDEX PContentKey_index ON {ContentUrlHistoryTable} (md5_ContentKey);
END
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
THROW;
END CATCH;
";

return _dataExecutor.ExecuteNonQuery(command);
}
}
}
1 change: 1 addition & 0 deletions src/Geta.NotFoundHandler/Data/IDataExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public interface IDataExecutor
DbParameter CreateIntParameter(string name, int value);
DbParameter CreateBoolParameter(string name, bool value);
DbParameter CreateDateTimeParameter(string name, DateTime value);
DbParameter CreateBinaryParameter(string name, byte[] value, int size = 8000);
}
}
7 changes: 7 additions & 0 deletions src/Geta.NotFoundHandler/Data/SqlDataExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ public DbParameter CreateDateTimeParameter(string name, DateTime value)
return parameter;
}

public DbParameter CreateBinaryParameter(string name, byte[] value, int size = 8000)
{
var parameter = CreateParameter(name, DbType.Binary, size);
parameter.Value = value;
return parameter;
}

private static SqlParameter CreateReturnParameter()
{
var parameter = new SqlParameter
Expand Down

0 comments on commit bf2d510

Please sign in to comment.