Sfoglia il codice sorgente

support multiple histogram series

Mitsuhiro Tanda 8 anni fa
parent
commit
cf1be5fdfc

+ 11 - 6
public/app/plugins/panel/graph/data_processor.ts

@@ -29,12 +29,17 @@ export class DataProcessor {
         });
       }
       case 'histogram': {
-        let histogramDataList = [
-          {
-            target: 'count',
-            datapoints: _.concat([], _.flatten(_.map(options.dataList, 'datapoints'))),
-          },
-        ];
+        let histogramDataList;
+        if (this.panel.stack) {
+          histogramDataList = options.dataList;
+        } else {
+          histogramDataList = [
+            {
+              target: 'count',
+              datapoints: _.concat([], _.flatten(_.map(options.dataList, 'datapoints'))),
+            },
+          ];
+        }
         return histogramDataList.map((item, index) => {
           return this.timeSeriesHandler(item, index, options);
         });

+ 12 - 6
public/app/plugins/panel/graph/graph.ts

@@ -17,7 +17,7 @@ import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
 import GraphTooltip from './graph_tooltip';
 import { ThresholdManager } from './threshold_manager';
 import { EventManager } from 'app/features/annotations/all';
-import { convertValuesToHistogram, getSeriesValues } from './histogram';
+import { convertToHistogramData } from './histogram';
 import config from 'app/core/config';
 
 /** @ngInject **/
@@ -236,15 +236,15 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
           }
           case 'histogram': {
             let bucketSize: number;
-            let values = getSeriesValues(data);
 
-            if (data.length && values.length) {
+            if (data.length) {
               let histMin = _.min(_.map(data, s => s.stats.min));
               let histMax = _.max(_.map(data, s => s.stats.max));
               let ticks = panel.xaxis.buckets || panelWidth / 50;
               bucketSize = tickStep(histMin, histMax, ticks);
-              let histogram = convertValuesToHistogram(values, bucketSize);
-              data[0].data = histogram;
+
+              data = convertToHistogramData(data, bucketSize, ctrl.hiddenSeries, panel.stack, histMin, histMax);
+
               options.series.bars.barWidth = bucketSize * 0.8;
             } else {
               bucketSize = 0;
@@ -413,7 +413,13 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
         let defaultTicks = panelWidth / 50;
 
         if (data.length && bucketSize) {
-          ticks = _.map(data[0].data, point => point[0]);
+          let tick_values = [];
+          for (let d of data) {
+            for (let point of d.data) {
+              tick_values[point[0]] = true;
+            }
+          }
+          ticks = Object.keys(tick_values).map(v => Number(v));
           min = _.min(ticks);
           max = _.max(ticks);
 

+ 39 - 6
public/app/plugins/panel/graph/histogram.ts

@@ -29,16 +29,22 @@ export function getSeriesValues(dataList: TimeSeries[]): number[] {
  * @param values
  * @param bucketSize
  */
-export function convertValuesToHistogram(values: number[], bucketSize: number): any[] {
+export function convertValuesToHistogram(values: number[], bucketSize: number, min: number, max: number): any[] {
   let histogram = {};
 
+  let minBound = getBucketBound(min, bucketSize);
+  let maxBound = getBucketBound(max, bucketSize);
+  let bound = minBound;
+  let n = 0;
+  while (bound <= maxBound) {
+    histogram[bound] = 0;
+    bound = minBound + bucketSize * n;
+    n++;
+  }
+
   for (let i = 0; i < values.length; i++) {
     let bound = getBucketBound(values[i], bucketSize);
-    if (histogram[bound]) {
-      histogram[bound] = histogram[bound] + 1;
-    } else {
-      histogram[bound] = 1;
-    }
+    histogram[bound] = histogram[bound] + 1;
   }
 
   let histogam_series = _.map(histogram, (count, bound) => {
@@ -49,6 +55,33 @@ export function convertValuesToHistogram(values: number[], bucketSize: number):
   return _.sortBy(histogam_series, point => point[0]);
 }
 
+/**
+ * Convert series into array of histogram data.
+ * @param data Array of series
+ * @param bucketSize
+ * @param stack
+ */
+export function convertToHistogramData(
+  data: any,
+  bucketSize: number,
+  hiddenSeries: any,
+  stack = false,
+  min: number,
+  max: number
+): any[] {
+  return data.map(series => {
+    let values = getSeriesValues([series]);
+    series.histogram = true;
+    if (!hiddenSeries[series.alias]) {
+      let histogram = convertValuesToHistogram(values, bucketSize, min, max);
+      series.data = histogram;
+    } else {
+      series.data = [];
+    }
+    return series;
+  });
+}
+
 function getBucketBound(value: number, bucketSize: number): number {
   return Math.floor(value / bucketSize) * bucketSize;
 }

+ 44 - 0
public/app/plugins/panel/graph/specs/graph_specs.ts

@@ -407,4 +407,48 @@ describe('grafanaGraph', function() {
     },
     10
   );
+
+  graphScenario('when graph is histogram, and enable stack', function(ctx) {
+    ctx.setup(function(ctrl, data) {
+      ctrl.panel.xaxis.mode = 'histogram';
+      ctrl.panel.stack = true;
+      ctrl.hiddenSeries = {};
+      data[0] = new TimeSeries({
+        datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+        alias: 'series1',
+      });
+      data[1] = new TimeSeries({
+        datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+        alias: 'series2',
+      });
+    });
+
+    it('should calculate correct histogram', function() {
+      expect(ctx.plotData[0].data[0][0]).to.be(100);
+      expect(ctx.plotData[0].data[0][1]).to.be(2);
+      expect(ctx.plotData[1].data[0][0]).to.be(100);
+      expect(ctx.plotData[1].data[0][1]).to.be(2);
+    });
+  });
+
+  graphScenario('when graph is histogram, and some series are hidden', function(ctx) {
+    ctx.setup(function(ctrl, data) {
+      ctrl.panel.xaxis.mode = 'histogram';
+      ctrl.panel.stack = false;
+      ctrl.hiddenSeries = { series2: true };
+      data[0] = new TimeSeries({
+        datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+        alias: 'series1',
+      });
+      data[1] = new TimeSeries({
+        datapoints: [[100, 1], [100, 2], [200, 3], [300, 4]],
+        alias: 'series2',
+      });
+    });
+
+    it('should calculate correct histogram', function() {
+      expect(ctx.plotData[0].data[0][0]).to.be(100);
+      expect(ctx.plotData[0].data[0][1]).to.be(2);
+    });
+  });
 });

+ 3 - 3
public/app/plugins/panel/graph/specs/histogram.jest.ts

@@ -13,15 +13,15 @@ describe('Graph Histogam Converter', function() {
       bucketSize = 10;
       let expected = [[0, 2], [10, 3], [20, 2]];
 
-      let histogram = convertValuesToHistogram(values, bucketSize);
+      let histogram = convertValuesToHistogram(values, bucketSize, 1, 29);
       expect(histogram).toMatchObject(expected);
     });
 
     it('Should not add empty buckets', () => {
       bucketSize = 5;
-      let expected = [[0, 2], [10, 2], [15, 1], [20, 1], [25, 1]];
+      let expected = [[0, 2], [5, 0], [10, 2], [15, 1], [20, 1], [25, 1]];
 
-      let histogram = convertValuesToHistogram(values, bucketSize);
+      let histogram = convertValuesToHistogram(values, bucketSize, 1, 29);
       expect(histogram).toMatchObject(expected);
     });
   });

+ 1 - 1
public/app/plugins/panel/graph/tab_display.html

@@ -71,7 +71,7 @@
 
 		<div class="section gf-form-group">
 			<h5 class="section-heading">Stacking & Null value</h5>
-			<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.render()">
+			<gf-form-switch class="gf-form" label="Stack" label-class="width-7" checked="ctrl.panel.stack" on-change="ctrl.refresh()">
 			</gf-form-switch>
 			<gf-form-switch class="gf-form" ng-show="ctrl.panel.stack" label="Percent" label-class="width-7" checked="ctrl.panel.percentage" on-change="ctrl.render()">
 			</gf-form-switch>