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 21 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
143 changes: 125 additions & 18 deletions Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs

Large diffs are not rendered by default.

115 changes: 93 additions & 22 deletions Microsoft.Azure.Cosmos/src/Linq/QueryUnderConstruction.cs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Microsoft.Azure.Cosmos/src/Linq/TranslationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ public Expression LookupSubstitution(ParameterExpression parameter)
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
5 changes: 3 additions & 2 deletions Microsoft.Azure.Cosmos/src/Linq/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ 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)))
{
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<Results>
<Result>
<Input>
<Description><![CDATA[GroupBy Single Value With Min]]></Description>
<Expression><![CDATA[query.GroupBy(k => k.FamilyId, (key, values) => values.Min())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE MIN(root["FamilyId"])
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
FROM root GROUP BY root["FamilyId"] ]]></SqlQuery>
<ErrorMessage><![CDATA[Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Microsoft.Azure.Cosmos.Services.Management.Tests.Family' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.Path '[0]', line 1, position 2.]]></ErrorMessage>
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[GroupBy Single Value With Max]]></Description>
<Expression><![CDATA[query.GroupBy(k => k.FamilyId, (key, values) => values.Max())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE MAX(root["FamilyId"])
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
FROM root GROUP BY root["FamilyId"] ]]></SqlQuery>
<ErrorMessage><![CDATA[Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Microsoft.Azure.Cosmos.Services.Management.Tests.Family' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.Path '[0]', line 1, position 2.]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[GroupBy Single Value With Min]]></Description>
<Expression><![CDATA[query.GroupBy(k => k.FamilyId, (key, values) => values.Min(value => value.Int))]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE MIN(root["Int"])
FROM root GROUP BY root["FamilyId"] ]]></SqlQuery>
<ErrorMessage><![CDATA[Unexpected character encountered while parsing value: [. Path '', line 1, position 2.]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[GroupBy Single Value With Max]]></Description>
<Expression><![CDATA[query.GroupBy(k => k.FamilyId, (key, values) => values.Max(value => value.Int))]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE MAX(root["Int"])
FROM root GROUP BY root["FamilyId"] ]]></SqlQuery>
<ErrorMessage><![CDATA[Unexpected character encountered while parsing value: [. Path '', line 1, position 2.]]></ErrorMessage>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[GroupBy Single Value With Count]]></Description>
<Expression><![CDATA[query.GroupBy(k => k.FamilyId, (key, values) => values.Count())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE COUNT(1)
FROM root GROUP BY root["FamilyId"] ]]></SqlQuery>
<ErrorMessage><![CDATA[Unexpected character encountered while parsing value: [. Path '', line 1, position 2.]]></ErrorMessage>
</Output>
</Result>
</Results>
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,27 @@ orderby f.FamilyId descending
this.ExecuteTestSuite(inputs);
}

[TestMethod]
public void TestGroupByTranslation()
{
List<LinqTestInput> inputs = new List<LinqTestInput>();
//inputs.Add(new LinqTestInput("GroupBy select single key", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/)));

inputs.Add(new LinqTestInput("GroupBy Single Value With Min", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/,
(key, values) => values.Min() /*return the Min of each group */)));
inputs.Add(new LinqTestInput("GroupBy Single Value With Max", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/,
(key, values) => values.Max() /*return the Max of each group */)));

inputs.Add(new LinqTestInput("GroupBy Single Value With Min", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/,
(key, values) => values.Min(value => value.Int) /*return the Min of each group */)));
inputs.Add(new LinqTestInput("GroupBy Single Value With Max", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/,
(key, values) => values.Max(value => value.Int) /*return the Max of each group */)));
inputs.Add(new LinqTestInput("GroupBy Single Value With Count", b => getQuery(b).GroupBy(k => k.FamilyId /*keySelector*/,
(key, values) => values.Count() /*return the Count of each group */)));

this.ExecuteTestSuite(inputs);
}

