diff --git a/packages/grafana-data/src/dataframe/ArrowDataFrame.test.ts b/packages/grafana-data/src/dataframe/ArrowDataFrame.test.ts index a77a4b6b0e6..96efaccfce5 100644 --- a/packages/grafana-data/src/dataframe/ArrowDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/ArrowDataFrame.test.ts @@ -43,7 +43,7 @@ describe('Read/Write arrow Table to DataFrame', () => { ], }); - const table = grafanaDataFrameToArrowTable(frame); + const table = grafanaDataFrameToArrowTable(frame, true); expect(table.length).toEqual(frame.length); // Now back to DataFrame diff --git a/packages/grafana-data/src/field/fieldState.test.ts b/packages/grafana-data/src/field/fieldState.test.ts index f715d15f401..713fd8b7a9e 100644 --- a/packages/grafana-data/src/field/fieldState.test.ts +++ b/packages/grafana-data/src/field/fieldState.test.ts @@ -54,6 +54,40 @@ describe('Check field state calculations (displayName and id)', () => { expect(title).toEqual('Series A Field 1'); }); + it('should add field name count to name if it exists more than once and is equal to TIME_SERIES_VALUE_FIELD_NAME', () => { + const title = checkScenario({ + frames: [ + toDataFrame({ + fields: [{ name: TIME_SERIES_VALUE_FIELD_NAME }, { name: TIME_SERIES_VALUE_FIELD_NAME }], + }), + ], + }); + const title2 = checkScenario({ + frames: [ + toDataFrame({ + fields: [{ name: TIME_SERIES_VALUE_FIELD_NAME }, { name: TIME_SERIES_VALUE_FIELD_NAME }], + }), + ], + fieldIndex: 1, + }); + + expect(title).toEqual('Value 1'); + expect(title2).toEqual('Value 2'); + }); + + it('should add field name count to name if field name exists more than once', () => { + const title2 = checkScenario({ + frames: [ + toDataFrame({ + fields: [{ name: 'A' }, { name: 'A' }], + }), + ], + fieldIndex: 1, + }); + + expect(title2).toEqual('A 2'); + }); + it('should only use label value if only one label', () => { const title = checkScenario({ frames: [ diff --git a/packages/grafana-data/src/field/fieldState.ts b/packages/grafana-data/src/field/fieldState.ts index 10d1bd736e8..d0ebccc3c2f 100644 --- a/packages/grafana-data/src/field/fieldState.ts +++ b/packages/grafana-data/src/field/fieldState.ts @@ -125,9 +125,46 @@ function calculateFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: displayName = TIME_SERIES_VALUE_FIELD_NAME; } + // Ensure unique field name + if (displayName === field.name) { + displayName = getUniqueFieldName(field, frame); + } + return displayName; } +function getUniqueFieldName(field: Field, frame?: DataFrame) { + let dupeCount = 0; + let foundSelf = false; + + if (frame) { + for (let i = 0; i < frame.fields.length; i++) { + const otherField = frame.fields[i]; + + if (field === otherField) { + foundSelf = true; + + if (dupeCount > 0) { + dupeCount++; + break; + } + } else if (field.name === otherField.name) { + dupeCount++; + + if (foundSelf) { + break; + } + } + } + } + + if (dupeCount) { + return `${field.name} ${dupeCount}`; + } + + return field.name; +} + /** * Checks all data frames and return name of label if there is only one label name in all frames */ diff --git a/packages/grafana-data/src/transformations/transformers/seriesToColumns.test.ts b/packages/grafana-data/src/transformations/transformers/seriesToColumns.test.ts index e026b7d3eb4..b7082341eba 100644 --- a/packages/grafana-data/src/transformations/transformers/seriesToColumns.test.ts +++ b/packages/grafana-data/src/transformations/transformers/seriesToColumns.test.ts @@ -346,4 +346,55 @@ describe('SeriesToColumns Transformer', () => { }, ]); }); + + it('handles duplicate field name', () => { + const cfg: DataTransformerConfig = { + id: DataTransformerID.seriesToColumns, + options: { + byField: 'time', + }, + }; + + const frame1 = toDataFrame({ + fields: [ + { name: 'time', type: FieldType.time, values: [1] }, + { name: 'temperature', type: FieldType.number, values: [10] }, + ], + }); + + const frame2 = toDataFrame({ + fields: [ + { name: 'time', type: FieldType.time, values: [1] }, + { name: 'temperature', type: FieldType.number, values: [20] }, + ], + }); + + const filtered = transformDataFrame([cfg], [frame1, frame2])[0]; + + expect(filtered.fields).toEqual([ + { + name: 'time', + state: { displayName: 'time' }, + type: FieldType.time, + values: new ArrayVector([1]), + config: {}, + }, + { + name: 'temperature', + state: { displayName: 'temperature 1' }, + type: FieldType.number, + values: new ArrayVector([10]), + config: {}, + labels: {}, + }, + { + name: 'temperature', + state: { displayName: 'temperature 2' }, + type: FieldType.number, + values: new ArrayVector([20]), + config: {}, + labels: {}, + }, + ]); + }); }); diff --git a/packages/grafana-data/src/transformations/transformers/seriesToColumns.ts b/packages/grafana-data/src/transformations/transformers/seriesToColumns.ts index 50f4a26ae95..3b8eea79b87 100644 --- a/packages/grafana-data/src/transformations/transformers/seriesToColumns.ts +++ b/packages/grafana-data/src/transformations/transformers/seriesToColumns.ts @@ -68,7 +68,7 @@ export const seriesToColumnsTransformer: DataTransformerInfo