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(
out.fields.map((f) => ({
name: f.name,

View File

@ -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);
}
}
frameCopy = maybeSortFrame(frameCopy, joinIndex);
}
if (options.keep) {

View File

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

View File

@ -28,6 +28,7 @@ import {
import { orderBy } from 'lodash';
import { findField } from 'app/features/dimensions';
import { getStackingGroups } from '@grafana/ui/src/components/uPlot/utils';
import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames';
function getBarCharScaleOrientation(orientation: VizOrientation) {
if (orientation === VizOrientation.Vertical) {
@ -301,7 +302,13 @@ export function prepareBarChartDisplayValues(
}
// 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) {
return { warn: 'Unable to join data' } as BarChartDisplayValues;
}

View File

@ -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;
}