[TestMethod]
public void TestLambdaReuse()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Query><![CDATA[SELECT * GROUP BY 1]]></Query>
</Input>
<Output>
<ParsedQuery><![CDATA[SELECT *GROUP BY 1 ]]></ParsedQuery>
<ParsedQuery><![CDATA[SELECT * GROUP BY 1 ]]></ParsedQuery>
</Output>
</Result>
<Result>
Expand All @@ -14,7 +14,7 @@
<Query><![CDATA[SELECT * GrOuP By 1]]></Query>
</Input>
<Output>
<ParsedQuery><![CDATA[SELECT *GROUP BY 1 ]]></ParsedQuery>
<ParsedQuery><![CDATA[SELECT * GROUP BY 1 ]]></ParsedQuery>
</Output>
</Result>
<Result>
Expand All @@ -23,7 +23,7 @@
<Query><![CDATA[SELECT * GROUP BY 1, 2, 3]]></Query>
</Input>
<Output>
<ParsedQuery><![CDATA[SELECT *GROUP BY 1, 2, 3 ]]></ParsedQuery>
<ParsedQuery><![CDATA[SELECT * GROUP BY 1, 2, 3 ]]></ParsedQuery>
</Output>
</Result>
<Result>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,16 +989,17 @@ OFFSET 0 LIMIT 0
}]]></SqlObject>
</Input>
<Output>
<TextOutput><![CDATA[SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0]]></TextOutput>
<TextOutput><![CDATA[SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42) GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0]]></TextOutput>
<PrettyPrint><![CDATA[
SELECT *
FROM inputPathCollection["somePath"] AS some alias
WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42]
WHERE ("this path" < 42)
GROUP BY "some"["random"]["path"][42]
ORDER BY "some"["random"]["path"][42] ASC
OFFSET 0 LIMIT 0
]]></PrettyPrint>
<HashCode>-245344741</HashCode>
<ObfusctedQuery><![CDATA[SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42)GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0]]></ObfusctedQuery>
<ObfusctedQuery><![CDATA[SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42) GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0]]></ObfusctedQuery>
</Output>
</Result>
<Result>
Expand Down Expand Up @@ -1127,18 +1128,19 @@ OFFSET 0 LIMIT 0
}]]></SqlObject>
</Input>
<Output>
<TextOutput><![CDATA[(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<TextOutput><![CDATA[(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42) GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<PrettyPrint><![CDATA[
(
SELECT *
FROM inputPathCollection["somePath"] AS some alias
WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42]
WHERE ("this path" < 42)
GROUP BY "some"["random"]["path"][42]
ORDER BY "some"["random"]["path"][42] ASC
OFFSET 0 LIMIT 0
)
]]></PrettyPrint>
<HashCode>51808704</HashCode>
<ObfusctedQuery><![CDATA[(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42)GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
<ObfusctedQuery><![CDATA[(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42) GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
</Output>
</Result>
<Result>
Expand Down Expand Up @@ -1267,18 +1269,19 @@ OFFSET 0 LIMIT 0
}]]></SqlObject>
</Input>
<Output>
<TextOutput><![CDATA[ARRAY(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<TextOutput><![CDATA[ARRAY(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42) GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<PrettyPrint><![CDATA[
ARRAY(
SELECT *
FROM inputPathCollection["somePath"] AS some alias
WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42]
WHERE ("this path" < 42)
GROUP BY "some"["random"]["path"][42]
ORDER BY "some"["random"]["path"][42] ASC
OFFSET 0 LIMIT 0
)
]]></PrettyPrint>
<HashCode>-1922520573</HashCode>
<ObfusctedQuery><![CDATA[ARRAY(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42)GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
<ObfusctedQuery><![CDATA[ARRAY(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42) GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
</Output>
</Result>
<Result>
Expand Down Expand Up @@ -1407,18 +1410,19 @@ ARRAY(
}]]></SqlObject>
</Input>
<Output>
<TextOutput><![CDATA[EXISTS(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<TextOutput><![CDATA[EXISTS(SELECT * FROM inputPathCollection["somePath"] AS some alias WHERE ("this path" < 42) GROUP BY "some"["random"]["path"][42] ORDER BY "some"["random"]["path"][42] ASC OFFSET 0 LIMIT 0)]]></TextOutput>
<PrettyPrint><![CDATA[
EXISTS(
SELECT *
FROM inputPathCollection["somePath"] AS some alias
WHERE ("this path" < 42)GROUP BY "some"["random"]["path"][42]
WHERE ("this path" < 42)
GROUP BY "some"["random"]["path"][42]
ORDER BY "some"["random"]["path"][42] ASC
OFFSET 0 LIMIT 0
)
]]></PrettyPrint>
<HashCode>1317938775</HashCode>
<ObfusctedQuery><![CDATA[EXISTS(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42)GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
<ObfusctedQuery><![CDATA[EXISTS(SELECT * FROM ident1__19["str1"] AS ident2__10 WHERE ("str2" < 42) GROUP BY "str3"["str4"]["str5"][42] ORDER BY "str3"["str4"]["str5"][42] ASC OFFSET 0 LIMIT 0)]]></ObfusctedQuery>
</Output>
</Result>
</Results>
Loading