From 8a1407c9f44fda2982cdb69009624009d5763834 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 Date: Fri, 4 Jul 2025 02:09:35 +0530 Subject: [PATCH] feat: added support for multiaggregation in columnUnit and thresholds --- .../src/components/QueryBuilderV2/utils.ts | 48 +++++++++++++++++ .../container/GridTableComponent/index.tsx | 11 ++-- .../ColumnUnitSelector.styles.scss | 2 +- .../ColumnUnitSelector/ColumnUnitSelector.tsx | 51 ++++++++----------- .../Threshold/ThresholdSelector.tsx | 23 ++------- frontend/src/hooks/useGetQueryLabels.ts | 48 +++++++++++++++++ 6 files changed, 129 insertions(+), 54 deletions(-) create mode 100644 frontend/src/hooks/useGetQueryLabels.ts diff --git a/frontend/src/components/QueryBuilderV2/utils.ts b/frontend/src/components/QueryBuilderV2/utils.ts index 48a7522712ae..f25f821785ef 100644 --- a/frontend/src/components/QueryBuilderV2/utils.ts +++ b/frontend/src/components/QueryBuilderV2/utils.ts @@ -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 = {}, +): { 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) || {}; + + 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; +} diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx index eee02f15e382..b61a8b9ea1b8 100644 --- a/frontend/src/container/GridTableComponent/index.tsx +++ b/frontend/src/container/GridTableComponent/index.tsx @@ -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]; } }); diff --git a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss index 5c824e7d9267..bd0870932264 100644 --- a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss +++ b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss @@ -17,7 +17,7 @@ align-items: center; .heading { - width: 20px; + width: 32px; } } } diff --git a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx index 5c24c186e3d2..49760c1a3231 100644 --- a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx +++ b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx @@ -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 (
Column Units - {aggregationQueries.map((query) => ( + {aggregationQueries.map(({ value, label }) => ( 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, ''); }} /> ))} diff --git a/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx b/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx index 994120951ed8..77b602684ae2 100644 --- a/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx +++ b/frontend/src/container/NewWidget/RightContainer/Threshold/ThresholdSelector.tsx @@ -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} /> diff --git a/frontend/src/hooks/useGetQueryLabels.ts b/frontend/src/hooks/useGetQueryLabels.ts new file mode 100644 index 000000000000..67b483c052bf --- /dev/null +++ b/frontend/src/hooks/useGetQueryLabels.ts @@ -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 = {}; + 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]); +};