From d739481c9a2a830e0f4cd98f1299f41aba020c98 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 29 Apr 2025 21:00:43 -0500 Subject: [PATCH] Transformations: Use field name matcher for finding group by fields (#104664) Co-authored-by: Leon Sorokin --- .../transformers/groupBy.test.ts | 46 +++++++++++++++++++ .../transformations/transformers/groupBy.ts | 28 +++++++---- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/packages/grafana-data/src/transformations/transformers/groupBy.test.ts b/packages/grafana-data/src/transformations/transformers/groupBy.test.ts index abfcc57c2c0..64a81b033df 100644 --- a/packages/grafana-data/src/transformations/transformers/groupBy.test.ts +++ b/packages/grafana-data/src/transformations/transformers/groupBy.test.ts @@ -461,4 +461,50 @@ describe('GroupBy transformer', () => { expect(result[0].fields).toEqual(expected); }); }); + + it('should match on base name if did not match on displayName', async () => { + const testSeries = toDataFrame({ + name: 'A', + fields: [ + { name: 'message', type: FieldType.string, values: ['A', 'A'], config: { displayName: 'MyMessage' } }, + { name: 'values', type: FieldType.number, values: [1, 2] }, + ], + }); + + const cfg: DataTransformerConfig = { + id: DataTransformerID.groupBy, + options: { + fields: { + message: { + operation: GroupByOperationID.groupBy, + aggregations: [], + }, + values: { + operation: GroupByOperationID.aggregate, + aggregations: [ReducerID.sum], + }, + }, + }, + }; + + await expect(transformDataFrame([cfg], [testSeries])).toEmitValuesWith((received) => { + const result = received[0]; + const expected: Field[] = [ + { + name: 'message', + type: FieldType.string, + values: ['A'], + config: { displayName: 'MyMessage' }, + }, + { + name: 'values (sum)', + type: FieldType.number, + values: [3], + config: {}, + }, + ]; + + expect(result[0].fields).toEqual(expected); + }); + }); }); diff --git a/packages/grafana-data/src/transformations/transformers/groupBy.ts b/packages/grafana-data/src/transformations/transformers/groupBy.ts index 3ee7b106ffb..ebc878547f1 100644 --- a/packages/grafana-data/src/transformations/transformers/groupBy.ts +++ b/packages/grafana-data/src/transformations/transformers/groupBy.ts @@ -4,6 +4,8 @@ import { getFieldDisplayName } from '../../field/fieldState'; import { DataFrame, Field } from '../../types/dataFrame'; import { DataTransformerInfo, TransformationApplicabilityLevels } from '../../types/transformations'; import { getFieldTypeForReducer, reduceField, ReducerID } from '../fieldReducer'; +import { getFieldMatcher } from '../matchers'; +import { FieldMatcherID } from '../matchers/ids'; import { DataTransformerID } from './ids'; import { findMaxFields } from './utils'; @@ -57,20 +59,31 @@ export const groupByTransformer: DataTransformerInfo operator: (options) => (source) => source.pipe( map((data) => { - const hasValidConfig = Object.keys(options.fields).find( - (name) => options.fields[name].operation === GroupByOperationID.groupBy - ); + const groupByFieldNames: string[] = []; - if (!hasValidConfig) { + for (const [k, v] of Object.entries(options.fields)) { + if (v.operation === GroupByOperationID.groupBy) { + groupByFieldNames.push(k); + } + } + + if (groupByFieldNames.length === 0) { return data; } + const matcher = getFieldMatcher({ + id: FieldMatcherID.byNames, + options: { names: groupByFieldNames }, + }); + const processed: DataFrame[] = []; for (const frame of data) { // Create a list of fields to group on // If there are none we skip the rest - const groupByFields: Field[] = frame.fields.filter((field) => shouldGroupOnField(field, options)); + + const groupByFields: Field[] = frame.fields.filter((field) => matcher(field, frame, data)); + if (groupByFields.length === 0) { continue; } @@ -131,11 +144,6 @@ export const groupByTransformer: DataTransformerInfo ), }; -const shouldGroupOnField = (field: Field, options: GroupByTransformerOptions): boolean => { - const fieldName = getFieldDisplayName(field); - return options?.fields[fieldName]?.operation === GroupByOperationID.groupBy; -}; - const shouldCalculateField = (field: Field, options: GroupByTransformerOptions): boolean => { const fieldName = getFieldDisplayName(field); return (