mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG: sort ascending if the values appear reversed (#30405)
This commit is contained in:
parent
e9e16cee6d
commit
f27450ed94
@ -29,7 +29,7 @@ import { isNumber } from 'lodash';
|
||||
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
||||
|
||||
export interface XYFieldMatchers {
|
||||
x: FieldMatcher;
|
||||
x: FieldMatcher; // first match
|
||||
y: FieldMatcher;
|
||||
}
|
||||
export interface GraphNGProps extends Omit<PlotProps, 'data' | 'config'> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ArrayVector, DataFrame, FieldType, toDataFrame } from '@grafana/data';
|
||||
import { AlignedFrameWithGapTest } from '../uPlot/types';
|
||||
import { alignDataFrames } from './utils';
|
||||
import { alignDataFrames, isLikelyAscendingVector } from './utils';
|
||||
|
||||
describe('alignDataFrames', () => {
|
||||
describe('aligned frame', () => {
|
||||
@ -215,4 +215,31 @@ describe('alignDataFrames', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('check ascending data', () => {
|
||||
it('simple ascending', () => {
|
||||
const v = new ArrayVector([1, 2, 3, 4, 5]);
|
||||
expect(isLikelyAscendingVector(v)).toBeTruthy();
|
||||
});
|
||||
it('simple ascending with null', () => {
|
||||
const v = new ArrayVector([null, 2, 3, 4, null]);
|
||||
expect(isLikelyAscendingVector(v)).toBeTruthy();
|
||||
});
|
||||
it('single value', () => {
|
||||
const v = new ArrayVector([null, null, null, 4, null]);
|
||||
expect(isLikelyAscendingVector(v)).toBeTruthy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([4]))).toBeTruthy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([]))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('middle values', () => {
|
||||
const v = new ArrayVector([null, null, 5, 4, null]);
|
||||
expect(isLikelyAscendingVector(v)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('decending', () => {
|
||||
expect(isLikelyAscendingVector(new ArrayVector([7, 6, null]))).toBeFalsy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([7, 8, 6]))).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -9,6 +9,8 @@ import {
|
||||
FieldType,
|
||||
FieldState,
|
||||
DataFrameFieldIndex,
|
||||
sortDataFrame,
|
||||
Vector,
|
||||
} from '@grafana/data';
|
||||
import { AlignedFrameWithGapTest } from '../uPlot/types';
|
||||
import uPlot, { AlignedData, JoinNullMode } from 'uplot';
|
||||
@ -16,24 +18,23 @@ import { XYFieldMatchers } from './GraphNG';
|
||||
|
||||
// the results ofter passing though data
|
||||
export interface XYDimensionFields {
|
||||
x: Field[];
|
||||
y: Field[];
|
||||
x: Field; // independent axis (cause)
|
||||
y: Field[]; // dependent axis (effect)
|
||||
}
|
||||
|
||||
export function mapDimesions(match: XYFieldMatchers, frame: DataFrame, frames?: DataFrame[]): XYDimensionFields {
|
||||
const out: XYDimensionFields = {
|
||||
x: [],
|
||||
y: [],
|
||||
};
|
||||
let x: Field | undefined;
|
||||
const y: Field[] = [];
|
||||
|
||||
for (const field of frame.fields) {
|
||||
if (match.x(field, frame, frames ?? [])) {
|
||||
out.x.push(field);
|
||||
if (!x && match.x(field, frame, frames ?? [])) {
|
||||
x = field;
|
||||
}
|
||||
if (match.y(field, frame, frames ?? [])) {
|
||||
out.y.push(field);
|
||||
y.push(field);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
return { x: x as Field, y };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,26 +59,29 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
|
||||
}
|
||||
|
||||
for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) {
|
||||
const frame = frames[frameIndex];
|
||||
const dims = mapDimesions(fields, frame, frames);
|
||||
let frame = frames[frameIndex];
|
||||
let dims = mapDimesions(fields, frame, frames);
|
||||
|
||||
if (!(dims.x.length && dims.y.length)) {
|
||||
if (!(dims.x && dims.y.length)) {
|
||||
continue; // no numeric and no time fields
|
||||
}
|
||||
|
||||
if (dims.x.length > 1) {
|
||||
throw new Error('Only a single x field is supported');
|
||||
// Quick check that x is ascending order
|
||||
if (!isLikelyAscendingVector(dims.x.values)) {
|
||||
const xIndex = frame.fields.indexOf(dims.x);
|
||||
frame = sortDataFrame(frame, xIndex);
|
||||
dims = mapDimesions(fields, frame, frames);
|
||||
}
|
||||
|
||||
let nullModesFrame: JoinNullMode[] = [0];
|
||||
|
||||
// Add the first X axis
|
||||
if (!sourceFields.length) {
|
||||
sourceFields.push(dims.x[0]);
|
||||
sourceFields.push(dims.x);
|
||||
}
|
||||
|
||||
const alignedData: AlignedData = [
|
||||
dims.x[0].values.toArray(), // The x axis (time)
|
||||
dims.x.values.toArray(), // The x axis (time)
|
||||
];
|
||||
|
||||
for (let fieldIndex = 0; fieldIndex < frame.fields.length; fieldIndex++) {
|
||||
@ -150,3 +154,34 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Quick test if the first and last points look to be ascending
|
||||
export function isLikelyAscendingVector(data: Vector): boolean {
|
||||
let first: any = undefined;
|
||||
|
||||
for (let idx = 0; idx < data.length; idx++) {
|
||||
const v = data.get(idx);
|
||||
if (v != null) {
|
||||
if (first != null) {
|
||||
if (first > v) {
|
||||
return false; // descending
|
||||
}
|
||||
break;
|
||||
}
|
||||
first = v;
|
||||
}
|
||||
}
|
||||
|
||||
let idx = data.length - 1;
|
||||
while (idx >= 0) {
|
||||
const v = data.get(idx--);
|
||||
if (v != null) {
|
||||
if (first > v) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // only one non-null point
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user