feat: added support for multiaggregation in columnUnit and thresholds

This commit is contained in:
SagarRajput-7 2025-07-04 02:09:35 +05:30
parent b3488e73a8
commit 8a1407c9f4
6 changed files with 129 additions and 54 deletions

View File

@ -1,6 +1,8 @@
import { createAggregation } from 'api/v5/queryRange/prepareQueryRangePayloadV5';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
Having,
IBuilderQuery,
Query,
TagFilter,
} from 'types/api/queryBuilder/queryBuilderData';
@ -220,3 +222,49 @@ export const getQueryTitles = (currentQuery: Query): string[] => {
return currentQuery.promql.map((q) => q.name);
};
// function to give you label value for query name taking multiaggregation into account
export function getQueryLabelWithAggregation(
queryData: IBuilderQuery[],
legendMap: Record<string, string> = {},
): { label: string; value: string }[] {
const labels: { label: string; value: string }[] = [];
const aggregationPerQuery =
queryData.reduce((acc, query) => {
if (query.queryName && query.aggregations?.length) {
acc[query.queryName] = createAggregation(query).map((a: any) => ({
alias: a.alias,
expression: a.expression,
}));
}
return acc;
}, {} as Record<string, any>) || {};
Object.entries(aggregationPerQuery).forEach(([queryName, aggregations]) => {
const legend = legendMap[queryName];
if (aggregations.length > 1) {
aggregations.forEach((agg: any, index: number) => {
const aggregationName = agg.alias || agg.expression || '';
const label = `${queryName}.${index}`;
const value = legend
? `${aggregationName}-${legend}`
: `${queryName}.${aggregationName}`;
labels.push({
label,
value,
});
});
} else if (aggregations.length === 1) {
const label = legend || queryName;
const value = legend || queryName;
labels.push({
label,
value,
});
}
});
return labels;
}

View File

@ -84,11 +84,12 @@ function GridTableComponent({
const newValue = { ...val };
Object.keys(val).forEach((k) => {
if (columnUnits[k]) {
// the check below takes care of not adding units for rows that have n/a values
newValue[k] =
val[k] !== 'n/a'
? getYAxisFormattedValue(String(val[k]), columnUnits[k])
: val[k];
// the check below takes care of not adding units for rows that have n/a or null values
if (val[k] !== 'n/a' && val[k] !== null) {
newValue[k] = getYAxisFormattedValue(String(val[k]), columnUnits[k]);
} else if (val[k] === null) {
newValue[k] = 'n/a';
}
newValue[`${k}_without_unit`] = val[k];
}
});

View File

@ -17,7 +17,7 @@
align-items: center;
.heading {
width: 20px;
width: 32px;
}
}
}

View File

