feat: Add new none aggregation function to allow fully user defined aggregations in SQL (#1174)

<img width="1956" height="851" alt="image" src="https://github.com/user-attachments/assets/3ca89db9-484b-4e74-88a5-4c31b6a96aef" />
This commit is contained in:
Mike Shi 2025-09-19 14:17:40 -07:00 committed by GitHub
parent 2c44ef98a8
commit 5a44953e49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 62 additions and 40 deletions

View file

@ -0,0 +1,6 @@
---
"@hyperdx/common-utils": minor
"@hyperdx/app": minor
---
feat: Add new none aggregation function to allow fully user defined aggregations in SQL

View file

@ -55,6 +55,7 @@ export const AGG_FNS = [
{ value: 'min' as const, label: 'Minimum' },
{ value: 'count_distinct' as const, label: 'Count Distinct' },
{ value: 'any' as const, label: 'Any' },
{ value: 'none' as const, label: 'None' },
];
export const getMetricAggFns = (

View file

@ -275,7 +275,12 @@ function ChartSeriesEditorComponent({
</div>
)}
{tableSource?.kind !== SourceKind.Metric && aggFn !== 'count' && (
<div style={{ minWidth: 220 }}>
<div
style={{
minWidth: 220,
...(aggFn === 'none' && { width: '100%' }),
}}
>
<SQLInlineEditorControlled
tableConnections={{
databaseName,
@ -289,44 +294,46 @@ function ChartSeriesEditorComponent({
/>
</div>
)}
<Flex align={'center'} gap={'xs'} className="flex-grow-1">
<Text size="sm">Where</Text>
{aggConditionLanguage === 'sql' ? (
<SQLInlineEditorControlled
tableConnections={{
databaseName,
tableName: tableName ?? '',
connectionId: connectionId ?? '',
}}
control={control}
name={`${namePrefix}aggCondition`}
placeholder="SQL WHERE clause (ex. column = 'foo')"
onLanguageChange={lang =>
setValue(`${namePrefix}aggConditionLanguage`, lang)
}
additionalSuggestions={attributeKeys}
language="sql"
onSubmit={onSubmit}
/>
) : (
<SearchInputV2
tableConnections={{
connectionId: connectionId ?? '',
databaseName: databaseName ?? '',
tableName: tableName ?? '',
}}
control={control}
name={`${namePrefix}aggCondition`}
onLanguageChange={lang =>
setValue(`${namePrefix}aggConditionLanguage`, lang)
}
language="lucene"
placeholder="Search your events w/ Lucene ex. column:foo"
onSubmit={onSubmit}
additionalSuggestions={attributeKeys}
/>
)}
</Flex>
{aggFn !== 'none' && (
<Flex align={'center'} gap={'xs'} className="flex-grow-1">
<Text size="sm">Where</Text>
{aggConditionLanguage === 'sql' ? (
<SQLInlineEditorControlled
tableConnections={{
databaseName,
tableName: tableName ?? '',
connectionId: connectionId ?? '',
}}
control={control}
name={`${namePrefix}aggCondition`}
placeholder="SQL WHERE clause (ex. column = 'foo')"
onLanguageChange={lang =>
setValue(`${namePrefix}aggConditionLanguage`, lang)
}
additionalSuggestions={attributeKeys}
language="sql"
onSubmit={onSubmit}
/>
) : (
<SearchInputV2
tableConnections={{
connectionId: connectionId ?? '',
databaseName: databaseName ?? '',
tableName: tableName ?? '',
}}
control={control}
name={`${namePrefix}aggCondition`}
onLanguageChange={lang =>
setValue(`${namePrefix}aggConditionLanguage`, lang)
}
language="lucene"
placeholder="Search your events w/ Lucene ex. column:foo"
onSubmit={onSubmit}
additionalSuggestions={attributeKeys}
/>
)}
</Flex>
)}
{showGroupBy && (
<Flex align={'center'} gap={'xs'}>
<Text size="sm" style={{ whiteSpace: 'nowrap' }}>

View file

@ -272,11 +272,13 @@ const aggFnExpr = ({
where?: string;
}) => {
const isAny = fn === 'any';
const isNone = fn === 'none';
const isCount = fn.startsWith('count');
const isWhereUsed = isNonEmptyWhereExpr(where);
// Cast to float64 because the expr might not be a number
const unsafeExpr = {
UNSAFE_RAW_SQL: isAny ? `${expr}` : `toFloat64OrDefault(toString(${expr}))`,
UNSAFE_RAW_SQL:
isAny || isNone ? `${expr}` : `toFloat64OrDefault(toString(${expr}))`,
};
const whereWithExtraNullCheck = `${where} AND ${unsafeExpr.UNSAFE_RAW_SQL} IS NOT NULL`;
@ -307,6 +309,11 @@ const aggFnExpr = ({
};
}
if (fn === 'none') {
// Can not use WHERE in none as we can not apply if to a custom aggregation function
return chSql`${{ UNSAFE_RAW_SQL: expr ?? '' }}`;
}
if (expr != null) {
if (fn === 'count_distinct') {
return chSql`count${isWhereUsed ? 'If' : ''}(DISTINCT ${{

View file

@ -62,6 +62,7 @@ export const AggregateFunctionSchema = z.enum([
'quantile',
'sum',
'any',
'none',
]);
export const AggregateFunctionWithCombinatorsSchema = z
.string()