diff --git a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/QueryBuilderExtensions.cs b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/QueryBuilderExtensions.cs
index 833ea4e4cb..c0a022f75a 100644
--- a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/QueryBuilderExtensions.cs
+++ b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/QueryBuilderExtensions.cs
@@ -8,6 +8,8 @@
using System.Linq;
using EdFi.Ods.Common.Context;
using EdFi.Ods.Common.Database.Querying;
+using EdFi.Ods.Common.Models.Resource;
+using EdFi.Ods.Common.Providers.Queries;
using EdFi.Ods.Common.Security.Authorization;
using EdFi.Ods.Common.Security.CustomViewBased;
@@ -21,6 +23,7 @@ public static class QueryBuilderExtensions
/// Applies a join-based filter to the criteria for the specified authorization view.
///
/// The to which criteria should be applied.
+ ///
/// The named parameters to be used to satisfy additional filtering requirements.
/// The name of the view to be filtered.
/// The name of the property to be joined for the entity being queried.
@@ -30,6 +33,7 @@ public static class QueryBuilderExtensions
/// The name of the property to be used for auth View Alias name.
public static void ApplySingleColumnJoinFilter(
this QueryBuilder queryBuilder,
+ Resource resource,
IDictionary parameters,
string viewName,
string subjectEndpointName,
@@ -43,11 +47,11 @@ public static void ApplySingleColumnJoinFilter(
if (useJoinAuth)
{
- ApplySingleColumnJoinFilterUsingJoins(queryBuilder, parameters, viewName, subjectEndpointName, viewSourceEndpointName, viewTargetEndpointName, joinType, authViewAlias);
+ ApplySingleColumnJoinFilterUsingJoins(queryBuilder, resource, parameters, viewName, subjectEndpointName, viewSourceEndpointName, viewTargetEndpointName, joinType, authViewAlias);
return;
}
- ApplySingleColumnJoinFilterUsingCtes(queryBuilder, parameters, viewName, subjectEndpointName, viewSourceEndpointName,
+ ApplySingleColumnJoinFilterUsingCtes(queryBuilder, resource, parameters, viewName, subjectEndpointName, viewSourceEndpointName,
viewTargetEndpointName,
joinType,
authViewAlias);
@@ -55,6 +59,7 @@ public static void ApplySingleColumnJoinFilter(
private static void ApplySingleColumnJoinFilterUsingCtes(
this QueryBuilder queryBuilder,
+ Resource resource,
IDictionary parameters,
string viewName,
string subjectEndpointName,
@@ -90,19 +95,21 @@ private static void ApplySingleColumnJoinFilterUsingCtes(
// Add the CTE to the main query, with alias
queryBuilder.With(authViewAlias, cte);
+ var subjectJoin = GetSubjectJoinDetails(resource, subjectEndpointName);
+
// Apply join to the authorization CTE
if (joinType == JoinType.InnerJoin)
{
queryBuilder.Join(
authViewAlias,
- j => j.On($"r.{subjectEndpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
+ j => j.On($"{subjectJoin.tableAlias}.{subjectJoin.endpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
}
else if (joinType == JoinType.LeftOuterJoin)
{
queryBuilder.LeftJoin(
authViewAlias,
- j => j.On($"r.{subjectEndpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
-
+ j => j.On($"{subjectJoin.tableAlias}.{subjectJoin.endpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
+
queryBuilder.Where(qb => qb.WhereNotNull($"{authViewAlias}.{viewTargetEndpointName}"));
}
else
@@ -113,6 +120,7 @@ private static void ApplySingleColumnJoinFilterUsingCtes(
private static void ApplySingleColumnJoinFilterUsingJoins(
QueryBuilder queryBuilder,
+ Resource resource,
IDictionary parameters,
string viewName,
string subjectEndpointName,
@@ -129,18 +137,20 @@ private static void ApplySingleColumnJoinFilterUsingJoins(
authViewAlias = string.IsNullOrWhiteSpace(authViewAlias) ? $"authView{viewName}" : $"authView{authViewAlias}";
- // Apply authorization join using ICriteria
+ var subjectJoin = GetSubjectJoinDetails(resource, subjectEndpointName);
+
+ // Apply join to the authorization view
if (joinType == JoinType.InnerJoin)
{
queryBuilder.Join(
$"auth.{viewName} AS {authViewAlias}",
- j => j.On($"r.{subjectEndpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
+ j => j.On($"{subjectJoin.tableAlias}.{subjectJoin.tableAlias}", $"{authViewAlias}.{viewTargetEndpointName}"));
}
else if (joinType == JoinType.LeftOuterJoin)
{
queryBuilder.LeftJoin(
$"auth.{viewName} AS {authViewAlias}",
- j => j.On($"r.{subjectEndpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
+ j => j.On($"{subjectJoin.tableAlias}.{subjectJoin.endpointName}", $"{authViewAlias}.{viewTargetEndpointName}"));
}
else
{
@@ -175,6 +185,21 @@ private static void ApplySingleColumnJoinFilterUsingJoins(
}
}
+ private static (string tableAlias, string endpointName) GetSubjectJoinDetails(Resource resource, string subjectEndpointName)
+ {
+ // If the resource is derived and the subject property is identifying (part of the PK), join using the base table
+ // to take advantage of indexes that are built on the base table and not each of the derived tables.
+ if (resource.Entity.IsDerived
+ && resource.Entity.PropertyByName.TryGetValue(subjectEndpointName, out var subjectProperty)
+ && subjectProperty.IsIdentifying)
+ {
+ return (AliasConstants.BaseAlias, subjectProperty.BaseProperty.PropertyName);
+ }
+
+ return (AliasConstants.RootAlias, subjectEndpointName);
+ }
+
+
///
/// Applies a join-based filter to the for the specified custom authorization view.
///
diff --git a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/ViewBasedAuthorizationFilterDefinition.cs b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/ViewBasedAuthorizationFilterDefinition.cs
index b60fe0c655..2ffe326303 100644
--- a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/ViewBasedAuthorizationFilterDefinition.cs
+++ b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/Filters/ViewBasedAuthorizationFilterDefinition.cs
@@ -52,6 +52,7 @@ public ViewBasedAuthorizationFilterDefinition(
}
@where.ApplySingleColumnJoinFilter(
+ resource,
parameters,
viewName,
subjectEndpointNames[0],
diff --git a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/RelationshipsAuthorizationStrategyFilterDefinitionsFactory.cs b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/RelationshipsAuthorizationStrategyFilterDefinitionsFactory.cs
index 25bc5570d8..a4f863acf2 100644
--- a/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/RelationshipsAuthorizationStrategyFilterDefinitionsFactory.cs
+++ b/Application/EdFi.Ods.Api/Security/AuthorizationStrategies/Relationships/RelationshipsAuthorizationStrategyFilterDefinitionsFactory.cs
@@ -70,7 +70,7 @@ protected IEnumerable CreateAllEducation
.OrderBy(p => p)
.Select(personType => $"{personType}USI")
.ToArray();
-
+
return personUsiNames.Select(usiName =>
new ViewBasedAuthorizationFilterDefinition(
$"{RelationshipAuthorizationConventions.FilterNamePrefix}To{usiName}{authorizationPathModifier}",
diff --git a/Application/EdFi.Ods.Common/Providers/Queries/AliasConstants.cs b/Application/EdFi.Ods.Common/Providers/Queries/AliasConstants.cs
new file mode 100644
index 0000000000..7c0f6c934d
--- /dev/null
+++ b/Application/EdFi.Ods.Common/Providers/Queries/AliasConstants.cs
@@ -0,0 +1,20 @@
+// 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.Providers.Queries;
+
+public class AliasConstants
+{
+ ///
+ /// The alias used when building queries that access an aggregate root's base table.
+ ///
+ public const string BaseAlias = "b";
+
+ ///
+ /// The alias used when building queries that access an aggregate root table (which is the derived table in an
+ /// inheritance relationship, when applicable).
+ ///
+ public const string RootAlias = "r";
+}
diff --git a/Application/EdFi.Ods.Common/Providers/Queries/EntityExtensions.cs b/Application/EdFi.Ods.Common/Providers/Queries/EntityExtensions.cs
index e4a03642ce..8c0bbc515f 100644
--- a/Application/EdFi.Ods.Common/Providers/Queries/EntityExtensions.cs
+++ b/Application/EdFi.Ods.Common/Providers/Queries/EntityExtensions.cs
@@ -9,18 +9,15 @@ namespace EdFi.Ods.Common.Providers.Queries;
public static class EntityExtensions
{
- private const string BaseAlias = "b";
- private const string StandardAlias = "r";
-
///
/// Determines the appropriate table alias for the given aggregate root entity.
///
/// The aggregate root entity to determine the alias for.
///
- /// Returns "b" if the entity is derived, otherwise returns "r".
+ /// Returns if the entity is derived, otherwise returns .
///
public static string RootTableAlias(this Entity aggregateRootEntity)
{
- return aggregateRootEntity.IsDerived ? BaseAlias : StandardAlias;
+ return aggregateRootEntity.IsDerived ? AliasConstants.BaseAlias : AliasConstants.RootAlias;
}
}