From 5469cd34fad6a688cc49c73da4f639dd47e60b6a Mon Sep 17 00:00:00 2001 From: Yevhen Shevchenko <90138953+yeshev@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:44:46 +0300 Subject: [PATCH] fix: column name with legend (#3200) * fix: column name with legend * fix: render columns with data * fix: index * fix: render rows * fix: remove log * fix: return operators for entiry metric * fix: remove noop and rate for table metric * fix: for request * chore: allow count attribute to be empty --------- Co-authored-by: Srikanth Chekuri --- frontend/src/constants/queryBuilder.ts | 2 +- .../getOperatorsBySourceAndPanelType.ts | 13 +- .../lib/query/createTableColumnsFromQuery.ts | 176 ++++++++++++------ pkg/query-service/model/v3/v3.go | 3 +- 4 files changed, 130 insertions(+), 64 deletions(-) diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts index 509b97340c..515be4d080 100644 --- a/frontend/src/constants/queryBuilder.ts +++ b/frontend/src/constants/queryBuilder.ts @@ -125,7 +125,7 @@ export const initialFilters: TagFilter = { export const initialQueryBuilderFormValues: IBuilderQuery = { dataSource: DataSource.METRICS, queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }), - aggregateOperator: MetricAggregateOperator.NOOP, + aggregateOperator: MetricAggregateOperator.COUNT, aggregateAttribute: initialAutocompleteData, filters: { items: [], op: 'AND' }, expression: createNewBuilderItemName({ diff --git a/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts index e415d8f7f4..e189275802 100644 --- a/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts +++ b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts @@ -1,5 +1,9 @@ import { mapOfOperators, PANEL_TYPES } from 'constants/queryBuilder'; -import { DataSource, StringOperators } from 'types/common/queryBuilder'; +import { + DataSource, + MetricAggregateOperator, + StringOperators, +} from 'types/common/queryBuilder'; import { SelectOption } from 'types/common/select'; type GetQueryOperatorsParams = { @@ -19,6 +23,13 @@ export const getOperatorsBySourceAndPanelType = ({ (operator) => operator.value === StringOperators.NOOP, ); } + if (panelType === PANEL_TYPES.TABLE && dataSource === DataSource.METRICS) { + operatorsByDataSource = operatorsByDataSource.filter( + (operator) => + operator.value !== MetricAggregateOperator.NOOP && + operator.value !== MetricAggregateOperator.RATE, + ); + } if ( dataSource !== DataSource.METRICS && panelType !== PANEL_TYPES.LIST && diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts index 2241c166f1..bf95335016 100644 --- a/frontend/src/lib/query/createTableColumnsFromQuery.ts +++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts @@ -6,7 +6,6 @@ import { } from 'constants/queryBuilder'; import { FORMULA_REGEXP } from 'constants/regExp'; import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces'; -import { toCapitalize } from 'lib/toCapitalize'; import { ReactNode } from 'react'; import { IBuilderFormula, @@ -145,7 +144,7 @@ const addOperatorFormulaColumns = ( let formulaLabel = `${formulaQuery.queryName}(${formulaQuery.expression})`; if (formulaQuery.legend) { - formulaLabel += ` - ${formulaQuery.legend}`; + formulaLabel = formulaQuery.legend; } const formulaColumn: DynamicColumn = { @@ -171,18 +170,14 @@ const addOperatorFormulaColumns = ( } if (currentQueryData.legend) { - operatorLabel += ` - ${currentQueryData.legend}`; - } else { - operatorLabel += ` - ${currentQueryData.queryName}`; + operatorLabel = currentQueryData.legend; } - const resultValue = `${toCapitalize(operatorLabel)}`; - const operatorColumn: DynamicColumn = { query, field: currentQueryData.queryName, dataIndex: currentQueryData.queryName, - title: customLabel || resultValue, + title: customLabel || operatorLabel, data: [], type: 'operator', // sortable: isNumber, @@ -218,26 +213,24 @@ const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => { const dynamicColumns: DynamicColumns = []; queryTableData.forEach((currentQuery) => { + const { series, queryName, list } = currentQuery; + const currentStagedQuery = getQueryByName( query.builder, - currentQuery.queryName, - isFormula(currentQuery.queryName) ? 'queryFormulas' : 'queryData', + queryName, + isFormula(queryName) ? 'queryFormulas' : 'queryData', ); - if (currentQuery.list) { - currentQuery.list.forEach((listItem) => { + if (list) { + list.forEach((listItem) => { Object.keys(listItem.data).forEach((label) => { addListLabels(currentStagedQuery, label as ListItemKey, dynamicColumns); }); }); } - if (currentQuery.series) { - const isValuesColumnExist = currentQuery.series.some( - (item) => item.values.length > 0, - ); - const isEveryValuesExist = currentQuery.series.every( - (item) => item.values.length > 0, - ); + if (series) { + const isValuesColumnExist = series.some((item) => item.values.length > 0); + const isEveryValuesExist = series.every((item) => item.values.length > 0); if (isValuesColumnExist) { addOperatorFormulaColumns( @@ -247,7 +240,7 @@ const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => { ); } - currentQuery.series.forEach((seria) => { + series.forEach((seria) => { Object.keys(seria.labels).forEach((label) => { if (label === currentQuery?.queryName) return; @@ -277,49 +270,105 @@ const fillEmptyRowCells = ( }); }; -const fillData = ( - seria: SeriesItem, - columns: DynamicColumns, - queryName: string, - value?: SeriesItem['values'][number], -): void => { - const labelEntries = Object.entries(seria.labels); +const findSeriaValueFromAnotherQuery = ( + currentLabels: Record, + nextQuery: QueryDataV3 | null, +): SeriesItem | null => { + if (!nextQuery || !nextQuery.series) return null; - const unusedColumnsKeys = new Set( - columns.map((item) => item.field), - ); + let value = null; - columns.forEach((column) => { - if (queryName === column.field && value) { - column.data.push(parseFloat(value.value).toFixed(2)); - unusedColumnsKeys.delete(column.field); - return; + const labelEntries = Object.entries(currentLabels); + + nextQuery.series.forEach((seria) => { + const localLabelEntries = Object.entries(seria.labels); + if (localLabelEntries.length !== labelEntries.length) return; + + const isExistLabels = localLabelEntries.find(([key, value]) => + labelEntries.find( + ([currentKey, currentValue]) => + currentKey === key && currentValue === value, + ), + ); + + if (isExistLabels) { + value = seria; } - - labelEntries.forEach(([key, currentValue]) => { - if (column.field === key) { - column.data.push(currentValue); - unusedColumnsKeys.delete(key); - } - }); - - fillEmptyRowCells(unusedColumnsKeys, columns, column); }); + + return value; }; -const fillDataFromSeria = ( - seria: SeriesItem, - columns: DynamicColumns, +const isEqualQueriesByLabel = ( + equalQueries: string[], queryName: string, +): boolean => equalQueries.includes(queryName); + +const fillDataFromSeries = ( + currentQuery: QueryDataV3, + queryTableData: QueryDataV3[], + columns: DynamicColumns, + equalQueriesByLabels: string[], + // TODO: fix it + // eslint-disable-next-line sonarjs/cognitive-complexity ): void => { - if (seria.values.length === 0) { - fillData(seria, columns, queryName); + const { series, queryName } = currentQuery; + const isEqualQuery = isEqualQueriesByLabel(equalQueriesByLabels, queryName); - return; - } + if (!series) return; - seria.values.forEach((value) => { - fillData(seria, columns, queryName, value); + series.forEach((seria) => { + const labelEntries = Object.entries(seria.labels); + + const unusedColumnsKeys = new Set( + columns.map((item) => item.field), + ); + + columns.forEach((column) => { + if (queryName === column.field) { + if (seria.values.length === 0) return; + + column.data.push(parseFloat(seria.values[0].value).toFixed(2)); + unusedColumnsKeys.delete(column.field); + return; + } + + if (column.type !== 'field' && column.field !== queryName) { + const nextQueryData = + queryTableData.find((q) => q.queryName === column.field) || null; + + const targetSeria = findSeriaValueFromAnotherQuery( + seria.labels, + nextQueryData, + ); + + if (targetSeria) { + const isEqual = isEqualQueriesByLabel(equalQueriesByLabels, column.field); + if (!isEqual) { + equalQueriesByLabels.push(column.field); + } + + column.data.push(parseFloat(targetSeria.values[0].value).toFixed(2)); + } else { + column.data.push('N/A'); + } + + unusedColumnsKeys.delete(column.field); + + return; + } + + if (isEqualQuery) return; + + labelEntries.forEach(([key, currentValue]) => { + if (column.field === key) { + column.data.push(currentValue); + unusedColumnsKeys.delete(key); + } + }); + + fillEmptyRowCells(unusedColumnsKeys, columns, column); + }); }); }; @@ -348,15 +397,20 @@ const fillColumnsData: FillColumnData = (queryTableData, cols) => { const formulas = cols.filter((item) => item.type === 'formula'); const resultColumns = [...fields, ...operators, ...formulas]; - queryTableData.forEach((currentQuery) => { - if (currentQuery.series) { - currentQuery.series.forEach((seria) => { - fillDataFromSeria(seria, resultColumns, currentQuery.queryName); - }); - } + const equalQueriesByLabels: string[] = []; - if (currentQuery.list) { - currentQuery.list.forEach((listItem) => { + queryTableData.forEach((currentQuery) => { + const { list } = currentQuery; + + fillDataFromSeries( + currentQuery, + queryTableData, + resultColumns, + equalQueriesByLabels, + ); + + if (list) { + list.forEach((listItem) => { fillDataFromList(listItem, resultColumns); }); } diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index d371273017..1fb4cf8e60 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -107,7 +107,8 @@ func (a AggregateOperator) RequireAttribute(dataSource DataSource) bool { switch dataSource { case DataSourceMetrics: switch a { - case AggregateOperatorNoOp: + case AggregateOperatorNoOp, + AggregateOperatorCount: return false default: return true