@ -2,9 +2,9 @@ import './ColumnUnitSelector.styles.scss';
import { Typography } from 'antd';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { Dispatch, SetStateAction } from 'react';
import { useGetQueryLabels } from 'hooks/useGetQueryLabels';
import { Dispatch, SetStateAction, useCallback } from 'react';
import { ColumnUnit } from 'types/api/dashboard/getAll';
import { EQueryType } from 'types/common/dashboard';
import YAxisUnitSelector from '../YAxisUnitSelector';
@ -17,40 +17,33 @@ export function ColumnUnitSelector(
props: ColumnUnitSelectorProps,
): JSX.Element {
const { currentQuery } = useQueryBuilder();
function getAggregateColumnsNamesAndLabels(): string[] {
if (currentQuery.queryType === EQueryType.QUERY_BUILDER) {
const queries = currentQuery.builder.queryData.map((q) => q.queryName);
console.log(currentQuery.builder.queryData, queries);
const formulas = currentQuery.builder.queryFormulas.map((q) => q.queryName);
return [...queries, ...formulas];
}
if (currentQuery.queryType === EQueryType.CLICKHOUSE) {
return currentQuery.clickhouse_sql.map((q) => q.name);
}
return currentQuery.promql.map((q) => q.name);
}
const { columnUnits, setColumnUnits } = props;
const aggregationQueries = getAggregateColumnsNamesAndLabels();
function handleColumnUnitSelect(queryName: string, value: string): void {
setColumnUnits((prev) => ({
...prev,
[queryName]: value,
}));
}
const aggregationQueries = useGetQueryLabels(currentQuery);
const handleColumnUnitSelect = useCallback(
(queryName: string, value: string): void => {
setColumnUnits((prev) => ({
...prev,
[queryName]: value,
}));
},
[setColumnUnits],
);
return (
<section className="column-unit-selector">
<Typography.Text className="heading">Column Units</Typography.Text>
{aggregationQueries.map((query) => (
{aggregationQueries.map(({ value, label }) => (
<YAxisUnitSelector
defaultValue={columnUnits[query]}
onSelect={(value: string): void => handleColumnUnitSelect(query, value)}
fieldLabel={query}
key={query}
defaultValue={columnUnits[value]}
onSelect={(unitValue: string): void =>
handleColumnUnitSelect(value, unitValue)
}
fieldLabel={label}
key={value}
handleClear={(): void => {
handleColumnUnitSelect(query, '');
handleColumnUnitSelect(value, '');
}}
/>
))}

View File

@ -4,11 +4,11 @@ import './ThresholdSelector.styles.scss';
import { Typography } from 'antd';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetQueryLabels } from 'hooks/useGetQueryLabels';
import { Antenna, Plus } from 'lucide-react';
import { useCallback } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid';
import Threshold from './Threshold';
@ -23,19 +23,7 @@ function ThresholdSelector({
}: ThresholdSelectorProps): JSX.Element {
const { currentQuery } = useQueryBuilder();
function getAggregateColumnsNamesAndLabels(): string[] {
if (currentQuery.queryType === EQueryType.QUERY_BUILDER) {
const queries = currentQuery.builder.queryData.map((q) => q.queryName);
const formulas = currentQuery.builder.queryFormulas.map((q) => q.queryName);
return [...queries, ...formulas];
}
if (currentQuery.queryType === EQueryType.CLICKHOUSE) {
return currentQuery.clickhouse_sql.map((q) => q.name);
}
return currentQuery.promql.map((q) => q.name);
}
const aggregationQueries = getAggregateColumnsNamesAndLabels();
const aggregationQueries = useGetQueryLabels(currentQuery);
const moveThreshold = useCallback(
(dragIndex: number, hoverIndex: number) => {
@ -65,7 +53,7 @@ function ThresholdSelector({
moveThreshold,
keyIndex: thresholds.length,
selectedGraph,
thresholdTableOptions: aggregationQueries[0] || '',
thresholdTableOptions: aggregationQueries[0]?.value || '',
},
...thresholds,
]);
@ -104,10 +92,7 @@ function ThresholdSelector({
moveThreshold={moveThreshold}
selectedGraph={selectedGraph}
thresholdLabel={threshold.thresholdLabel}
tableOptions={aggregationQueries.map((query) => ({
value: query,
label: query,
}))}
tableOptions={aggregationQueries}
thresholdTableOptions={threshold.thresholdTableOptions}
columnUnits={columnUnits}
/>

View File

@ -0,0 +1,48 @@
import { getQueryLabelWithAggregation } from 'components/QueryBuilderV2/utils';
import { useMemo } from 'react';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
export const useGetQueryLabels = (
currentQuery: Query,
): { label: string; value: string }[] => {
const legendMap = useMemo(() => {
const newLegendMap: Record<string, string> = {};
if (currentQuery?.queryType === EQueryType.QUERY_BUILDER) {
currentQuery?.builder?.queryData?.forEach((q) => {
if (q.legend) {
newLegendMap[q.queryName] = q.legend;
}
});
currentQuery?.builder?.queryFormulas?.forEach((f) => {
if (f.legend) {
newLegendMap[f.queryName] = f.legend;
}
});
}
return newLegendMap;
}, [currentQuery]);
return useMemo(() => {
if (currentQuery?.queryType === EQueryType.QUERY_BUILDER) {
const queryLabels = getQueryLabelWithAggregation(
currentQuery?.builder?.queryData || [],
legendMap,
);
const formulaLabels = currentQuery?.builder?.queryFormulas?.map(
(formula) => ({
label: formula.queryName,
value: formula.queryName,
}),
);
return [...queryLabels, ...formulaLabels];
}
if (currentQuery?.queryType === EQueryType.CLICKHOUSE) {
return currentQuery?.clickhouse_sql?.map((q) => ({
label: q.name,
value: q.name,
}));
}
return currentQuery?.promql?.map((q) => ({ label: q.name, value: q.name }));
}, [currentQuery, legendMap]);
};