TimeSeries: always try to detect & fix reverse-sorted time field (#47906)

This commit is contained in:
Leon Sorokin 2022-04-19 23:20:09 -05:00 committed by GitHub
parent 89fa35a53f
commit aa98123e72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 17 deletions

View File

@ -188,7 +188,7 @@ describe('align frames', () => {
], ],
}); });
const out = outerJoinDataFrames({ frames: [series1], enforceSort: true, keepOriginIndices: true })!; const out = outerJoinDataFrames({ frames: [series1], keepOriginIndices: true })!;
expect( expect(
out.fields.map((f) => ({ out.fields.map((f) => ({
name: f.name, name: f.name,

View File

@ -48,13 +48,6 @@ export interface JoinOptions {
*/ */
keep?: FieldMatcher; 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 * @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); 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, * 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 * 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 (joinIndex >= 0) {
if (!isLikelyAscendingVector(frameCopy.fields[joinIndex].values)) { frameCopy = maybeSortFrame(frameCopy, joinIndex);
frameCopy = sortDataFrame(frameCopy, joinIndex);
}
}
} }
if (options.keep) { if (options.keep) {

View File

@ -301,7 +301,6 @@ export const prepareTimeSeriesTransformer: SynchronousDataTransformerInfo<Prepar
const frame = outerJoinDataFrames({ const frame = outerJoinDataFrames({
frames: data, frames: data,
joinBy: fieldMatchers.get(FieldMatcherID.firstTimeField).get({}), joinBy: fieldMatchers.get(FieldMatcherID.firstTimeField).get({}),
enforceSort: true,
keepOriginIndices: true, keepOriginIndices: true,
}); });
return frame ? [frame] : []; return frame ? [frame] : [];

View File

@ -28,6 +28,7 @@ import {
import { orderBy } from 'lodash'; import { orderBy } from 'lodash';
import { findField } from 'app/features/dimensions'; import { findField } from 'app/features/dimensions';
import { getStackingGroups } from '@grafana/ui/src/components/uPlot/utils'; import { getStackingGroups } from '@grafana/ui/src/components/uPlot/utils';
import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames';
function getBarCharScaleOrientation(orientation: VizOrientation) { function getBarCharScaleOrientation(orientation: VizOrientation) {
if (orientation === VizOrientation.Vertical) { if (orientation === VizOrientation.Vertical) {
@ -301,7 +302,13 @@ export function prepareBarChartDisplayValues(
} }
// Bar chart requires a single frame // Bar chart requires a single frame
const frame = series.length === 1 ? series[0] : outerJoinDataFrames({ frames: series, enforceSort: false }); const frame =
series.length === 1
? maybeSortFrame(
series[0],
series[0].fields.findIndex((f) => f.type === FieldType.time)
)
: outerJoinDataFrames({ frames: series });
if (!frame) { if (!frame) {
return { warn: 'Unable to join data' } as BarChartDisplayValues; return { warn: 'Unable to join data' } as BarChartDisplayValues;
} }

View File

@ -7,6 +7,7 @@ import {
GrafanaTheme2, GrafanaTheme2,
outerJoinDataFrames, outerJoinDataFrames,
} from '@grafana/data'; } from '@grafana/data';
import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames';
import { findField } from 'app/features/dimensions'; import { findField } from 'app/features/dimensions';
import { prepareGraphableFields } from '../timeseries/utils'; import { prepareGraphableFields } from '../timeseries/utils';
import { CandlestickOptions, CandlestickFieldMap, VizDisplayMode } from './models.gen'; import { CandlestickOptions, CandlestickFieldMap, VizDisplayMode } from './models.gen';
@ -102,7 +103,13 @@ export function prepareCandlestickFields(
// All fields // All fields
const fieldMap = options.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) { if (!aligned?.length) {
return null; return null;
} }