mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
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:
parent
2c44ef98a8
commit
5a44953e49
5 changed files with 62 additions and 40 deletions
6
.changeset/great-readers-pretend.md
Normal file
6
.changeset/great-readers-pretend.md
Normal 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
|
||||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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' }}>
|
||||
|
|
|
|||
|
|
@ -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 ${{
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ export const AggregateFunctionSchema = z.enum([
|
|||
'quantile',
|
||||
'sum',
|
||||
'any',
|
||||
'none',
|
||||
]);
|
||||
export const AggregateFunctionWithCombinatorsSchema = z
|
||||
.string()
|
||||
|
|
|
|||
Loading…
Reference in a new issue