Skip to content

Commit

Permalink
[ODS-6480] Schrödinger’s studentContactAssociation (#1124)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmcelhanon authored Aug 27, 2024
1 parent fa6b3e3 commit 80cc2da
Show file tree
Hide file tree
Showing 7 changed files with 1,063 additions and 74 deletions.
45 changes: 25 additions & 20 deletions Application/EdFi.Ods.Common/Database/Querying/QueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public QueryBuilder(Dialect dialect)
/// </summary>
/// <param name="dialect"></param>
/// <param name="parameterIndexer"></param>
private QueryBuilder(Dialect dialect, ParameterIndexer parameterIndexer)
public QueryBuilder(Dialect dialect, ParameterIndexer parameterIndexer)
{
_dialect = dialect;
_parameterIndexer = parameterIndexer;
Expand All @@ -57,6 +57,11 @@ private QueryBuilder(Dialect dialect, SqlBuilder sqlBuilder, string tableName, P

private IDictionary<string, object> Parameters { get; } = new Dictionary<string, object>();

public ParameterIndexer ParameterIndexer
{
get => _parameterIndexer;
}

/// <summary>
/// Clones the current instance with all its state so it can be used as a starting point for future queries.
/// </summary>
Expand Down Expand Up @@ -388,33 +393,33 @@ public Cte(string name, QueryBuilder queryBuilder, object parameters = null)

public object Parameters { get; }
}
}

protected class ParameterIndexer
{
private int _parameterIndex = -1;
public class ParameterIndexer
{
private int _parameterIndex = -1;

public ParameterIndexer() { }
public ParameterIndexer() { }

private ParameterIndexer(int parameterIndex)
{
_parameterIndex = parameterIndex;
}
private ParameterIndexer(int parameterIndex)
{
_parameterIndex = parameterIndex;
}

public int Increment() => Interlocked.Increment(ref _parameterIndex);
public int Increment() => Interlocked.Increment(ref _parameterIndex);

/// <summary>
/// Gets the next parameter name, incrementing the index with a call to <see cref="Increment" />.
/// </summary>
/// <returns></returns>
public string NextParameterName() => $"@p{Increment()}";
/// <summary>
/// Gets the next parameter name, incrementing the index with a call to <see cref="Increment" />.
/// </summary>
/// <returns></returns>
public string NextParameterName() => $"@p{Increment()}";

public ParameterIndexer Clone()
{
return new ParameterIndexer(_parameterIndex);
}
public ParameterIndexer Clone()
{
return new ParameterIndexer(_parameterIndex);
}
}

public class Join
{
private readonly List<(string first, string second, string @operator)> _segments =
Expand Down
14 changes: 14 additions & 0 deletions Application/EdFi.Ods.Common/Database/Querying/SqlBuilderHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

namespace EdFi.Ods.Common.Database.Querying;

public static class SqlBuilderHelpers
{
public static string Alias(this string tableName, string alias)
{
return $"{tableName} AS {alias}";
}
}
19 changes: 16 additions & 3 deletions Application/EdFi.Ods.Common/Models/Resource/ResourceProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public class ResourceProperty : ResourceMemberBase
{
private readonly ResourceMemberBase _containingMember;
private Lazy<Resource> _descriptorResource;

private Lazy<bool> _isUniqueIdUsage;

public ResourceProperty(ResourceClassBase resourceClass, EntityProperty entityProperty)
: this(null, resourceClass, entityProperty) { }

Expand All @@ -66,7 +67,7 @@ public ResourceProperty(ResourceMemberBase containingMember, ResourceClassBase r
// Assign property characteristics
IsDescriptorUsage = entityProperty.IsDescriptorUsage;
IsDirectDescriptorUsage = entityProperty.IsDirectDescriptorUsage;

IsIdentifying = entityProperty.IsIdentifying
|| (UniqueIdConventions.IsUniqueId(entityProperty.PropertyName)
&& entityProperty.Entity.IsPersonEntity()
Expand Down Expand Up @@ -128,6 +129,10 @@ private void InitializeLazyMembers()
() => EntityProperty.DescriptorEntity == null
? null
: ResourceClass?.ResourceModel?.GetResourceByFullName(EntityProperty.DescriptorEntity.FullName));

_isUniqueIdUsage = new Lazy<bool>(
() => EntityProperty.DefiningProperty.Entity.IsPersonEntity()
&& EntityProperty.DefiningProperty.Entity != ResourceClass.Entity);
}

public string DescriptorName { get; }
Expand Down Expand Up @@ -178,7 +183,15 @@ public bool IsLookup
/// Indicates whether the property represents the usage of a descriptor.
/// </summary>
public bool IsDescriptorUsage { get; }


/// <summary>
/// Indicates whether the property is a usage of a person-type unique identifier.
/// </summary>
public bool IsUniqueIdUsage
{
get => _isUniqueIdUsage.Value;
}

public bool IsServerAssigned { get; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public class ChangeQueriesDatabaseConstants
/// </summary>
public const string OldKeyValueColumnPrefix = "Old";

/// <summary>
/// Prefix applied to an USI column name containing the current value (for the tracked UniqueId value).
/// </summary>
public const string CurrentKeyValueColumnPrefix = "Current";

/// <summary>
/// Prefix applied to the identifier column name containing the new value.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using EdFi.Common.Extensions;
using EdFi.Ods.Common.Database.NamingConventions;
using EdFi.Ods.Common.Database.Querying;
using EdFi.Ods.Common.Models.Domain;
using EdFi.Ods.Common.Models.Resource;

using static EdFi.Ods.Features.ChangeQueries.ChangeQueriesDatabaseConstants;

namespace EdFi.Ods.Features.ChangeQueries.Repositories.DeletedItems
{
public class DeletedItemsQueryBuilderFactory : IDeletedItemsQueryBuilderFactory
Expand All @@ -19,19 +22,23 @@ public class DeletedItemsQueryBuilderFactory : IDeletedItemsQueryBuilderFactory
private readonly ITrackedChangesIdentifierProjectionsProvider _trackedChangesIdentifierProjectionsProvider;

private const string SourceTableAlias = "src";
private const string CurrentTableAliasPrefix = "curr";

private readonly ConcurrentDictionary<FullName, QueryBuilder> _queryBuilderByResourceName = new();

private readonly Func<QueryBuilder> _createQueryBuilder;
private readonly Func<ParameterIndexer, QueryBuilder> _createQueryBuilderWithIndexer;

public DeletedItemsQueryBuilderFactory(
IDatabaseNamingConvention namingConvention,
ITrackedChangesIdentifierProjectionsProvider trackedChangesIdentifierProjectionsProvider,
Func<QueryBuilder> createQueryBuilder)
Func<QueryBuilder> createQueryBuilder,
Func<ParameterIndexer, QueryBuilder> createQueryBuilderWithIndexer)
{
_namingConvention = namingConvention;
_trackedChangesIdentifierProjectionsProvider = trackedChangesIdentifierProjectionsProvider;
_createQueryBuilder = createQueryBuilder;
_createQueryBuilderWithIndexer = createQueryBuilderWithIndexer;
}

public QueryBuilder CreateQueryBuilder(Resource resource)
Expand All @@ -51,16 +58,62 @@ private QueryBuilder CreateBaselineQueryBuilder(Resource resource)

var identifierProjections = _trackedChangesIdentifierProjectionsProvider.GetIdentifierProjections(resource);

var baselineDeletedItemsQuery = QueryFactoryHelper.CreateBaseTrackedChangesQuery(_createQueryBuilder, _namingConvention, entity)
// USIs needing translation to "current" values
var subjectUsiProperties = entity.Identifier.Properties
.Where(p => p.DefiningProperty.Entity.IsPersonEntity())
.ToArray();

QueryBuilder baselineDeletedItemsQuery;

if (subjectUsiProperties.Any())
{
// Build the CTE query
var cteQuery = QueryFactoryHelper.CreateBaseTrackedChangesQuery(_createQueryBuilder, _namingConvention, entity);
cteQuery.Select($"{TrackedChangesAlias}.*");

int i = 0;

foreach (var usiProperty in subjectUsiProperties)
{
string uniqueIdName = usiProperty.PropertyName.ReplaceSuffix("USI", "UniqueId");

string tableName = _namingConvention.TableName(usiProperty.DefiningProperty.Entity);
string schemaName = _namingConvention.Schema(usiProperty.DefiningProperty.Entity);

cteQuery.LeftJoin(
$"{schemaName}.{tableName}".Alias($"{CurrentTableAliasPrefix}{i}"),
$"{TrackedChangesAlias}.{_namingConvention.ColumnName($"Old{uniqueIdName}")}",
$"{CurrentTableAliasPrefix}{i}.{_namingConvention.ColumnName(uniqueIdName)}");

// Current USI
cteQuery.Select(
$"{CurrentTableAliasPrefix}{i}.{_namingConvention.ColumnName(usiProperty.DefiningProperty)} AS {_namingConvention.ColumnName($"Current{usiProperty.PropertyName}")}");

i++;
}

string cteName = "TranslatedTrackedChanges";

QueryFactoryHelper.ApplyDiscriminatorCriteriaForDerivedEntities(cteQuery, entity, _namingConvention);

baselineDeletedItemsQuery = _createQueryBuilderWithIndexer(cteQuery.ParameterIndexer)
.From(cteName.Alias(TrackedChangesAlias))
.With(cteName, cteQuery);
}
else
{
baselineDeletedItemsQuery = QueryFactoryHelper.CreateBaseTrackedChangesQuery(_createQueryBuilder, _namingConvention, entity);
QueryFactoryHelper.ApplyDiscriminatorCriteriaForDerivedEntities(baselineDeletedItemsQuery, entity, _namingConvention);
}

baselineDeletedItemsQuery
.Select(
$"{ChangeQueriesDatabaseConstants.TrackedChangesAlias}.{_namingConvention.ColumnName("Id")}",
$"{ChangeQueriesDatabaseConstants.TrackedChangesAlias}.{_namingConvention.ColumnName(ChangeQueriesDatabaseConstants.ChangeVersionColumnName)}")
$"{TrackedChangesAlias}.{_namingConvention.ColumnName("Id")}",
$"{TrackedChangesAlias}.{_namingConvention.ColumnName(ChangeVersionColumnName)}")
.Select(QueryFactoryHelper.IdentifyingColumns(identifierProjections, columnGroups: ColumnGroups.OldValue))
.Distinct()
.OrderBy(
$"{ChangeQueriesDatabaseConstants.TrackedChangesAlias}.{_namingConvention.ColumnName(ChangeQueriesDatabaseConstants.ChangeVersionColumnName)}");

QueryFactoryHelper.ApplyDiscriminatorCriteriaForDerivedEntities(baselineDeletedItemsQuery, entity, _namingConvention);
$"{TrackedChangesAlias}.{_namingConvention.ColumnName(ChangeVersionColumnName)}");

// Deletes-specific query filters
ApplySourceTableExclusionForUndeletedItems();
Expand All @@ -78,7 +131,7 @@ void ApplySourceTableExclusionForUndeletedItems()
foreach (var projection in identifierProjections)
{
@join.On(
$"{ChangeQueriesDatabaseConstants.TrackedChangesAlias}.{projection.ChangeTableJoinColumnName}",
$"{TrackedChangesAlias}.{projection.ChangeTableJoinColumnName}",
$"{SourceTableAlias}.{projection.SourceTableJoinColumnName}");
}

Expand All @@ -97,9 +150,9 @@ void ApplyDeletesOnlyCriteria()
: entity).Identifier.Properties.First();

string columnName = _namingConvention.ColumnName(
$"{ChangeQueriesDatabaseConstants.NewKeyValueColumnPrefix}{firstIdentifierProperty.PropertyName}");
$"{NewKeyValueColumnPrefix}{firstIdentifierProperty.PropertyName}");

baselineDeletedItemsQuery.WhereNull($"{ChangeQueriesDatabaseConstants.TrackedChangesAlias}.{columnName}");
baselineDeletedItemsQuery.WhereNull($"{TrackedChangesAlias}.{columnName}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using EdFi.Ods.Common.Models.Domain;
using EdFi.Ods.Common.Models.Resource;

using static EdFi.Ods.Features.ChangeQueries.ChangeQueriesDatabaseConstants;

namespace EdFi.Ods.Features.ChangeQueries.Repositories
{
public class TrackedChangesIdentifierProjectionsProvider : ITrackedChangesIdentifierProjectionsProvider
Expand Down Expand Up @@ -50,7 +52,7 @@ private QueryProjection[] CreateIdentifierProjections(ResourceClassBase resource
IsDescriptorUsage = rp.IsDescriptorUsage,

// Columns for performing join to source table (if necessary)
ChangeTableJoinColumnName = _namingConvention.ColumnName($"{ChangeQueriesDatabaseConstants.OldKeyValueColumnPrefix}{changeTableJoinProperty.PropertyName}"),
ChangeTableJoinColumnName = _namingConvention.ColumnName($"{(rp.IsUniqueIdUsage ? CurrentKeyValueColumnPrefix : OldKeyValueColumnPrefix)}{changeTableJoinProperty.PropertyName}"),
SourceTableJoinColumnName = _namingConvention.ColumnName(rp.EntityProperty),
};
})
Expand Down
Loading

0 comments on commit 80cc2da

Please sign in to comment.