Skip to content

Commit

Permalink
Query: Adds translation support for single key single value select GR…
Browse files Browse the repository at this point in the history
…OUP BY LINQ queries (#4074)

* preliminary change

* Add some more boiler plate code

* move all linq test to the same folder; add some groupBy test

* fix references error in test refactoring

add code for group by substitution. Still need to adjust binding post groupby

* preliminary for the groupby functions with key and value selector

* trying to change collection inputs for group by

* WIP bookmark

* Successfully ignore "key"

* clean up code

* Sucessfully bind the case of group by with only key selector and no value selector followed by an optional select clause

* enable one group by test

* add support for aggregate value selector

* added baseline

* working on adding support for multivalue value selector and key selector

* code clean up

* more clean up

* more clean up

* update test

* Move test to separate file

* code clean up

* remove baseline file that got moved

* fix merge issue

* Changes test infrastructure to reflect changes from Master

* address code review part 1

* Address code review 2 and adds code coverage

* Addressed code review and added tests. Still a couple of bugs to iron out

* Fix group by translation issue and add more test

* update comments

* address pr comment

---------

Co-authored-by: Minh Le <leminh@microsoft.com>
Co-authored-by: Aditya <adityasa@users.noreply.github.com>
  • Loading branch information
3 people committed Apr 1, 2024
1 parent 5788326 commit 15d83a7
Show file tree
Hide file tree
Showing 11 changed files with 1,789 additions and 965 deletions.
157 changes: 140 additions & 17 deletions Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs

Large diffs are not rendered by default.

143 changes: 108 additions & 35 deletions Microsoft.Azure.Cosmos/src/Linq/QueryUnderConstruction.cs

Large diffs are not rendered by default.

35 changes: 28 additions & 7 deletions Microsoft.Azure.Cosmos/src/Linq/TranslationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ internal sealed class TranslationContext
/// </summary>
public IDictionary<object, string> Parameters;

/// <summary>
/// Dictionary for group by key substitution.
/// </summary>
public ParameterSubstitution GroupByKeySubstitution;

/// <summary>
/// Boolean to indicate a GroupBy expression is the last expression to finished processing.
/// </summary>
public bool LastExpressionIsGroupBy;

/// <summary>
/// If the FROM clause uses a parameter name, it will be substituted for the parameter used in
/// the lambda expressions for the WHERE and SELECT clauses.
Expand Down Expand Up @@ -86,6 +96,7 @@ public TranslationContext(CosmosLinqSerializerOptionsInternal linqSerializerOpti
this.subqueryBindingStack = new Stack<SubqueryBinding>();
this.Parameters = parameters;
this.clientOperation = null;
this.LastExpressionIsGroupBy = false;

if (linqSerializerOptionsInternal?.CustomCosmosLinqSerializer != null)
{
Expand All @@ -104,6 +115,8 @@ public TranslationContext(CosmosLinqSerializerOptionsInternal linqSerializerOpti
this.CosmosLinqSerializer = TranslationContext.DefaultLinqSerializer;
this.MemberNames = TranslationContext.DefaultMemberNames;
}

this.GroupByKeySubstitution = new ParameterSubstitution();
}

public ScalarOperationKind ClientOperation => this.clientOperation ?? ScalarOperationKind.None;
Expand All @@ -120,17 +133,25 @@ public void SetClientOperation(ScalarOperationKind clientOperation)

public Expression LookupSubstitution(ParameterExpression parameter)
{
if (this.CurrentQuery.GroupByParameter != null)
{
Expression groupBySubstitutionExpression = this.GroupByKeySubstitution.Lookup(parameter);
if (groupBySubstitutionExpression != null)
{
return groupBySubstitutionExpression;
}
}
return this.substitutions.Lookup(parameter);
}

public ParameterExpression GenFreshParameter(Type parameterType, string baseParameterName)
public ParameterExpression GenerateFreshParameter(Type parameterType, string baseParameterName, bool includeSuffix = true)
{
return Utilities.NewParameter(baseParameterName, parameterType, this.InScope);
return Utilities.NewParameter(baseParameterName, parameterType, this.InScope, includeSuffix);
}

public Func<string, ParameterExpression> GetGenFreshParameterFunc()
{
return (paramName) => this.GenFreshParameter(typeof(object), paramName);
return (paramName) => this.GenerateFreshParameter(typeof(object), paramName);
}

/// <summary>
Expand Down Expand Up @@ -211,12 +232,12 @@ public void PushCollection(Collection collection)
throw new ArgumentNullException("collection");
}

this.collectionStack.Add(collection);
if (this.CurrentQuery.GroupByParameter == null) this.collectionStack.Add(collection);
}

public void PopCollection()
{
this.collectionStack.RemoveAt(this.collectionStack.Count - 1);
if (this.CurrentQuery.GroupByParameter == null) this.collectionStack.RemoveAt(this.collectionStack.Count - 1);
}

/// <summary>
Expand All @@ -226,7 +247,7 @@ public void PopCollection()
/// <param name="name">Suggested name for the input parameter.</param>
public ParameterExpression SetInputParameter(Type type, string name)
{
return this.CurrentQuery.fromParameters.SetInputParameter(type, name, this.InScope);
return this.CurrentQuery.FromParameters.SetInputParameter(type, name, this.InScope);
}

/// <summary>
Expand All @@ -237,7 +258,7 @@ public ParameterExpression SetInputParameter(Type type, string name)
public void SetFromParameter(ParameterExpression parameter, SqlCollection collection)
{
Binding binding = new Binding(parameter, collection, isInCollection: true);
this.CurrentQuery.fromParameters.Add(binding);
this.CurrentQuery.FromParameters.Add(binding);
}

/// <summary>
Expand Down
7 changes: 4 additions & 3 deletions Microsoft.Azure.Cosmos/src/Linq/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ public static LambdaExpression GetLambda(Expression expr)
/// <param name="prefix">Prefix for the parameter name.</param>
/// <param name="type">Parameter type.</param>
/// <param name="inScope">Names to avoid.</param>
/// <param name="includeSuffix">Enable suffix to parameter name</param>
/// <returns>The new parameter.</returns>
public static ParameterExpression NewParameter(string prefix, Type type, HashSet<ParameterExpression> inScope)
public static ParameterExpression NewParameter(string prefix, Type type, HashSet<ParameterExpression> inScope, bool includeSuffix = true)
{
int suffix = 0;
while (true)
{
string name = prefix + suffix.ToString(CultureInfo.InvariantCulture);
string name = prefix + (includeSuffix ? suffix.ToString(CultureInfo.InvariantCulture) : string.Empty);
ParameterExpression param = Expression.Parameter(type, name);
if (!inScope.Any(p => p.Name.Equals(name)))
if (!inScope.Any(p => p.Name.Equals(name)) || !includeSuffix)
{
inScope.Add(param);
return param;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ public override void Visit(SqlQuery sqlQuery)

if (sqlQuery.GroupByClause != null)
{
this.WriteDelimiter(string.Empty);
sqlQuery.GroupByClause.Accept(this);
this.writer.Write(" ");
}
Expand Down
Loading

0 comments on commit 15d83a7

Please sign in to comment.