From 789533e13c7c5e80e2c4ea6790c2afe693a2f0a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:43:20 +0000 Subject: [PATCH 1/2] Initial plan From d99c62df1de04f85d83bfeab92da6514ffc99e75 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:50:45 +0000 Subject: [PATCH 2/2] Optimize MongoDB query by applying filters before joins - Add EvaluateMongoDbAttributesDirect method to work directly with SCIMRepresentationAttribute - Add EvaluateAttributesDirect extension methods for all expression types - Update FindSCIMRepresentations to use direct attribute filtering without EnrichedAttribute join - This allows MongoDB to use indexes on SchemaAttributeId and value fields - Filters are now applied before expensive join operations Co-authored-by: simpleidserver <10213388+simpleidserver@users.noreply.github.com> --- .../MongoDbSCIMExpressionLinqExtensions.cs | 80 +++++++++++++++++++ .../SCIMRepresentationQueryRepository.cs | 13 +-- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/Extensions/MongoDbSCIMExpressionLinqExtensions.cs b/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/Extensions/MongoDbSCIMExpressionLinqExtensions.cs index 6f5b5c045..bd84ce1e8 100644 --- a/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/Extensions/MongoDbSCIMExpressionLinqExtensions.cs +++ b/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/Extensions/MongoDbSCIMExpressionLinqExtensions.cs @@ -109,6 +109,23 @@ public static Expression EvaluateRepresentations(this SCIMComparisonExpression e #region Filter attributes + public static IMongoQueryable EvaluateMongoDbAttributesDirect(this SCIMExpression expression, IMongoQueryable attributes) + { + var treeNodeParameter = Expression.Parameter(typeof(SCIMRepresentationAttribute), "attr"); + var anyWhereExpression = expression.EvaluateAttributesDirect(treeNodeParameter); + if (anyWhereExpression == null) return null; + var enumarableType = typeof(Queryable); + var whereMethod = enumarableType.GetMethods() + .Where(m => m.Name == "Where" && m.IsGenericMethodDefinition) + .Where(m => m.GetParameters().Count() == 2).First().MakeGenericMethod(typeof(SCIMRepresentationAttribute)); + var equalLambda = Expression.Lambda>(anyWhereExpression, treeNodeParameter); + var whereExpr = Expression.Call(whereMethod, Expression.Constant(attributes), equalLambda); + var finalSelectArg = Expression.Parameter(typeof(IQueryable), "f"); + var finalSelectRequestBody = Expression.Lambda(whereExpr, new ParameterExpression[] { finalSelectArg }); + var result = (IMongoQueryable)finalSelectRequestBody.Compile().DynamicInvoke(attributes); + return result; + } + public static IMongoQueryable EvaluateMongoDbAttributes(this SCIMExpression expression, IMongoQueryable attributes) { var treeNodeParameter = Expression.Parameter(typeof(EnrichedAttribute), "tn"); @@ -126,6 +143,17 @@ public static IMongoQueryable EvaluateMongoDbAttributes(this return result; } + public static Expression EvaluateAttributesDirect(this SCIMExpression expression, ParameterExpression parameterExpression) + { + var attrExpression = expression as SCIMAttributeExpression; + var logicalExpression = expression as SCIMLogicalExpression; + var comparisonExpression = expression as SCIMComparisonExpression; + if (attrExpression != null) return attrExpression.EvaluateAttributesDirect(parameterExpression); + if (logicalExpression != null) return logicalExpression.EvaluateAttributesDirect(parameterExpression); + if (comparisonExpression != null) return comparisonExpression.EvaluateAttributesDirect(parameterExpression); + return null; + } + public static Expression EvaluateAttributes(this SCIMExpression expression, ParameterExpression parameterExpression) { var attrExpression = expression as SCIMAttributeExpression; @@ -137,6 +165,19 @@ public static Expression EvaluateAttributes(this SCIMExpression expression, Para return null; } + public static Expression EvaluateAttributesDirect(this SCIMAttributeExpression expression, ParameterExpression parameterExpression) + { + var schemaAttributeIdProperty = Expression.Property(parameterExpression, "SchemaAttributeId"); + var equal = Expression.Equal(schemaAttributeIdProperty, Expression.Constant(expression.SchemaAttribute.Id)); + var complex = expression as SCIMComplexAttributeExpression; + if (complex != null) + { + return complex.GroupingFilter.EvaluateAttributesDirect(parameterExpression); + } + + return equal; + } + public static Expression EvaluateAttributes(this SCIMAttributeExpression expression, ParameterExpression parameterExpression) { var schemaAttributeIdProperty = Expression.Property(Expression.Property(parameterExpression, "Attribute"), "SchemaAttributeId"); @@ -150,6 +191,22 @@ public static Expression EvaluateAttributes(this SCIMAttributeExpression express return equal; } + public static Expression EvaluateAttributesDirect(this SCIMLogicalExpression expression, ParameterExpression parameterExpression) + { + var leftExpression = expression.LeftExpression.EvaluateAttributesDirect(parameterExpression); + var rightExpression = expression.RightExpression.EvaluateAttributesDirect(parameterExpression); + if (leftExpression == null && rightExpression == null) return null; + if (leftExpression != null && rightExpression == null) return leftExpression; + if (leftExpression == null && rightExpression != null) return rightExpression; + switch (expression.LogicalOperator) + { + case SCIMLogicalOperators.AND: + return Expression.AndAlso(leftExpression, rightExpression); + default: + return Expression.OrElse(leftExpression, rightExpression); + } + } + public static Expression EvaluateAttributes(this SCIMLogicalExpression expression, ParameterExpression parameterExpression) { var leftExpression = expression.LeftExpression.EvaluateAttributes(parameterExpression); @@ -166,6 +223,29 @@ public static Expression EvaluateAttributes(this SCIMLogicalExpression expressio } } + public static Expression EvaluateAttributesDirect(this SCIMComparisonExpression expression, ParameterExpression parameterExpression) + { + var lastChild = expression.LeftExpression.GetLastChild(); + if (ParserConstants.MappingStandardAttributePathToProperty.ContainsKey(lastChild.GetFullPath())) return null; + var schemaAttributeId = Expression.Property(parameterExpression, "SchemaAttributeId"); + var propertyValueString = Expression.Property(parameterExpression, "ValueString"); + var propertyValueInteger = Expression.Property(parameterExpression, "ValueInteger"); + var propertyValueDatetime = Expression.Property(parameterExpression, "ValueDateTime"); + var propertyValueBoolean = Expression.Property(parameterExpression, "ValueBoolean"); + var propertyValueDecimal = Expression.Property(parameterExpression, "ValueDecimal"); + var propertyValueBinary = Expression.Property(parameterExpression, "ValueBinary"); + var comparison = SCIMExpressionLinqExtensions.BuildComparisonExpression(expression, lastChild.SchemaAttribute, + propertyValueString, + propertyValueInteger, + propertyValueDatetime, + propertyValueBoolean, + propertyValueDecimal, + propertyValueBinary, + parameterExpression, + true); + return Expression.And(Expression.Equal(schemaAttributeId, Expression.Constant(lastChild.SchemaAttribute.Id)), comparison); + } + public static Expression EvaluateAttributes(this SCIMComparisonExpression expression, ParameterExpression parameterExpression) { var lastChild = expression.LeftExpression.GetLastChild(); diff --git a/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/SCIMRepresentationQueryRepository.cs b/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/SCIMRepresentationQueryRepository.cs index f97506b36..700ae16ac 100644 --- a/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/SCIMRepresentationQueryRepository.cs +++ b/src/Scim/SimpleIdServer.Scim.Persistence.MongoDB/SCIMRepresentationQueryRepository.cs @@ -30,20 +30,13 @@ public async Task FindSCIMRepresentations(Sea int total = 0; if (parameter.Filter != null) { - var filteredRepresentationAttributes = from a in _scimDbContext.SCIMRepresentationAttributeLst.AsQueryable() - join b in _scimDbContext.SCIMRepresentationAttributeLst.AsQueryable() on a.ParentAttributeId equals b.Id into Parents - select new EnrichedAttribute - { - Attribute = a, - Parent = Parents.First(), - Children = new List() - }; - filteredRepresentationAttributes = parameter.Filter.EvaluateMongoDbAttributes(filteredRepresentationAttributes); + var filteredRepresentationAttributes = _scimDbContext.SCIMRepresentationAttributeLst.AsQueryable(); + filteredRepresentationAttributes = parameter.Filter.EvaluateMongoDbAttributesDirect(filteredRepresentationAttributes); if (filteredRepresentationAttributes != null) { filteredRepresentationIds = (await filteredRepresentationAttributes - .Select(a => a.Attribute.RepresentationId) + .Select(a => a.RepresentationId) .ToMongoListAsync()) .Distinct() .ToList();