public AggregationResult Compute(Partition p) { if (p == null) { throw new ArgumentNullException("p"); } Stopwatch w = Stopwatch.StartNew(); AggregationResult result = new AggregationResult(this); result.AggregationContext = this.Aggregator.CreateContext(); // Get any columns passed to the aggregation function IUntypedColumn[] columns = null; if (this.AggregationColumns != null) { columns = new IUntypedColumn[this.AggregationColumns.Length]; for (int i = 0; i < this.AggregationColumns.Length; ++i) { string columnName = this.AggregationColumns[i]; if (!p.Columns.TryGetValue(columnName, out columns[i])) { result.Details.AddError(ExecutionDetails.ColumnDoesNotExist, columnName); return(result); } } } // Find the number of dimensions and number of "cells" for which we'll aggregate List <string> resultBlockColumns = new List <string>(); int rowCount = 1; for (int i = 0; i < this.Dimensions.Count; ++i) { AggregationDimension dimension = this.Dimensions[i]; if (!String.IsNullOrEmpty(dimension.Name)) { resultBlockColumns.Add(dimension.Name); } else { resultBlockColumns.Add(StringExtensions.Format("Dimension {0}", i + 1)); } rowCount *= (dimension.GroupByWhere.Count + 1); } resultBlockColumns.Add("Aggregate"); // Create the DataBlock to hold the final results result.Values = new DataBlock(resultBlockColumns, rowCount); // Find the set of items in the base query ShortSet baseWhereSet = new ShortSet(p.Count); this.Where.TryEvaluate(p, baseWhereSet, result.Details); result.Total = baseWhereSet.Count(); // If this is only one dimension, use only one ShortSet and aggregate as we go if (this.Dimensions.Count == 1) { AggregationDimension dimension = this.Dimensions[0]; ShortSet setForDimension = new ShortSet(p.Count); int nextBlockRow = 0; foreach (IExpression dimensionValue in dimension.GroupByWhere) { // Get the set for this value intersected with the base set setForDimension.Clear(); dimensionValue.TryEvaluate(p, setForDimension, result.Details); setForDimension.And(baseWhereSet); // Compute and store the aggregate value if (!setForDimension.IsEmpty()) { result.Values[nextBlockRow, 1] = this.Aggregator.Aggregate(result.AggregationContext, setForDimension, columns); } nextBlockRow++; } // Add the total result.Values[nextBlockRow, 1] = this.Aggregator.Aggregate(result.AggregationContext, baseWhereSet, columns); } else { // Compute the set of items actually matching each dimension-value List <List <Tuple <IExpression, ShortSet> > > allDimensionValueSets = new List <List <Tuple <IExpression, ShortSet> > >(); foreach (AggregationDimension dimension in this.Dimensions) { List <Tuple <IExpression, ShortSet> > dimensionSet = new List <Tuple <IExpression, ShortSet> >(); // Add one item for each value in this dimension foreach (IExpression dimensionValue in dimension.GroupByWhere) { ShortSet setForDimensionValue = new ShortSet(p.Count); dimensionValue.TryEvaluate(p, setForDimensionValue, result.Details); dimensionSet.Add(new Tuple <IExpression, ShortSet>(dimensionValue, setForDimensionValue)); } // Add one 'Total row' item dimensionSet.Add(new Tuple <IExpression, ShortSet>(new AllExpression(), baseWhereSet)); allDimensionValueSets.Add(dimensionSet); } // Run the aggregator over the items AggregateAllDimensionsFlat(result.AggregationContext, result.Values, p.Count, baseWhereSet, allDimensionValueSets, columns, this.Aggregator); } // Add the dimension names to the result if this is the only partition; otherwise, merge will add it if (p.Mask.Equals(PartitionMask.All)) { AddDimensionsToBlock(result.Values); } // Capture timing and return result.Runtime = w.Elapsed; return(result); }
private static void AggregateAllDimensionsFlat(object aggregationContext, DataBlock block, ushort itemCount, ShortSet baseWhereSet, List <List <Tuple <IExpression, ShortSet> > > allDimensionValueSets, IUntypedColumn[] columns, IAggregator aggregator) { int nextBlockRow = 0; int dimensionCount = allDimensionValueSets.Count; int currentDimension = 0; int[] nextIndexPerDimension = new int[dimensionCount]; ShortSet[] setsPerDimension = new ShortSet[dimensionCount + 1]; setsPerDimension[0] = baseWhereSet; for (int i = 1; i < setsPerDimension.Length; ++i) { setsPerDimension[i] = new ShortSet(itemCount); } object[] aggregationResults = new object[block.RowCount]; while (currentDimension >= 0) { if (currentDimension == dimensionCount) { // Leaf: Compute Aggregate, then ask for next combination aggregationResults[nextBlockRow] = aggregator.Aggregate(aggregationContext, setsPerDimension[currentDimension], columns); nextBlockRow++; currentDimension--; } else if (nextIndexPerDimension[currentDimension] < allDimensionValueSets[currentDimension].Count) { // Non-leaf, more values: Get next value and combine Tuple <IExpression, ShortSet> dimensionValue = allDimensionValueSets[currentDimension][nextIndexPerDimension[currentDimension]]; ShortSet current = setsPerDimension[currentDimension + 1]; current.FromAnd(setsPerDimension[currentDimension], dimensionValue.Item2); nextIndexPerDimension[currentDimension]++; currentDimension++; // Skip rest of range if set already empty [slower; IsEmpty too expensive] if (current.IsEmpty()) { int rowsToSkip = 1; for (int i = currentDimension; i < dimensionCount; ++i) { rowsToSkip *= allDimensionValueSets[i].Count; } nextBlockRow += rowsToSkip; currentDimension--; } } else { // Non-leaf, no more values: Pop up to previous dimension for next value there nextIndexPerDimension[currentDimension] = 0; currentDimension--; } } block.SetColumn(block.ColumnCount - 1, aggregationResults); }
public virtual object Aggregate(object context, ShortSet matches, IUntypedColumn[] columns) { if (columns == null || columns.Length < 1) { throw new ArgumentException(StringExtensions.Format("At least one column must be passed to {0}.", this.GetType().Name)); } if (matches == null) { throw new ArgumentNullException("matches"); } if (matches.IsEmpty()) { return(DefaultValue); } // Enumerate set once and get values once, avoiding any per-item method calls ushort[] items = matches.Values; Array values = columns[0].GetValues(items); Type itemType = columns[0].ColumnType; // Cast to a specific type at the array level to avoid per item casting if (itemType.Equals(typeof(long))) { return(AggregateLong((long[])values)); } else if (itemType.Equals(typeof(int))) { return(AggregateInt((int[])values)); } else if (itemType.Equals(typeof(short))) { return(AggregateShort((short[])values)); } else if (itemType.Equals(typeof(byte))) { return(AggregateByte((byte[])values)); } else if (itemType.Equals(typeof(ulong))) { return(AggregateULong((ulong[])values)); } else if (itemType.Equals(typeof(uint))) { return(AggregateUInt((uint[])values)); } else if (itemType.Equals(typeof(ushort))) { return(AggregateUShort((ushort[])values)); } else if (itemType.Equals(typeof(double))) { return(AggregateDouble((double[])values)); } else if (itemType.Equals(typeof(float))) { return(AggregateFloat((float[])values)); } else if (itemType.Equals(typeof(DateTime))) { return(AggregateDateTime((DateTime[])values)); } else if (itemType.Equals(typeof(TimeSpan))) { return(AggregateTimeSpan((TimeSpan[])values)); } else if (itemType.Equals(typeof(Guid))) { return(AggregateGuid((Guid[])values)); } else if (itemType.Equals(typeof(ByteBlock))) { return(AggregateByteBlock((ByteBlock[])values)); } else { throw new NotImplementedException(StringExtensions.Format("{0} is unable to aggregate type {1}.", this.GetType().Name, values.GetValue(0).GetType().Name)); } }