From aa98123e7209bbd5c0dce5a04409b29cd6888acf Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Tue, 19 Apr 2022 23:20:09 -0500 Subject: [PATCH] TimeSeries: always try to detect & fix reverse-sorted time field (#47906) --- .../transformers/joinDataFrames.test.ts | 2 +- .../transformers/joinDataFrames.ts | 30 +++++++++++-------- .../prepareTimeSeries/prepareTimeSeries.ts | 1 - public/app/plugins/panel/barchart/utils.ts | 9 +++++- .../app/plugins/panel/candlestick/fields.ts | 9 +++++- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts index d1612fdc839..a5d11987b68 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.test.ts @@ -188,7 +188,7 @@ describe('align frames', () => { ], }); - const out = outerJoinDataFrames({ frames: [series1], enforceSort: true, keepOriginIndices: true })!; + const out = outerJoinDataFrames({ frames: [series1], keepOriginIndices: true })!; expect( out.fields.map((f) => ({ name: f.name, diff --git a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts index 4288f3690e4..c366a55e5ff 100644 --- a/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts +++ b/packages/grafana-data/src/transformations/transformers/joinDataFrames.ts @@ -48,13 +48,6 @@ export interface JoinOptions { */ keep?: FieldMatcher; - /** - * When the result is a single frame, this will to a quick check to see if the values are sorted, - * and sort if necessary. If the first/last values are in order the whole vector is assumed to be - * sorted - */ - enforceSort?: boolean; - /** * @internal -- used when we need to keep a reference to the original frame/field index */ @@ -65,6 +58,21 @@ function getJoinMatcher(options: JoinOptions): FieldMatcher { return options.joinBy ?? pickBestJoinField(options.frames); } +/** + * @internal + */ +export function maybeSortFrame(frame: DataFrame, fieldIdx: number) { + if (fieldIdx >= 0) { + let sortByField = frame.fields[fieldIdx]; + + if (sortByField.type !== FieldType.string && !isLikelyAscendingVector(sortByField.values)) { + frame = sortDataFrame(frame, fieldIdx); + } + } + + return frame; +} + /** * This will return a single frame joined by the first matching field. When a join field is not specified, * the default will use the first time field @@ -109,12 +117,8 @@ export function outerJoinDataFrames(options: JoinOptions): DataFrame | undefined } } - if (options.enforceSort) { - if (joinIndex >= 0) { - if (!isLikelyAscendingVector(frameCopy.fields[joinIndex].values)) { - frameCopy = sortDataFrame(frameCopy, joinIndex); - } - } + if (joinIndex >= 0) { + frameCopy = maybeSortFrame(frameCopy, joinIndex); } if (options.keep) { diff --git a/public/app/features/transformers/prepareTimeSeries/prepareTimeSeries.ts b/public/app/features/transformers/prepareTimeSeries/prepareTimeSeries.ts index 4d237750620..e2248bc4502 100644 --- a/public/app/features/transformers/prepareTimeSeries/prepareTimeSeries.ts +++ b/public/app/features/transformers/prepareTimeSeries/prepareTimeSeries.ts @@ -301,7 +301,6 @@ export const prepareTimeSeriesTransformer: SynchronousDataTransformerInfo f.type === FieldType.time) + ) + : outerJoinDataFrames({ frames: series }); if (!frame) { return { warn: 'Unable to join data' } as BarChartDisplayValues; } diff --git a/public/app/plugins/panel/candlestick/fields.ts b/public/app/plugins/panel/candlestick/fields.ts index b81b98af79b..be833fbde45 100644 --- a/public/app/plugins/panel/candlestick/fields.ts +++ b/public/app/plugins/panel/candlestick/fields.ts @@ -7,6 +7,7 @@ import { GrafanaTheme2, outerJoinDataFrames, } from '@grafana/data'; +import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames'; import { findField } from 'app/features/dimensions'; import { prepareGraphableFields } from '../timeseries/utils'; import { CandlestickOptions, CandlestickFieldMap, VizDisplayMode } from './models.gen'; @@ -102,7 +103,13 @@ export function prepareCandlestickFields( // All fields const fieldMap = options.fields ?? {}; - const aligned = series.length === 1 ? series[0] : outerJoinDataFrames({ frames: series, enforceSort: true }); + const aligned = + series.length === 1 + ? maybeSortFrame( + series[0], + series[0].fields.findIndex((f) => f.type === FieldType.time) + ) + : outerJoinDataFrames({ frames: series }); if (!aligned?.length) { return null; }