feat: debounce highlighted attribute validation query (#1626)

Follow up to https://github.com/hyperdxio/hyperdx/pull/1576 after feedback from @MikeShi42.

With these changes, validation queries run either when the "Validate expression" button is clicked, or 1 second after the input value was changed for either the expression or the alias.


https://github.com/user-attachments/assets/8a42e3cd-e9da-40a3-95c2-8b037ab766e4
This commit is contained in:
Karl Power 2026-01-22 15:55:09 +01:00 committed by GitHub
parent cf71a1cb4c
commit 3a2c33d389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 10 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
feat: debounce highlighted attribute validation query

View file

@ -41,6 +41,7 @@ import {
Tooltip,
} from '@mantine/core';
import { DateInput } from '@mantine/dates';
import { useDebouncedCallback, useDidUpdate } from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import {
IconCheck,
@ -201,41 +202,72 @@ function HighlightedAttributeRow({
connectionId,
removeHighlightedAttribute,
}: HighlightedAttributeRowProps) {
const expression = useWatch({
const expressionInput = useWatch({
control,
name: `${name}.${index}.sqlExpression`,
});
const alias = useWatch({
const aliasInput = useWatch({
control,
name: `${name}.${index}.alias`,
});
const [explainParams, setExplainParams] = useState<{
expression: typeof expressionInput;
alias: typeof aliasInput;
}>();
const setExplainParamsDebounced = useDebouncedCallback(
(params: typeof explainParams) => {
setExplainParams(params);
},
1_000,
);
useDidUpdate(() => {
setExplainParamsDebounced({
expression: expressionInput,
alias: aliasInput,
});
}, [expressionInput, aliasInput]);
const {
data: explainData,
error: explainError,
isLoading: explainLoading,
refetch: explainExpression,
} = useExplainQuery(
{
from: { databaseName, tableName },
connection: connectionId,
select: [{ alias, valueExpression: expression }],
select: [
{
alias: explainParams?.alias,
valueExpression: explainParams?.expression ?? '',
},
],
where: '',
},
{ enabled: false },
{
enabled: !!explainParams?.expression,
},
);
const runExpression = () => {
if (expression) {
explainExpression();
}
setExplainParams({
expression: expressionInput,
alias: aliasInput,
});
};
const isExpressionValid = !!explainData?.length;
const isExpressionInvalid = explainError instanceof ClickHouseQueryError;
const shouldShowResult =
explainParams?.expression === expressionInput &&
explainParams?.alias === aliasInput &&
(isExpressionValid || isExpressionInvalid);
return (
<React.Fragment key={id}>
<Grid.Col span={3} pe={0}>
@ -271,7 +303,7 @@ function HighlightedAttributeRow({
variant="subtle"
color="gray"
loading={explainLoading}
disabled={!expression || explainLoading}
disabled={!expressionInput || explainLoading}
onClick={runExpression}
>
<IconCheck size={16} />
@ -288,7 +320,7 @@ function HighlightedAttributeRow({
</Flex>
</Grid.Col>
{(isExpressionValid || isExpressionInvalid) && (
{shouldShowResult && (
<Grid.Col span={5} pe={0} pt={0}>
{isExpressionValid && (
<Text c="green" size="xs">