Example #1
0
        protected override IEnumerable <Entity> ExecuteInternal(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues)
        {
            var source      = Source.Execute(dataSources, options, parameterTypes, parameterValues);
            var schema      = GetSchema(dataSources, parameterTypes);
            var expressions = Sorts.Select(sort => sort.Expression.Compile(schema, parameterTypes)).ToList();

            if (PresortedCount == 0)
            {
                // We haven't been able to fold any of the sort orders down to the source, so we need to apply
                // them all again here
                IOrderedEnumerable <Entity> sortedSource;

                if (Sorts[0].SortOrder == SortOrder.Descending)
                {
                    sortedSource = source.OrderByDescending(e => expressions[0](e, parameterValues, options));
                }
                else
                {
                    sortedSource = source.OrderBy(e => expressions[0](e, parameterValues, options));
                }

                for (var i = 1; i < Sorts.Count; i++)
                {
                    var expr = expressions[i];

                    if (Sorts[i].SortOrder == SortOrder.Descending)
                    {
                        sortedSource = sortedSource.ThenByDescending(e => expr(e, parameterValues, options));
                    }
                    else
                    {
                        sortedSource = sortedSource.ThenBy(e => expr(e, parameterValues, options));
                    }
                }

                foreach (var entity in sortedSource)
                {
                    yield return(entity);
                }
            }
            else
            {
                // We have managed to fold some but not all of the sorts down to the source. Take records
                // from the source that have equal values of the sorts that have been folded, then sort those
                // subsets individually on the remaining sorts
                var preSortedColumns = Sorts
                                       .Take(PresortedCount)
                                       .Select(s => { schema.ContainsColumn(((ColumnReferenceExpression)s.Expression).GetColumnName(), out var colName); return(colName); })
                                       .ToList();

                var subset   = new List <Entity>();
                var comparer = new DistinctEqualityComparer(preSortedColumns);

                foreach (var next in source)
                {
                    // Get the other values to sort on from this next record
                    var nextSortedValues = expressions
                                           .Take(PresortedCount)
                                           .Select(expr => expr(next, parameterValues, options))
                                           .ToList();

                    // If we've already got a subset to work on, check if this fits in the same subset
                    if (subset.Count > 0 && !comparer.Equals(subset[0], next))
                    {
                        // A value is different, so this record doesn't fit in the same subset. Sort the subset
                        // by the remaining sorts and return the values from it
                        SortSubset(subset, schema, parameterTypes, parameterValues, expressions, options);

                        foreach (var entity in subset)
                        {
                            yield return(entity);
                        }

                        // Now clear out the previous subset so we can move on to the next
                        subset.Clear();
                    }

                    subset.Add(next);
                }

                // Sort and return the final subset
                SortSubset(subset, schema, parameterTypes, parameterValues, expressions, options);

                foreach (var entity in subset)
                {
                    yield return(entity);
                }
            }
        }
Example #2
0
        protected override IEnumerable <Entity> ExecuteInternal(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues)
        {
            var schema      = Source.GetSchema(dataSources, parameterTypes);
            var groupByCols = GetGroupingColumns(schema);

            var isScalarAggregate = IsScalarAggregate;

            InitializeAggregates(schema, parameterTypes);
            Entity currentGroup = null;
            var    comparer     = new DistinctEqualityComparer(groupByCols);
            var    aggregates   = CreateAggregateFunctions(parameterValues, options, false);
            var    states       = isScalarAggregate ? ResetAggregates(aggregates) : null;

            foreach (var entity in Source.Execute(dataSources, options, parameterTypes, parameterValues))
            {
                if (!isScalarAggregate || currentGroup != null)
                {
                    var startNewGroup = currentGroup == null;

                    if (currentGroup != null && !comparer.Equals(currentGroup, entity))
                    {
                        // We've reached the end of the previous group - return that row now
                        var result = new Entity();

                        for (var i = 0; i < groupByCols.Count; i++)
                        {
                            result[groupByCols[i]] = currentGroup[groupByCols[i]];
                        }

                        foreach (var aggregate in GetValues(states))
                        {
                            result[aggregate.Key] = aggregate.Value;
                        }

                        yield return(result);

                        startNewGroup = true;
                    }

                    if (startNewGroup)
                    {
                        currentGroup = entity;
                        states       = ResetAggregates(aggregates);
                    }
                }

                foreach (var func in states.Values)
                {
                    func.AggregateFunction.NextRecord(entity, func.State);
                }
            }

            if (states != null)
            {
                // For scalar aggregates, or for non-scalar aggregates where we've found at least one group, we need to
                // return the values for the final group
                var result = new Entity();

                for (var i = 0; i < groupByCols.Count; i++)
                {
                    result[groupByCols[i]] = currentGroup[groupByCols[i]];
                }

                foreach (var aggregate in GetValues(states))
                {
                    result[aggregate.Key] = aggregate.Value;
                }

                yield return(result);
            }
        }