/// <summary> /// Attempts to merge group columns with select/order columns, returns group columns which were not used in select. /// </summary> private IEnumerable <QueryGroupColumnData> MergeAndGetOrphanGroupColumns() { if (_queryParts.Groups.Count == 1) { foreach (var selectColumn in _queryParts.Select) { var shouldReplaceColumnPath = (selectColumn.AggregationType.HasValue && string.IsNullOrEmpty(selectColumn.ColumnPath)) || (!selectColumn.AggregationType.HasValue && selectColumn.ColumnPath == "Key"); if (shouldReplaceColumnPath) { selectColumn.ColumnPath = _queryParts.Groups.First().ColumnPath; } } } foreach (var groupColumn in _queryParts.Groups) { // in some cases ColumnPath contains wrong ESQ column path // because of re-linq tree visit rules. All cases are described: Func <QuerySelectColumnData, bool> selector = (column) => // case: // .GroupBy(item => new object[] { item.Column<int>("Column1"), item.Column<string>("Column2") }) // .Select(group => new { Column1 = group.Key[0], Column2 = group.Key[1] }) column.ColumnPath == QueryUtils.GetIndexMemberName(groupColumn.Position) // case: // .GroupBy(item => new { Column1 = item.Column<int>("Column1"), Column2 = item.Column<string>("Column2") }) // .Select(group => new { Alias1 = group.Key.Column1, Alias2 = group.Key.Column2 }) || column.ColumnPath == QueryUtils.GetAliasMemberName(groupColumn.Alias) // case: // .GroupBy(item => item.Column<int>("Column1")) // .Select(group => new { Alias1 = group.Key }) || !column.AggregationType.HasValue && column.ColumnPath == QueryUtils.SingleGroupColumnAlias // case (aggregate columns have no column path): // .GroupBy(item => item.Column<int>("Column1")) // .Select(group => new { Alias1 = group.Key, Count = group.Count() }) || column.AggregationType.HasValue && string.IsNullOrEmpty(column.ColumnPath); // get select columns with incorrect column path var selectKeyColumns = _queryParts.Select .Where(selector) .ToArray(); // same for order columns var orderKeyColumns = _queryParts.Orders .Where(selector) .ToArray(); // 1. if column is contained in both Select/OrderBy AND GroupBy clause, then it will be added to ESQ // in Select/OrderBy block and should not be added again in GroupBy block. // 2. if column is only contained in GroupBy clause, it will not be added in Select/OrderBy block // and query will be broken - so we need to add it in GroupBy block, hence yield return. if (selectKeyColumns.Any() || orderKeyColumns.Any()) { selectKeyColumns.ForEach(column => column.ColumnPath = groupColumn.ColumnPath); orderKeyColumns.ForEach(column => column.ColumnPath = groupColumn.ColumnPath); } else { yield return(groupColumn); } } }