in pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactory.java [63:499]
public static AggregationFunction getAggregationFunction(FunctionContext function, boolean nullHandlingEnabled) {
try {
String upperCaseFunctionName =
AggregationFunctionType.getNormalizedAggregationFunctionName(function.getFunctionName());
List<ExpressionContext> arguments = function.getArguments();
int numArguments = arguments.size();
ExpressionContext firstArgument = arguments.get(0);
if (upperCaseFunctionName.startsWith("PERCENTILE")) {
String remainingFunctionName = upperCaseFunctionName.substring(10);
if (remainingFunctionName.equals("SMARTTDIGEST")) {
return new PercentileSmartTDigestAggregationFunction(arguments, nullHandlingEnabled);
}
if (remainingFunctionName.equals("KLL")) {
return new PercentileKLLAggregationFunction(arguments, nullHandlingEnabled);
}
if (remainingFunctionName.equals("KLLMV")) {
return new PercentileKLLMVAggregationFunction(arguments);
}
if (remainingFunctionName.equals("RAWKLL")) {
return new PercentileRawKLLAggregationFunction(arguments, nullHandlingEnabled);
}
if (remainingFunctionName.equals("RAWKLLMV")) {
return new PercentileRawKLLMVAggregationFunction(arguments);
}
if (numArguments == 1) {
// Single argument percentile (e.g. Percentile99(foo), PercentileTDigest95(bar), etc.)
// NOTE: This convention is deprecated. DO NOT add new functions here
if (remainingFunctionName.matches("\\d+")) {
// Percentile
return new PercentileAggregationFunction(firstArgument, parsePercentileToInt(remainingFunctionName),
nullHandlingEnabled);
} else if (remainingFunctionName.matches("EST\\d+")) {
// PercentileEst
String percentileString = remainingFunctionName.substring(3);
return new PercentileEstAggregationFunction(firstArgument, parsePercentileToInt(percentileString),
nullHandlingEnabled);
} else if (remainingFunctionName.matches("RAWEST\\d+")) {
// PercentileRawEst
String percentileString = remainingFunctionName.substring(6);
return new PercentileRawEstAggregationFunction(firstArgument, parsePercentileToInt(percentileString),
nullHandlingEnabled);
} else if (remainingFunctionName.matches("TDIGEST\\d+")) {
// PercentileTDigest
String percentileString = remainingFunctionName.substring(7);
return new PercentileTDigestAggregationFunction(firstArgument, parsePercentileToInt(percentileString),
nullHandlingEnabled);
} else if (remainingFunctionName.matches("RAWTDIGEST\\d+")) {
// PercentileRawTDigest
String percentileString = remainingFunctionName.substring(10);
return new PercentileRawTDigestAggregationFunction(firstArgument, parsePercentileToInt(percentileString),
nullHandlingEnabled);
} else if (remainingFunctionName.matches("\\d+MV")) {
// PercentileMV
String percentileString = remainingFunctionName.substring(0, remainingFunctionName.length() - 2);
return new PercentileMVAggregationFunction(firstArgument, parsePercentileToInt(percentileString));
} else if (remainingFunctionName.matches("EST\\d+MV")) {
// PercentileEstMV
String percentileString = remainingFunctionName.substring(3, remainingFunctionName.length() - 2);
return new PercentileEstMVAggregationFunction(firstArgument, parsePercentileToInt(percentileString));
} else if (remainingFunctionName.matches("RAWEST\\d+MV")) {
// PercentileRawEstMV
String percentileString = remainingFunctionName.substring(6, remainingFunctionName.length() - 2);
return new PercentileRawEstMVAggregationFunction(firstArgument, parsePercentileToInt(percentileString));
} else if (remainingFunctionName.matches("TDIGEST\\d+MV")) {
// PercentileTDigestMV
String percentileString = remainingFunctionName.substring(7, remainingFunctionName.length() - 2);
return new PercentileTDigestMVAggregationFunction(firstArgument, parsePercentileToInt(percentileString));
} else if (remainingFunctionName.matches("RAWTDIGEST\\d+MV")) {
// PercentileRawTDigestMV
String percentileString = remainingFunctionName.substring(10, remainingFunctionName.length() - 2);
return new PercentileRawTDigestMVAggregationFunction(firstArgument, parsePercentileToInt(percentileString));
}
} else if (numArguments == 2) {
// Double arguments percentile (e.g. percentile(foo, 99), percentileTDigest(bar, 95), etc.) where the
// second argument is a decimal number from 0.0 to 100.0.
double percentile = arguments.get(1).getLiteral().getDoubleValue();
Preconditions.checkArgument(percentile >= 0 && percentile <= 100, "Invalid percentile: %s", percentile);
if (remainingFunctionName.isEmpty()) {
// Percentile
return new PercentileAggregationFunction(firstArgument, percentile, nullHandlingEnabled);
}
if (remainingFunctionName.equals("EST")) {
// PercentileEst
return new PercentileEstAggregationFunction(firstArgument, percentile, nullHandlingEnabled);
}
if (remainingFunctionName.equals("RAWEST")) {
// PercentileRawEst
return new PercentileRawEstAggregationFunction(firstArgument, percentile, nullHandlingEnabled);
}
if (remainingFunctionName.equals("TDIGEST")) {
// PercentileTDigest
return new PercentileTDigestAggregationFunction(firstArgument, percentile, nullHandlingEnabled);
}
if (remainingFunctionName.equals("RAWTDIGEST")) {
// PercentileRawTDigest
return new PercentileRawTDigestAggregationFunction(firstArgument, percentile, nullHandlingEnabled);
}
if (remainingFunctionName.equals("MV")) {
// PercentileMV
return new PercentileMVAggregationFunction(firstArgument, percentile);
}
if (remainingFunctionName.equals("ESTMV")) {
// PercentileEstMV
return new PercentileEstMVAggregationFunction(firstArgument, percentile);
}
if (remainingFunctionName.equals("RAWESTMV")) {
// PercentileRawEstMV
return new PercentileRawEstMVAggregationFunction(firstArgument, percentile);
}
if (remainingFunctionName.equals("TDIGESTMV")) {
// PercentileTDigestMV
return new PercentileTDigestMVAggregationFunction(firstArgument, percentile);
}
if (remainingFunctionName.equals("RAWTDIGESTMV")) {
// PercentileRawTDigestMV
return new PercentileRawTDigestMVAggregationFunction(firstArgument, percentile);
}
} else if (numArguments == 3) {
// Triple arguments percentile (e.g. percentileTDigest(bar, 95, 1000), etc.) where the
// second argument is a decimal number from 0.0 to 100.0 and third argument is a decimal number indicating
// the compression_factor for the TDigest. This can only be used for TDigest type percentile functions to
// pass in a custom compression_factor. If the two argument version is used the default compression_factor
// of 100.0 is used.
double percentile = arguments.get(1).getLiteral().getDoubleValue();
Preconditions.checkArgument(percentile >= 0 && percentile <= 100, "Invalid percentile: %s", percentile);
int compressionFactor = arguments.get(2).getLiteral().getIntValue();
Preconditions.checkArgument(compressionFactor >= 0, "Invalid compressionFactor: %d", compressionFactor);
if (remainingFunctionName.equals("TDIGEST")) {
// PercentileTDigest
return new PercentileTDigestAggregationFunction(firstArgument, percentile, compressionFactor,
nullHandlingEnabled);
}
if (remainingFunctionName.equals("RAWTDIGEST")) {
// PercentileRawTDigest
return new PercentileRawTDigestAggregationFunction(firstArgument, percentile, compressionFactor,
nullHandlingEnabled);
}
if (remainingFunctionName.equals("TDIGESTMV")) {
// PercentileTDigestMV
return new PercentileTDigestMVAggregationFunction(firstArgument, percentile, compressionFactor);
}
if (remainingFunctionName.equals("RAWTDIGESTMV")) {
// PercentileRawTDigestMV
return new PercentileRawTDigestMVAggregationFunction(firstArgument, percentile, compressionFactor);
}
}
throw new IllegalArgumentException("Invalid percentile function: " + function);
} else {
AggregationFunctionType functionType = AggregationFunctionType.valueOf(upperCaseFunctionName);
switch (functionType) {
case COUNT:
return new CountAggregationFunction(arguments, nullHandlingEnabled);
case MIN:
return new MinAggregationFunction(arguments, nullHandlingEnabled);
case MAX:
return new MaxAggregationFunction(arguments, nullHandlingEnabled);
case SUM:
case SUM0:
return new SumAggregationFunction(arguments, nullHandlingEnabled);
case SUMPRECISION:
return new SumPrecisionAggregationFunction(arguments, nullHandlingEnabled);
case AVG:
return new AvgAggregationFunction(arguments, nullHandlingEnabled);
case MODE:
return new ModeAggregationFunction(arguments, nullHandlingEnabled);
case FIRSTWITHTIME: {
Preconditions.checkArgument(numArguments == 3,
"FIRST_WITH_TIME expects 3 arguments, got: %s. The function can be used as "
+ "firstWithTime(dataColumn, timeColumn, 'dataType')", numArguments);
ExpressionContext timeCol = arguments.get(1);
ExpressionContext dataTypeExp = arguments.get(2);
Preconditions.checkArgument(dataTypeExp.getType() == ExpressionContext.Type.LITERAL,
"FIRST_WITH_TIME expects the 3rd argument to be literal, got: %s. The function can be used as "
+ "firstWithTime(dataColumn, timeColumn, 'dataType')", dataTypeExp.getType());
DataType dataType = DataType.valueOf(dataTypeExp.getLiteral().getStringValue().toUpperCase());
switch (dataType) {
case BOOLEAN:
return new FirstIntValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled,
true);
case INT:
return new FirstIntValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled,
false);
case LONG:
return new FirstLongValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case FLOAT:
return new FirstFloatValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case DOUBLE:
return new FirstDoubleValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case STRING:
return new FirstStringValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
default:
throw new IllegalArgumentException("Unsupported data type for FIRST_WITH_TIME: " + dataType);
}
}
case LISTAGG: {
Preconditions.checkArgument(numArguments == 2 || numArguments == 3,
"LISTAGG expects 2 arguments, got: %s. The function can be used as "
+ "listAgg([distinct] expression, 'separator')", numArguments);
ExpressionContext separatorExpression = arguments.get(1);
Preconditions.checkArgument(separatorExpression.getType() == ExpressionContext.Type.LITERAL,
"LISTAGG expects the 2nd argument to be literal, got: %s. The function can be used as "
+ "listAgg([distinct] expression, 'separator')", separatorExpression.getType());
String separator = separatorExpression.getLiteral().getStringValue();
boolean isDistinct = false;
if (numArguments == 3) {
ExpressionContext isDistinctListAggExp = arguments.get(2);
isDistinct = isDistinctListAggExp.getLiteral().getBooleanValue();
}
if (isDistinct) {
return new ListAggDistinctFunction(arguments.get(0), separator, nullHandlingEnabled);
}
return new ListAggFunction(arguments.get(0), separator, nullHandlingEnabled);
}
case SUMARRAYLONG:
return new SumArrayLongAggregationFunction(arguments);
case SUMARRAYDOUBLE:
return new SumArrayDoubleAggregationFunction(arguments);
case ARRAYAGG: {
Preconditions.checkArgument(numArguments >= 2,
"ARRAY_AGG expects 2 or 3 arguments, got: %s. The function can be used as "
+ "arrayAgg(dataColumn, 'dataType', ['isDistinct'])", numArguments);
ExpressionContext dataTypeExp = arguments.get(1);
Preconditions.checkArgument(dataTypeExp.getType() == ExpressionContext.Type.LITERAL,
"ARRAY_AGG expects the 2nd argument to be literal, got: %s. The function can be used as "
+ "arrayAgg(dataColumn, 'dataType', ['isDistinct'])", dataTypeExp.getType());
DataType dataType = DataType.valueOf(dataTypeExp.getLiteral().getStringValue().toUpperCase());
boolean isDistinct = false;
if (numArguments == 3) {
ExpressionContext isDistinctExp = arguments.get(2);
Preconditions.checkArgument(isDistinctExp.getType() == ExpressionContext.Type.LITERAL,
"ARRAY_AGG expects the 3rd argument to be literal, got: %s. The function can be used as "
+ "arrayAgg(dataColumn, 'dataType', ['isDistinct'])", isDistinctExp.getType());
isDistinct = isDistinctExp.getLiteral().getBooleanValue();
}
if (isDistinct) {
switch (dataType) {
case BOOLEAN:
case INT:
return new ArrayAggDistinctIntFunction(firstArgument, dataType, nullHandlingEnabled);
case LONG:
case TIMESTAMP:
return new ArrayAggDistinctLongFunction(firstArgument, dataType, nullHandlingEnabled);
case FLOAT:
return new ArrayAggDistinctFloatFunction(firstArgument, nullHandlingEnabled);
case DOUBLE:
return new ArrayAggDistinctDoubleFunction(firstArgument, nullHandlingEnabled);
case STRING:
return new ArrayAggDistinctStringFunction(firstArgument, nullHandlingEnabled);
default:
throw new IllegalArgumentException("Unsupported data type for ARRAY_AGG: " + dataType);
}
}
switch (dataType) {
case BOOLEAN:
case INT:
return new ArrayAggIntFunction(firstArgument, dataType, nullHandlingEnabled);
case LONG:
case TIMESTAMP:
return new ArrayAggLongFunction(firstArgument, dataType, nullHandlingEnabled);
case FLOAT:
return new ArrayAggFloatFunction(firstArgument, nullHandlingEnabled);
case DOUBLE:
return new ArrayAggDoubleFunction(firstArgument, nullHandlingEnabled);
case STRING:
return new ArrayAggStringFunction(firstArgument, nullHandlingEnabled);
default:
throw new IllegalArgumentException("Unsupported data type for ARRAY_AGG: " + dataType);
}
}
case LASTWITHTIME: {
Preconditions.checkArgument(numArguments == 3,
"LAST_WITH_TIME expects 3 arguments, got: %s. The function can be used as "
+ "lastWithTime(dataColumn, timeColumn, 'dataType')", numArguments);
ExpressionContext timeCol = arguments.get(1);
ExpressionContext dataTypeExp = arguments.get(2);
Preconditions.checkArgument(dataTypeExp.getType() == ExpressionContext.Type.LITERAL,
"LAST_WITH_TIME expects the 3rd argument to be literal, got: %s. The function can be used as "
+ "lastWithTime(dataColumn, timeColumn, 'dataType')", dataTypeExp.getType());
DataType dataType = DataType.valueOf(dataTypeExp.getLiteral().getStringValue().toUpperCase());
switch (dataType) {
case BOOLEAN:
return new LastIntValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled, true);
case INT:
return new LastIntValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled, false);
case LONG:
return new LastLongValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case FLOAT:
return new LastFloatValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case DOUBLE:
return new LastDoubleValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
case STRING:
return new LastStringValueWithTimeAggregationFunction(firstArgument, timeCol, nullHandlingEnabled);
default:
throw new IllegalArgumentException("Unsupported data type for LAST_WITH_TIME: " + dataType);
}
}
case MINMAXRANGE:
return new MinMaxRangeAggregationFunction(arguments, nullHandlingEnabled);
case DISTINCTCOUNT:
return new DistinctCountAggregationFunction(arguments, nullHandlingEnabled);
case DISTINCTCOUNTOFFHEAP:
return new DistinctCountOffHeapAggregationFunction(arguments, nullHandlingEnabled);
case DISTINCTCOUNTBITMAP:
return new DistinctCountBitmapAggregationFunction(arguments);
case SEGMENTPARTITIONEDDISTINCTCOUNT:
return new SegmentPartitionedDistinctCountAggregationFunction(arguments);
case DISTINCTCOUNTHLL:
return new DistinctCountHLLAggregationFunction(arguments);
case DISTINCTCOUNTRAWHLL:
return new DistinctCountRawHLLAggregationFunction(arguments);
case DISTINCTCOUNTSMARTHLL:
return new DistinctCountSmartHLLAggregationFunction(arguments);
case FASTHLL:
return new FastHLLAggregationFunction(arguments);
case DISTINCTCOUNTTHETASKETCH:
return new DistinctCountThetaSketchAggregationFunction(arguments);
case DISTINCTCOUNTRAWTHETASKETCH:
return new DistinctCountRawThetaSketchAggregationFunction(arguments);
case DISTINCTSUM:
return new DistinctSumAggregationFunction(arguments, nullHandlingEnabled);
case DISTINCTAVG:
return new DistinctAvgAggregationFunction(arguments, nullHandlingEnabled);
case IDSET:
return new IdSetAggregationFunction(arguments);
case COUNTMV:
return new CountMVAggregationFunction(arguments, nullHandlingEnabled);
case MINMV:
return new MinMVAggregationFunction(arguments, nullHandlingEnabled);
case MAXMV:
return new MaxMVAggregationFunction(arguments, nullHandlingEnabled);
case SUMMV:
return new SumMVAggregationFunction(arguments, nullHandlingEnabled);
case AVGMV:
return new AvgMVAggregationFunction(arguments, nullHandlingEnabled);
case MINMAXRANGEMV:
return new MinMaxRangeMVAggregationFunction(arguments, nullHandlingEnabled);
case DISTINCTCOUNTMV:
return new DistinctCountMVAggregationFunction(arguments);
case DISTINCTCOUNTBITMAPMV:
return new DistinctCountBitmapMVAggregationFunction(arguments);
case DISTINCTCOUNTHLLMV:
return new DistinctCountHLLMVAggregationFunction(arguments);
case DISTINCTCOUNTRAWHLLMV:
return new DistinctCountRawHLLMVAggregationFunction(arguments);
case DISTINCTCOUNTHLLPLUS:
return new DistinctCountHLLPlusAggregationFunction(arguments);
case DISTINCTCOUNTRAWHLLPLUS:
return new DistinctCountRawHLLPlusAggregationFunction(arguments);
case DISTINCTCOUNTHLLPLUSMV:
return new DistinctCountHLLPlusMVAggregationFunction(arguments);
case DISTINCTCOUNTRAWHLLPLUSMV:
return new DistinctCountRawHLLPlusMVAggregationFunction(arguments);
case DISTINCTSUMMV:
return new DistinctSumMVAggregationFunction(arguments);
case DISTINCTAVGMV:
return new DistinctAvgMVAggregationFunction(arguments);
case STUNION:
return new StUnionAggregationFunction(arguments);
case HISTOGRAM:
return new HistogramAggregationFunction(arguments);
case COVARPOP:
return new CovarianceAggregationFunction(arguments, false);
case COVARSAMP:
return new CovarianceAggregationFunction(arguments, true);
case BOOLAND:
return new BooleanAndAggregationFunction(arguments, nullHandlingEnabled);
case BOOLOR:
return new BooleanOrAggregationFunction(arguments, nullHandlingEnabled);
case VARPOP:
return new VarianceAggregationFunction(arguments, false, false, nullHandlingEnabled);
case VARSAMP:
return new VarianceAggregationFunction(arguments, true, false, nullHandlingEnabled);
case STDDEVPOP:
return new VarianceAggregationFunction(arguments, false, true, nullHandlingEnabled);
case STDDEVSAMP:
return new VarianceAggregationFunction(arguments, true, true, nullHandlingEnabled);
case SKEWNESS:
return new FourthMomentAggregationFunction(arguments, FourthMomentAggregationFunction.Type.SKEWNESS);
case KURTOSIS:
return new FourthMomentAggregationFunction(arguments, FourthMomentAggregationFunction.Type.KURTOSIS);
case FOURTHMOMENT:
return new FourthMomentAggregationFunction(arguments, FourthMomentAggregationFunction.Type.MOMENT);
case DISTINCTCOUNTTUPLESKETCH:
// mode actually doesn't matter here because we only care about keys, not values
return new DistinctCountIntegerTupleSketchAggregationFunction(arguments, IntegerSummary.Mode.Sum);
case DISTINCTCOUNTRAWINTEGERSUMTUPLESKETCH:
return new IntegerTupleSketchAggregationFunction(arguments, IntegerSummary.Mode.Sum);
case SUMVALUESINTEGERSUMTUPLESKETCH:
return new SumValuesIntegerTupleSketchAggregationFunction(arguments, IntegerSummary.Mode.Sum);
case AVGVALUEINTEGERSUMTUPLESKETCH:
return new AvgValueIntegerTupleSketchAggregationFunction(arguments, IntegerSummary.Mode.Sum);
case PINOTPARENTAGGEXPRMAX:
return new ParentExprMinMaxAggregationFunction(arguments, true);
case PINOTPARENTAGGEXPRMIN:
return new ParentExprMinMaxAggregationFunction(arguments, false);
case PINOTCHILDAGGEXPRMAX:
return new ChildExprMinMaxAggregationFunction(arguments, true);
case PINOTCHILDAGGEXPRMIN:
return new ChildExprMinMaxAggregationFunction(arguments, false);
case EXPRMAX:
case EXPRMIN:
throw new IllegalArgumentException(
"Aggregation function: " + functionType + " is only supported in selection without alias.");
case FUNNELCOUNT:
return new FunnelCountAggregationFunctionFactory(arguments).get();
case FUNNELMAXSTEP:
return new FunnelMaxStepAggregationFunction(arguments);
case FUNNELMATCHSTEP:
return new FunnelMatchStepAggregationFunction(arguments);
case FUNNELCOMPLETECOUNT:
return new FunnelCompleteCountAggregationFunction(arguments);
case FUNNELSTEPDURATIONSTATS:
return new FunnelStepDurationStatsAggregationFunction(arguments);
case FUNNELEVENTSFUNCTIONEVAL:
return new FunnelEventsFunctionEvalAggregationFunction(arguments);
case FREQUENTSTRINGSSKETCH:
return new FrequentStringsSketchAggregationFunction(arguments);
case FREQUENTLONGSSKETCH:
return new FrequentLongsSketchAggregationFunction(arguments);
case DISTINCTCOUNTCPCSKETCH:
return new DistinctCountCPCSketchAggregationFunction(arguments);
case DISTINCTCOUNTRAWCPCSKETCH:
return new DistinctCountRawCPCSketchAggregationFunction(arguments);
case DISTINCTCOUNTULL:
return new DistinctCountULLAggregationFunction(arguments);
case DISTINCTCOUNTRAWULL:
return new DistinctCountRawULLAggregationFunction(arguments);
case TIMESERIESAGGREGATE:
return new TimeSeriesAggregationFunction(arguments);
default:
throw new IllegalArgumentException("Unsupported aggregation function type: " + functionType);
}
}
} catch (Exception e) {
throw new BadQueryRequestException("Invalid aggregation function: " + function + "; Reason: " + e.getMessage());
}
}