Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Adds translation support for single key single value select GROUP BY LINQ queries #4074

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e1ceebb
preliminary change
Aug 25, 2023
8c2d289
Add some more boiler plate code
Aug 29, 2023
ccba9f2
move all linq test to the same folder; add some groupBy test
Aug 29, 2023
72b315c
fix references error in test refactoring
Aug 30, 2023
d962b02
preliminary for the groupby functions with key and value selector
Sep 7, 2023
36d668e
trying to change collection inputs for group by
Sep 11, 2023
73ea3c6
WIP bookmark
Sep 12, 2023
dc50d78
Successfully ignore "key"
Sep 18, 2023
e42ae59
clean up code
Sep 19, 2023
2244764
Sucessfully bind the case of group by with only key selector and no v…
Sep 19, 2023
df9362e
enable one group by test
Sep 26, 2023
7790ab7
merge with master
Sep 26, 2023
da48bfc
Merge branch 'master' into users/leminh/LINQGroupBySupport
Sep 26, 2023
44d3736
add support for aggregate value selector
Sep 26, 2023
7631e82
added baseline
Sep 27, 2023
b4b1658
working on adding support for multivalue value selector and key selector
Sep 27, 2023
787839d
code clean up
Oct 13, 2023
7da5772
Merge branch 'master' into users/leminh/LINQGroupBySupport
Oct 13, 2023
e8f8f62
more clean up
Oct 13, 2023
f589069
more clean up
Oct 13, 2023
bca5953
update test
Oct 13, 2023
544931a
Move test to separate file
Dec 11, 2023
a7888d8
code clean up
Dec 11, 2023
6b7fb50
remove baseline file that got moved
Dec 11, 2023
d23c29d
merge with master
Dec 11, 2023
5a4470b
fix merge issue
Dec 11, 2023
84e4c2f
merge with master
Jan 25, 2024
1e80e2d
Changes test infrastructure to reflect changes from Master
Jan 25, 2024
0b08efd
address code review part 1
Jan 25, 2024
9a6eacf
Address code review 2 and adds code coverage
Jan 25, 2024
f73baf1
Merge branch 'master' into users/leminh/LINQGroupBySupport
leminh98 Jan 25, 2024
9f77356
Addressed code review and added tests. Still a couple of bugs to iron…
Feb 6, 2024
105a335
Merge branch 'users/leminh/LINQGroupBySupport' of https://github.com/…
Feb 6, 2024
25c7080
resolve merge conflict with master
Feb 6, 2024
430f96a
Fix group by translation issue and add more test
Feb 7, 2024
d1ec2c5
update comments
Mar 6, 2024
fc32e3a
address pr comment
Apr 1, 2024
33a2c9b
Merge branch 'master' into users/leminh/LINQGroupBySupport
leminh98 Apr 1, 2024
2444be7
Merge branch 'master' into users/leminh/LINQGroupBySupport
adityasa Apr 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static class LinqMethods
public const string Count = "Count";
public const string Max = "Max";
public const string Min = "Min";
public const string GroupBy = "GroupBy";
public const string OrderBy = "OrderBy";
public const string ThenBy = "ThenBy";
public const string OrderByDescending = "OrderByDescending";
Expand Down Expand Up @@ -1198,6 +1199,13 @@ private static Collection VisitMethodCall(MethodCallExpression inputExpression,
{
context.currentQuery = context.PackageCurrentQueryIfNeccessary();
result = ExpressionToSql.VisitSelectMany(inputExpression.Arguments, context);
break;
}
case LinqMethods.GroupBy:
{
SqlGroupByClause groupBy = ExpressionToSql.VisitGroupBy(inputExpression.Arguments, context);
context.currentQuery = context.currentQuery.AddGroupByClause(groupBy, context);

break;
}
case LinqMethods.OrderBy:
Expand Down Expand Up @@ -1380,6 +1388,7 @@ private static bool IsSubqueryScalarExpression(Expression expression, out Subque
case LinqMethods.Skip:
case LinqMethods.Take:
case LinqMethods.Distinct:
case LinqMethods.GroupBy:
isSubqueryExpression = true;
expressionObjKind = SubqueryKind.ArrayScalarExpression;
break;
Expand Down Expand Up @@ -1409,7 +1418,7 @@ private static SqlScalarExpression VisitScalarExpression(LambdaExpression lambda
}

/// <summary>
/// Visit an lambda expression which is in side a lambda and translate it to a scalar expression or a collection scalar expression.
/// Visit an lambda expression which is inside a lambda and translate it to a scalar expression or a collection scalar expression.
/// If it is a collection scalar expression, e.g. should be translated to subquery such as SELECT VALUE ARRAY, SELECT VALUE EXISTS,
/// SELECT VALUE [aggregate], the subquery will be aliased to a new binding for the FROM clause. E.g. consider
/// Select(family => family.Children.Select(child => child.Grade)). Since the inner Select corresponds to a subquery, this method would
Expand Down Expand Up @@ -1676,6 +1685,60 @@ private static Collection VisitSelectMany(ReadOnlyCollection<Expression> argumen
return collection;
}

private static SqlGroupByClause VisitGroupBy(ReadOnlyCollection<Expression> arguments, TranslationContext context)
{
if (arguments.Count != 3)
{
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.InvalidArgumentsCount, LinqMethods.GroupBy, 3, arguments.Count));
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
}

// First argument is input, second is key selector and third is value selector
LambdaExpression keySelectorLambda = Utilities.GetLambda(arguments[1]);
SqlScalarExpression keySelectorFunc = ExpressionToSql.VisitScalarExpression(keySelectorLambda, context);

// TODO - We need special treatment for this binding
LambdaExpression valueSelectorLambda = Utilities.GetLambda(arguments[2]);
SqlScalarExpression valueSelectorFunc = ExpressionToSql.VisitScalarExpression(valueSelectorLambda, context);

SqlGroupByClause groupby = SqlGroupByClause.Create(keySelectorFunc, valueSelectorFunc);

// First, we need to fully translate the scalar expression
// then we can use the return type, and the scalar expression collection name to create new binding

//Collection collection = ExpressionToSql.ConvertToCollection(sqlfunc);
//context.PushCollection(collection);
//ParameterExpression parameter = context.GenFreshParameter(sqlfunc.GetType(), ExpressionToSql.GetBindingParameterName(context));
//context.PushParameter(parameter, context.CurrentSubqueryBinding.ShouldBeOnNewQuery);
//context.PopParameter();
//context.PopCollection();

//SqlQuery query = context.currentQuery.FlattenAsPossible().GetSqlQuery();
//SqlCollection subqueryCollection = SqlSubqueryCollection.Create(query);

//ParameterExpression parameterExpression = context.GenFreshParameter(typeof(object), ExpressionToSql.DefaultParameterName);
//Binding binding = new Binding(parameterExpression, subqueryCollection, isInCollection: false, isInputParameter: true);

//context.currentQuery = new QueryUnderConstruction(context.GetGenFreshParameterFunc());
//context.currentQuery.AddBinding(binding);

//result = new Collection(LinqMethods.GroupBy);

//====================================
// Once we have visit a group by query, then the input need to be set

//Type elemType = TypeSystem.GetElementType(inputExpression.Type);
//context.SetInputParameter(elemType, ParameterSubstitution.InputParameterName); // ignore result

//// First outer collection
//Collection result = new Collection(ExpressionToSql.SqlRoot);

// Set the input param of the current query to whatever the lambda produce
//context.currentQuery.fromParameters.SetInputParameter(lambda.ReturnType, context.currentQuery.GetInputParameterInContext(isInNewQuery: false).Name, context.InScope);
//context.SetInputParameter(TypeSystem.GetElementType(lambda.ReturnType), context.currentQuery.GetInputParameterInContext(isInNewQuery: false).Name); // ignore result
//context.currentQuery.fromParameters.SetInputParameter(lambda.ReturnType, context.currentQuery.GetInputParameterInContext(isInNewQuery: false).Name, context.InScope);
return groupby;
}

private static SqlOrderByClause VisitOrderBy(ReadOnlyCollection<Expression> arguments, bool isDescending, TranslationContext context)
{
if (arguments.Count != 2)
Expand Down
Loading
Loading