in Arriba/Arriba/Model/Query/AggregationQuery.cs [163:281]
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;
}