The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/core/components/GraphNG/utils.ts

143 lines
4.2 KiB

import { DataFrame, Field, FieldType, outerJoinDataFrames, TimeRange, applyNullInsertThreshold } from '@grafana/data';
import { NULL_EXPAND, NULL_REMOVE, NULL_RETAIN, nullToUndefThreshold } from '@grafana/data/internal';
import { GraphDrawStyle } from '@grafana/schema';
import { XYFieldMatchers } from './types';
function isVisibleBarField(f: Field) {
return (
f.type === FieldType.number && f.config.custom?.drawStyle === GraphDrawStyle.Bars && !f.config.custom?.hideFrom?.viz
);
}
export function getRefField(frame: DataFrame, refFieldName?: string | null) {
return frame.fields.find((field) => {
// note: getFieldDisplayName() would require full DF[]
return refFieldName != null ? field.name === refFieldName : field.type === FieldType.time;
});
}
// will mutate the DataFrame's fields' values
function applySpanNullsThresholds(frame: DataFrame, refFieldName?: string | null) {
const refField = getRefField(frame, refFieldName);
let refValues = refField?.values;
for (let i = 0; i < frame.fields.length; i++) {
let field = frame.fields[i];
if (field === refField || isVisibleBarField(field)) {
continue;
}
let spanNulls = field.config.custom?.spanNulls;
if (typeof spanNulls === 'number') {
if (spanNulls !== -1 && refValues) {
field.values = nullToUndefThreshold(refValues, field.values, spanNulls);
}
}
}
return frame;
}
export function preparePlotFrame(frames: DataFrame[], dimFields: XYFieldMatchers, timeRange?: TimeRange | null) {
let xField: Field;
loop: for (let frame of frames) {
for (let field of frame.fields) {
if (dimFields.x(field, frame, frames)) {
xField = field;
break loop;
}
}
}
// apply null insertions at interval
frames = frames.map((frame) => {
if (!xField?.state?.nullThresholdApplied) {
return applyNullInsertThreshold({
frame,
refFieldName: xField.name,
refFieldPseudoMin: timeRange?.from.valueOf(),
refFieldPseudoMax: timeRange?.to.valueOf(),
});
} else {
return frame;
}
});
let numBarSeries = frames.reduce(
(acc, frame) => acc + frame.fields.reduce((acc, field) => acc + (isVisibleBarField(field) ? 1 : 0), 0),
0
);
// to make bar widths of all series uniform (equal to narrowest bar series), find smallest distance between x points
let minXDelta = Infinity;
if (numBarSeries > 1) {
frames.forEach((frame) => {
if (!frame.fields.some(isVisibleBarField)) {
return;
}
const xVals = xField.values;
for (let i = 0; i < xVals.length; i++) {
if (i > 0) {
minXDelta = Math.min(minXDelta, xVals[i] - xVals[i - 1]);
}
}
});
}
let alignedFrame = outerJoinDataFrames({
frames,
joinBy: dimFields.x,
keep: dimFields.y,
keepOriginIndices: true,
// the join transformer force-deletes our state.displayName cache unless keepDisplayNames: true
// https://github.com/grafana/grafana/pull/31121
// https://github.com/grafana/grafana/pull/71806
keepDisplayNames: true,
// prevent minesweeper-expansion of nulls (gaps) when joining bars
// since bar width is determined from the minimum distance between non-undefined values
// (this strategy will still retain any original pre-join nulls, though)
nullMode: (field) => {
if (isVisibleBarField(field)) {
return NULL_RETAIN;
}
let spanNulls = field.config.custom?.spanNulls;
return spanNulls === true ? NULL_REMOVE : spanNulls === -1 ? NULL_RETAIN : NULL_EXPAND;
},
});
if (alignedFrame) {
alignedFrame = applySpanNullsThresholds(alignedFrame, xField!.name);
// append 2 null vals at minXDelta to bar series
if (minXDelta !== Infinity) {
alignedFrame.fields.forEach((f, fi) => {
let vals = f.values;
if (fi === 0) {
let lastVal = vals[vals.length - 1];
vals.push(lastVal + minXDelta, lastVal + 2 * minXDelta);
} else if (isVisibleBarField(f)) {
vals.push(null, null);
} else {
vals.push(undefined, undefined);
}
});
alignedFrame.length += 2;
}
return alignedFrame;
}
return null;
}