Use Data frames response format (#1099)

* Use data frames for numeric data

* Use data frames for text data

* Use data frames for IT services

* fix multiple series

* Convert to the wide format if possible

* Fix table format for text data

* Add refId to the data frames

* Align time series from Zabbix API

* Fill gaps with nulls

* Fix moving average functions

* Option for disabling data alignment

* remove unused logging

* Add labels to data frames

* Detect units

* Set min and max for if percent unit used

* Use value mapping from Zabbix

* Rename unitConverter -> convertZabbixUnit

* More units

* Add missing points in front of each series

* Fix handling table data

* fix db connector data frames handling

* fix it services data frames handling

* Detect all known grafana units

* Chore: remove unused logging

* Fix problems format

* Debug logging: show original units

* Add global option for disabling data alignment

* Add tooltip for the disableDataAlignment feature

* Add note about query options

* Functions for aligning timeseries on the backend
This commit is contained in:
Alexander Zobnin
2020-12-22 15:33:14 +03:00
committed by GitHub
parent ad378a81e1
commit 83618178f0
18 changed files with 700 additions and 91 deletions

View File

@@ -12,6 +12,7 @@
import _ from 'lodash';
import * as utils from './utils';
import * as c from './constants';
import { TimeSeriesPoints, TimeSeriesValue } from '@grafana/data';
const POINT_VALUE = 0;
const POINT_TIMESTAMP = 1;
@@ -62,6 +63,61 @@ function downsample(datapoints, time_to, ms_interval, func) {
return downsampledSeries.reverse();
}
/**
* Detects interval between data points and aligns time series. If there's no value in the interval, puts null as a value.
*/
export function align(datapoints: TimeSeriesPoints, interval?: number): TimeSeriesPoints {
if (interval) {
interval = detectSeriesInterval(datapoints);
}
if (interval <= 0 || datapoints.length <= 1) {
return datapoints;
}
const aligned_ts: TimeSeriesPoints = [];
let frame_ts = getPointTimeFrame(datapoints[0][POINT_TIMESTAMP], interval);
let point_frame_ts = frame_ts;
let point: TimeSeriesValue[];
for (let i = 0; i < datapoints.length; i++) {
point = datapoints[i];
point_frame_ts = getPointTimeFrame(point[POINT_TIMESTAMP], interval);
if (point_frame_ts > frame_ts) {
// Move frame window to next non-empty interval and fill empty by null
while (frame_ts < point_frame_ts) {
aligned_ts.push([null, frame_ts]);
frame_ts += interval;
}
}
aligned_ts.push([point[POINT_VALUE], point_frame_ts]);
frame_ts += interval;
}
return aligned_ts;
}
/**
* Detects interval between data points in milliseconds.
*/
function detectSeriesInterval(datapoints: TimeSeriesPoints): number {
if (datapoints.length < 2) {
return -1;
}
let deltas = [];
for (let i = 1; i < datapoints.length; i++) {
// Get deltas (in seconds)
const d = (datapoints[i][POINT_TIMESTAMP] - datapoints[i - 1][POINT_TIMESTAMP]) / 1000;
deltas.push(Math.round(d));
}
// Use 50th percentile (median) as an interval
deltas = _.sortBy(deltas);
const intervalSec = deltas[Math.floor(deltas.length * 0.5)];
return intervalSec * 1000;
}
/**
* Group points by given time interval
* datapoints: [[<value>, <unixtime>], ...]
@@ -255,7 +311,10 @@ function rate(datapoints) {
return newSeries;
}
function simpleMovingAverage(datapoints, n) {
function simpleMovingAverage(datapoints: TimeSeriesPoints, n: number): TimeSeriesPoints {
// It's not possible to calculate MA if n greater than number of points
n = Math.min(n, datapoints.length);
const sma = [];
let w_sum;
let w_avg = null;
@@ -298,7 +357,10 @@ function simpleMovingAverage(datapoints, n) {
return sma;
}
function expMovingAverage(datapoints, n) {
function expMovingAverage(datapoints: TimeSeriesPoints, n: number): TimeSeriesPoints {
// It's not possible to calculate MA if n greater than number of points
n = Math.min(n, datapoints.length);
let ema = [datapoints[0]];
let ema_prev = datapoints[0][POINT_VALUE];
let ema_cur;
@@ -526,6 +588,7 @@ const exportedFunctions = {
PERCENTILE,
sortByTime,
flattenDatapoints,
align,
};
export default exportedFunctions;