Skip to content

Commit

Permalink
Add support for nested non aggregating group by operators (#3087)
Browse files Browse the repository at this point in the history
Fixes #3076
  • Loading branch information
hazzik authored Aug 10, 2022
1 parent 5b3d8e2 commit 267e226
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 16 deletions.
23 changes: 23 additions & 0 deletions src/NHibernate.Test/Async/Linq/ByMethod/GroupByTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,29 @@ public async Task GroupByComputedValueFromNestedObjectSelectAsync()
Assert.AreEqual(2155, orderGroups.Sum(g => g.Count));
}

[Test(Description="GH-3076")]
public async Task NestedNonAggregateGroupByAsync()
{
var list = await (db.OrderLines
.GroupBy(x => new { x.Order.OrderId, x.Product.ProductId }) // this works fine
.GroupBy(x => x.Key.ProductId) // exception: "A recognition error occurred"
.ToListAsync());

Assert.That(list, Has.Count.EqualTo(77));
}

[Test(Description="GH-3076")]
public async Task NestedNonAggregateGroupBySelectAsync()
{
var list = await (db.OrderLines
.GroupBy(x => new { x.Order.OrderId, x.Product.ProductId }) // this works fine
.GroupBy(x => x.Key.ProductId) // exception: "A recognition error occurred"
.Select(x => new { ProductId = x })
.ToListAsync());

Assert.That(list, Has.Count.EqualTo(77));
}

private static void CheckGrouping<TKey, TElement>(IEnumerable<IGrouping<TKey, TElement>> groupedItems, Func<TElement, TKey> groupBy)
{
var used = new HashSet<object>();
Expand Down
23 changes: 23 additions & 0 deletions src/NHibernate.Test/Linq/ByMethod/GroupByTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,29 @@ public void GroupByComputedValueFromNestedObjectSelect()
Assert.AreEqual(2155, orderGroups.Sum(g => g.Count));
}

[Test(Description="GH-3076")]
public void NestedNonAggregateGroupBy()
{
var list = db.OrderLines
.GroupBy(x => new { x.Order.OrderId, x.Product.ProductId }) // this works fine
.GroupBy(x => x.Key.ProductId) // exception: "A recognition error occurred"
.ToList();

Assert.That(list, Has.Count.EqualTo(77));
}

[Test(Description="GH-3076")]
public void NestedNonAggregateGroupBySelect()
{
var list = db.OrderLines
.GroupBy(x => new { x.Order.OrderId, x.Product.ProductId }) // this works fine
.GroupBy(x => x.Key.ProductId) // exception: "A recognition error occurred"
.Select(x => new { ProductId = x })
.ToList();

Assert.That(list, Has.Count.EqualTo(77));
}

private static void CheckGrouping<TKey, TElement>(IEnumerable<IGrouping<TKey, TElement>> groupedItems, Func<TElement, TKey> groupBy)
{
var used = new HashSet<object>();
Expand Down
33 changes: 20 additions & 13 deletions src/NHibernate/Linq/GroupBy/NonAggregatingGroupByRewriter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq.ResultOperators;
using Remotion.Linq;
Expand All @@ -13,22 +14,23 @@ public static class NonAggregatingGroupByRewriter
{
public static void ReWrite(QueryModel queryModel)
{
if (queryModel.ResultOperators.Count == 1
&& queryModel.ResultOperators[0] is GroupResultOperator
if (queryModel.ResultOperators.Count > 0
&& queryModel.ResultOperators.All(r => r is GroupResultOperator)
&& IsNonAggregatingGroupBy(queryModel))
{
var resultOperator = (GroupResultOperator)queryModel.ResultOperators[0];
queryModel.ResultOperators.Clear();
queryModel.ResultOperators.Add(new NonAggregatingGroupBy(resultOperator));
for (var i = 0; i < queryModel.ResultOperators.Count; i++)
{
var resultOperator = (GroupResultOperator) queryModel.ResultOperators[i];
queryModel.ResultOperators[i] = new NonAggregatingGroupBy(resultOperator);
}

return;
}

var subQueryExpression = queryModel.MainFromClause.FromExpression as SubQueryExpression;

if ((subQueryExpression != null)
&& (subQueryExpression.QueryModel.ResultOperators.Count == 1)
&& (subQueryExpression.QueryModel.ResultOperators[0] is GroupResultOperator)
&& (IsNonAggregatingGroupBy(queryModel)))
if (queryModel.MainFromClause.FromExpression is SubQueryExpression subQueryExpression
&& subQueryExpression.QueryModel.ResultOperators.Count > 0
&& subQueryExpression.QueryModel.ResultOperators.All(r => r is GroupResultOperator)
&& IsNonAggregatingGroupBy(queryModel))
{
FlattenSubQuery(subQueryExpression, queryModel);
}
Expand Down Expand Up @@ -58,7 +60,12 @@ private static void FlattenSubQuery(SubQueryExpression subQueryExpression, Query
throw new NotImplementedException();
}

queryModel.ResultOperators.Add(new NonAggregatingGroupBy((GroupResultOperator) subQueryModel.ResultOperators[0]));
for (var i = 0; i < subQueryModel.ResultOperators.Count; i++)
{
var resultOperator = new NonAggregatingGroupBy((GroupResultOperator) subQueryModel.ResultOperators[i]);
queryModel.ResultOperators.Add(resultOperator);
}

queryModel.ResultOperators.Add(clientSideSelect);
}

Expand Down Expand Up @@ -103,4 +110,4 @@ public ClientSideSelect2(LambdaExpression selectClause)
SelectClause = selectClause;
}
}
}
}
8 changes: 6 additions & 2 deletions src/NHibernate/Linq/ResultOperators/NonAggregatingGroupBy.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Remotion.Linq.Clauses.ResultOperators;
using Remotion.Linq.Clauses.StreamedData;

namespace NHibernate.Linq.ResultOperators
{
Expand All @@ -9,6 +10,9 @@ public NonAggregatingGroupBy(GroupResultOperator groupBy)
GroupBy = groupBy;
}

public GroupResultOperator GroupBy { get; private set; }
public GroupResultOperator GroupBy { get; }

public override IStreamedDataInfo GetOutputDataInfo(IStreamedDataInfo inputInfo) =>
GroupBy.GetOutputDataInfo(inputInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
using NHibernate.Linq.ResultOperators;
using NHibernate.Util;
using Remotion.Linq.Clauses.ExpressionVisitors;
using Remotion.Linq.Clauses.StreamedData;

namespace NHibernate.Linq.Visitors.ResultOperatorProcessors
{
public class ProcessNonAggregatingGroupBy : IResultOperatorProcessor<NonAggregatingGroupBy>
{
public void Process(NonAggregatingGroupBy resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree)
{
var selector = queryModelVisitor.Model.SelectClause.Selector;
var selector = ((StreamedSequenceInfo) queryModelVisitor.PreviousEvaluationType).ItemExpression;
var keySelector = resultOperator.GroupBy.KeySelector;
var elementSelector = resultOperator.GroupBy.ElementSelector;

Expand Down

0 comments on commit 267e226

Please sign in to comment.