diff --git a/src/Core/Models/GraphQLFilterParsers.cs b/src/Core/Models/GraphQLFilterParsers.cs
index 153def832f..779ec64f33 100644
--- a/src/Core/Models/GraphQLFilterParsers.cs
+++ b/src/Core/Models/GraphQLFilterParsers.cs
@@ -419,10 +419,26 @@ private void HandleNestedFilterForSql(
predicatesForExistsQuery.Push(existsQueryFilterPredicate);
// Add JoinPredicates to the subquery query structure so a predicate connecting
- // the outer table is added to the where clause of subquery
- existsQuery.AddJoinPredicatesForRelatedEntity(
- targetEntityName: queryStructure.EntityName,
- relatedSourceAlias: queryStructure.SourceAlias,
+ // the outer table is added to the where clause of subquery.
+ // For self-referencing relationships (e.g., parent/child hierarchy), we need to use
+ // the relationship name to look up the correct foreign key definition.
+ // The parent query (queryStructure) calls AddJoinPredicatesForRelationship which adds
+ // predicates to the subquery (existsQuery), connecting queryStructure.SourceAlias to existsQuery.SourceAlias.
+ string relationshipName = filterField.Name;
+ EntityRelationshipKey fkLookupKey = new(queryStructure.EntityName, relationshipName);
+
+ if (queryStructure is not BaseSqlQueryStructure sqlQueryStructure)
+ {
+ throw new DataApiBuilderException(
+ message: "Expected SQL query structure for nested filter processing.",
+ statusCode: HttpStatusCode.InternalServerError,
+ subStatusCode: DataApiBuilderException.SubStatusCodes.UnexpectedError);
+ }
+
+ sqlQueryStructure.AddJoinPredicatesForRelationship(
+ fkLookupKey: fkLookupKey,
+ targetEntityName: nestedFilterEntityName,
+ subqueryTargetTableAlias: existsQuery.SourceAlias,
subQuery: existsQuery);
// The right operand is the SqlExistsQueryStructure.
diff --git a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs
index 90e46940a9..ccc4e1efea 100644
--- a/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs
+++ b/src/Service.Tests/SqlTests/GraphQLFilterTests/MsSqlGQLFilterTests.cs
@@ -355,6 +355,51 @@ WHERE [table3].[name] IN ('Aniruddh')
await TestNestedFilterWithOrAndIN(existsPredicate, roleName: "authenticated");
}
+ ///
+ /// Test Nested Filter for Self-Referencing relationship
+ /// Tests that nested filters work correctly on self-referencing relationships (e.g., parent/child hierarchy).
+ /// Uses DimAccount table with parent_account relationship.
+ ///
+ [TestMethod]
+ public async Task TestNestedFilterSelfReferencing()
+ {
+ // This query should find all accounts whose parent account has AccountKey = 1
+ // Expected to return account with AccountKey 2 (direct child of account 1)
+ string existsPredicate = $@"
+ EXISTS( SELECT 1
+ FROM {GetPreIndentDefaultSchema()}[DimAccount] AS [table1]
+ WHERE [table1].[AccountKey] = 1
+ AND [table0].[ParentAccountKey] = [table1].[AccountKey] )";
+
+ string graphQLQueryName = "dbo_DimAccounts";
+ // Gets all the accounts that have a parent account with AccountKey = 1
+ string gqlQuery = @"{
+ dbo_DimAccounts (" + QueryBuilder.FILTER_FIELD_NAME + ": {" +
+ @"parent_account: { AccountKey: { eq: 1 }}})
+ {
+ items {
+ AccountKey
+ ParentAccountKey
+ }
+ }
+ }";
+
+ string dbQuery = MakeQueryOn(
+ table: "DimAccount",
+ queriedColumns: new List { "AccountKey", "ParentAccountKey" },
+ existsPredicate,
+ GetDefaultSchema(),
+ pkColumns: new List { "AccountKey" });
+
+ JsonElement actual = await ExecuteGraphQLRequestAsync(
+ gqlQuery,
+ graphQLQueryName,
+ isAuthenticated: false);
+
+ string expected = await GetDatabaseResultAsync(dbQuery);
+ SqlTestHelper.PerformTestEqualJsonStrings(expected, actual.ToString());
+ }
+
///
/// Gets the default schema for
/// MsSql.