Skip to content

Commit 96aed85

Browse files
committed
NH-3797 - Use group by keys as first class HqlCandidates for select clause rewriting
We make the assumption that if the group by key is being sent to the DB for grouping then we can send the same expression to the DB for selecting.
1 parent b66245e commit 96aed85

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

src/NHibernate/Linq/GroupBy/AggregatingGroupByRewriter.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Linq.Expressions;
45
using NHibernate.Linq.Clauses;
56
using NHibernate.Linq.ReWriters;
67
using NHibernate.Linq.Visitors;
@@ -42,9 +43,10 @@ public static class AggregatingGroupByRewriter
4243
typeof (CacheableResultOperator)
4344
};
4445

45-
public static void ReWrite(QueryModel queryModel)
46+
public static IEnumerable<Expression> ReWrite(QueryModel queryModel)
4647
{
47-
var subQueryExpression = queryModel.MainFromClause.FromExpression as SubQueryExpression;
48+
IEnumerable<Expression> groupByKeys = Enumerable.Empty<Expression>();
49+
var subQueryExpression = queryModel.MainFromClause.FromExpression as SubQueryExpression;
4850

4951
if (subQueryExpression != null)
5052
{
@@ -57,10 +59,17 @@ public static void ReWrite(QueryModel queryModel)
5759
var groupBy = operators[0] as GroupResultOperator;
5860
if (groupBy != null)
5961
{
60-
FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy);
62+
if (groupBy.KeySelector is NewExpression)
63+
groupByKeys = (groupBy.KeySelector as NewExpression).Arguments;
64+
else if (groupBy.KeySelector is NewArrayExpression)
65+
groupByKeys = (groupBy.KeySelector as NewArrayExpression).Expressions;
66+
else
67+
groupByKeys = new[] { groupBy.KeySelector };
68+
FlattenSubQuery(queryModel, subQueryExpression.QueryModel, groupBy);
6169
}
6270
}
6371
}
72+
return groupByKeys;
6473
}
6574

6675
private static void FlattenSubQuery(QueryModel queryModel, QueryModel subQueryModel, GroupResultOperator groupBy)

src/NHibernate/Linq/Visitors/QueryModelVisitor.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
24
using System.Linq.Expressions;
35
using NHibernate.Hql.Ast;
46
using NHibernate.Linq.Clauses;
@@ -32,7 +34,7 @@ public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel quer
3234
NonAggregatingGroupByRewriter.ReWrite(queryModel);
3335

3436
// Rewrite aggregate group-by statements
35-
AggregatingGroupByRewriter.ReWrite(queryModel);
37+
var groupByKeys = AggregatingGroupByRewriter.ReWrite(queryModel);
3638

3739
// Rewrite aggregating group-joins
3840
AggregatingGroupJoinRewriter.ReWrite(queryModel);
@@ -74,7 +76,11 @@ public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel quer
7476
// Identify and name query sources
7577
QuerySourceIdentifier.Visit(parameters.QuerySourceNamer, queryModel);
7678

77-
var visitor = new QueryModelVisitor(parameters, root, queryModel) { RewrittenOperatorResult = result };
79+
var visitor = new QueryModelVisitor(parameters, root, queryModel)
80+
{
81+
RewrittenOperatorResult = result,
82+
GroupByKeys = groupByKeys,
83+
};
7884
visitor.Visit();
7985

8086
return visitor._hqlTree.GetTranslation();
@@ -94,6 +100,8 @@ public static ExpressionToHqlTranslationResults GenerateHqlQuery(QueryModel quer
94100

95101
public ResultOperatorRewriterResult RewrittenOperatorResult { get; private set; }
96102

103+
public IEnumerable<Expression> GroupByKeys { get; private set; }
104+
97105
static QueryModelVisitor()
98106
{
99107
// TODO - reflection to build map
@@ -230,7 +238,7 @@ public override void VisitSelectClause(SelectClause selectClause, QueryModel que
230238
{
231239
CurrentEvaluationType = selectClause.GetOutputDataInfo();
232240

233-
var visitor = new SelectClauseVisitor(typeof(object[]), VisitorParameters);
241+
var visitor = new SelectClauseVisitor(typeof(object[]), VisitorParameters, GroupByKeys);
234242

235243
visitor.Visit(selectClause.Selector);
236244

src/NHibernate/Linq/Visitors/SelectClauseVisitor.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ public class SelectClauseVisitor : ExpressionTreeVisitor
1818
private List<HqlExpression> _hqlTreeNodes = new List<HqlExpression>();
1919
private readonly HqlGeneratorExpressionTreeVisitor _hqlVisitor;
2020

21-
public SelectClauseVisitor(System.Type inputType, VisitorParameters parameters)
21+
public SelectClauseVisitor(System.Type inputType, VisitorParameters parameters, IEnumerable<Expression> groupByKeys)
2222
{
2323
_inputParameter = Expression.Parameter(inputType, "input");
2424
_parameters = parameters;
2525
_hqlVisitor = new HqlGeneratorExpressionTreeVisitor(_parameters);
26+
_hqlNodes = new HashSet<Expression>(groupByKeys);
2627
}
2728

2829
public LambdaExpression ProjectionExpression { get; private set; }
@@ -43,7 +44,7 @@ public void Visit(Expression expression)
4344
// Find the sub trees that can be expressed purely in HQL
4445
var nominator = new SelectClauseHqlNominator(_parameters);
4546
nominator.Visit(expression);
46-
_hqlNodes = nominator.HqlCandidates;
47+
_hqlNodes.UnionWith(nominator.HqlCandidates);
4748

4849
// Linq2SQL ignores calls to local methods. Linq2EF seems to not support
4950
// calls to local methods at all. For NHibernate we support local methods,

0 commit comments

Comments
 (0)