mirror of
https://github.com/grafana/grafana.git
synced 2026-07-29 15:59:50 -05:00
HeatmapNG: fix Prometheus frame-per-bucket heatmap response rendering (#46309)
This commit is contained in:
@@ -46,12 +46,28 @@ export function sortAscStrInf(aName?: string | null, bName?: string | null) {
|
||||
}
|
||||
|
||||
/** Given existing buckets, create a values style frame */
|
||||
// Assumes frames have already been sorted ASC and de-accumulated.
|
||||
export function createHeatmapFromBuckets(frames: DataFrame[]): DataFrame {
|
||||
frames = frames.slice();
|
||||
// assumes all Time fields are identical
|
||||
// TODO: handle null-filling w/ fields[0].config.interval?
|
||||
const xField = frames[0].fields[0];
|
||||
const xValues = xField.values.toArray();
|
||||
const yField = frames[0].fields[1];
|
||||
|
||||
// sort ASC by frame.name (Prometheus bucket bound)
|
||||
// or use frame.fields[1].config.displayNameFromDS ?
|
||||
frames.sort((a, b) => sortAscStrInf(a.name, b.name));
|
||||
// similar to initBins() below
|
||||
const len = xValues.length * frames.length;
|
||||
const xs = new Array(len);
|
||||
const ys = new Array(len);
|
||||
const counts2 = new Array(len);
|
||||
|
||||
const counts = frames.map((frame) => frame.fields[1].values.toArray().slice());
|
||||
|
||||
// transpose
|
||||
counts.forEach((bucketCounts, bi) => {
|
||||
for (let i = 0; i < bucketCounts.length; i++) {
|
||||
counts2[counts.length * i + bi] = bucketCounts[i];
|
||||
}
|
||||
});
|
||||
|
||||
const bucketBounds = frames.map((frame, i) => {
|
||||
return i; // until we have y ordinal scales working for facets/scatter
|
||||
@@ -70,39 +86,6 @@ export function createHeatmapFromBuckets(frames: DataFrame[]): DataFrame {
|
||||
*/
|
||||
});
|
||||
|
||||
// assumes all Time fields are identical
|
||||
// TODO: handle null-filling w/ fields[0].config.interval?
|
||||
const xField = frames[0].fields[0];
|
||||
const xValues = xField.values.toArray();
|
||||
const yField = frames[0].fields[1];
|
||||
|
||||
// similar to initBins() below
|
||||
const len = xValues.length * bucketBounds.length;
|
||||
const xs = new Array(len);
|
||||
const ys = new Array(len);
|
||||
const counts2 = new Array(len);
|
||||
|
||||
// cumulative counts
|
||||
const counts = frames.map((frame) => frame.fields[1].values.toArray().slice());
|
||||
|
||||
// de-accumulate
|
||||
counts.reverse();
|
||||
counts.forEach((bucketCounts, bi) => {
|
||||
if (bi < counts.length - 1) {
|
||||
for (let i = 0; i < bucketCounts.length; i++) {
|
||||
bucketCounts[i] -= counts[bi + 1][i];
|
||||
}
|
||||
}
|
||||
});
|
||||
counts.reverse();
|
||||
|
||||
// transpose
|
||||
counts.forEach((bucketCounts, bi) => {
|
||||
for (let i = 0; i < bucketCounts.length; i++) {
|
||||
counts2[counts.length * i + bi] = bucketCounts[i];
|
||||
}
|
||||
});
|
||||
|
||||
// fill flat/repeating array
|
||||
for (let i = 0, yi = 0, xi = 0; i < len; yi = ++i % bucketBounds.length) {
|
||||
ys[i] = bucketBounds[yi];
|
||||
@@ -144,6 +127,41 @@ export function createHeatmapFromBuckets(frames: DataFrame[]): DataFrame {
|
||||
};
|
||||
}
|
||||
|
||||
// Sorts frames ASC by numeric bucket name and de-accumulates values in each frame's Value field [1]
|
||||
// similar to Prometheus result_transformer.ts -> transformToHistogramOverTime()
|
||||
export function prepBucketFrames(frames: DataFrame[]): DataFrame[] {
|
||||
frames = frames.slice();
|
||||
|
||||
// sort ASC by frame.name (Prometheus bucket bound)
|
||||
// or use frame.fields[1].config.displayNameFromDS ?
|
||||
frames.sort((a, b) => sortAscStrInf(a.name, b.name));
|
||||
|
||||
// cumulative counts
|
||||
const counts = frames.map((frame) => frame.fields[1].values.toArray().slice());
|
||||
|
||||
// de-accumulate
|
||||
counts.reverse();
|
||||
counts.forEach((bucketCounts, bi) => {
|
||||
if (bi < counts.length - 1) {
|
||||
for (let i = 0; i < bucketCounts.length; i++) {
|
||||
bucketCounts[i] -= counts[bi + 1][i];
|
||||
}
|
||||
}
|
||||
});
|
||||
counts.reverse();
|
||||
|
||||
return frames.map((frame, i) => ({
|
||||
...frame,
|
||||
fields: [
|
||||
frame.fields[0],
|
||||
{
|
||||
...frame.fields[1],
|
||||
values: new ArrayVector(counts[i]),
|
||||
},
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
export function calculateHeatmapFromData(frames: DataFrame[], options: HeatmapCalculationOptions): DataFrame {
|
||||
//console.time('calculateHeatmapFromData');
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { DataFrame, DataFrameType, getDisplayProcessor, GrafanaTheme2 } from '@grafana/data';
|
||||
import {
|
||||
calculateHeatmapFromData,
|
||||
createHeatmapFromBuckets,
|
||||
sortAscStrInf,
|
||||
} from 'app/features/transformers/calculateHeatmap/heatmap';
|
||||
import { calculateHeatmapFromData, createHeatmapFromBuckets } from 'app/features/transformers/calculateHeatmap/heatmap';
|
||||
import { HeatmapSourceMode, PanelOptions } from './models.gen';
|
||||
|
||||
export const enum BucketLayout {
|
||||
@@ -59,8 +55,11 @@ export function prepareHeatmapData(
|
||||
// detect a frame-per-bucket heatmap frame
|
||||
// TODO: improve heuristic? infer from fields[1].labels.le === '+Inf' ?
|
||||
if (frames[0].meta?.custom?.resultType === 'matrix' && frames.some((f) => f.name?.startsWith('+Inf'))) {
|
||||
// already done by the Prometheus datasource frontend
|
||||
//frames = prepBucketFrames(frames);
|
||||
|
||||
return {
|
||||
yAxisValues: frames.map((f) => f.name ?? null).sort(sortAscStrInf),
|
||||
yAxisValues: frames.map((f) => f.name ?? null),
|
||||
...getHeatmapData(createHeatmapFromBuckets(frames), theme),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user