diff --git a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts index 9c4805bdf9d..add09f6b3ba 100644 --- a/public/app/plugins/panel/heatmap/heatmap_data_converter.ts +++ b/public/app/plugins/panel/heatmap/heatmap_data_converter.ts @@ -1,6 +1,7 @@ /// import _ from 'lodash'; +import TimeSeries from 'app/core/time_series2'; let VALUE_INDEX = 0; let TIME_INDEX = 1; @@ -15,6 +16,48 @@ interface YBucket { values: number[]; } +function elasticHistogramToHeatmap(series) { + let seriesBuckets = _.map(series, (s: TimeSeries) => { + return convertEsSeriesToHeatmap(s); + }); + let buckets = mergeBuckets(seriesBuckets); + return buckets; +} + +function convertEsSeriesToHeatmap(series: TimeSeries, saveZeroCounts = false) { + let xBuckets: XBucket[] = []; + + _.forEach(series.datapoints, point => { + let bound = series.alias; + let count = point[VALUE_INDEX]; + let values = new Array(count); + values.fill(Number(bound)); + + let valueBuckets = {}; + valueBuckets[bound] = { + y: Number(bound), + values: values + }; + + let xBucket: XBucket = { + x: point[TIME_INDEX], + buckets: valueBuckets + }; + + // Don't push buckets vith 0 count until saveZeroCounts flag is set + if (count !== 0 || (count === 0 && saveZeroCounts)) { + xBuckets.push(xBucket); + } + }); + + let heatmap: any = {}; + _.forEach(xBuckets, (bucket: XBucket) => { + heatmap[bucket.x] = bucket; + }); + + return heatmap; +} + /** * Convert set of time series into heatmap buckets * @return {Object} Heatmap object: @@ -321,20 +364,29 @@ function mergeBuckets(seriesBuckets) { } else { _.forEach(seriesBucket, (xBucket, xBound) => { if (mergedBuckets[xBound]) { - mergedBuckets[xBound].points = xBucket.points.concat(mergedBuckets[xBound].points); - mergedBuckets[xBound].values = xBucket.values.concat(mergedBuckets[xBound].values); + if (xBucket.points) { + mergedBuckets[xBound].points = xBucket.points.concat(mergedBuckets[xBound].points); + } + if (xBucket.values) { + mergedBuckets[xBound].values = xBucket.values.concat(mergedBuckets[xBound].values); + } _.forEach(xBucket.buckets, (yBucket, yBound) => { let bucket = mergedBuckets[xBound].buckets[yBound]; if (bucket && bucket.values) { mergedBuckets[xBound].buckets[yBound].values = bucket.values.concat(yBucket.values); - mergedBuckets[xBound].buckets[yBound].points = bucket.points.concat(yBucket.points); + + if (bucket.points) { + mergedBuckets[xBound].buckets[yBound].points = bucket.points.concat(yBucket.points); + } } else { mergedBuckets[xBound].buckets[yBound] = yBucket; } let points = mergedBuckets[xBound].buckets[yBound].points; - mergedBuckets[xBound].buckets[yBound].seriesStat = getSeriesStat(points); + if (points) { + mergedBuckets[xBound].buckets[yBound].seriesStat = getSeriesStat(points); + } }); } else { mergedBuckets[xBound] = xBucket; @@ -363,10 +415,15 @@ function logp(value, base) { * @param objB */ function isHeatmapDataEqual(objA: any, objB: any): boolean { - let is_eql = true; + let is_eql = !emptyXOR(objA, objB); _.forEach(objA, (xBucket: XBucket, x) => { if (objB[x]) { + if (emptyXOR(xBucket.buckets, objB[x].buckets)) { + is_eql = false; + return false; + } + _.forEach(xBucket.buckets, (yBucket: YBucket, y) => { if (objB[x].buckets && objB[x].buckets[y]) { if (objB[x].buckets[y].values) { @@ -396,8 +453,13 @@ function isHeatmapDataEqual(objA: any, objB: any): boolean { return is_eql; } +function emptyXOR(foo: any, bar: any): boolean { + return (_.isEmpty(foo) || _.isEmpty(bar)) && !(_.isEmpty(foo) && _.isEmpty(bar)); +} + export { convertToHeatMap, + elasticHistogramToHeatmap, convertToCards, removeZeroBuckets, mergeZeroBuckets, diff --git a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts index 389b40b38bb..2f37fd04dd2 100644 --- a/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts +++ b/public/app/plugins/panel/heatmap/specs/heatmap_data_converter_specs.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import { describe, beforeEach, it, sinon, expect, angularMocks } from '../../../../../test/lib/common'; import TimeSeries from 'app/core/time_series2'; -import { convertToHeatMap, isHeatmapDataEqual } from '../heatmap_data_converter'; +import { convertToHeatMap, elasticHistogramToHeatmap, isHeatmapDataEqual } from '../heatmap_data_converter'; describe('isHeatmapDataEqual', () => { let ctx: any = {}; @@ -40,10 +40,27 @@ describe('isHeatmapDataEqual', () => { let heatmapE = _.cloneDeep(ctx.heatmapA); heatmapE['1422774000000'].buckets['1'].values = [1, 1.6]; + let empty = {}; + let emptyValues = _.cloneDeep(ctx.heatmapA); + emptyValues['1422774000000'].buckets['1'].values = []; + expect(isHeatmapDataEqual(ctx.heatmapA, ctx.heatmapB)).to.be(true); + expect(isHeatmapDataEqual(ctx.heatmapB, ctx.heatmapA)).to.be(true); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapC)).to.be(true); + expect(isHeatmapDataEqual(heatmapC, ctx.heatmapA)).to.be(true); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapD)).to.be(false); + expect(isHeatmapDataEqual(heatmapD, ctx.heatmapA)).to.be(false); + expect(isHeatmapDataEqual(ctx.heatmapA, heatmapE)).to.be(false); + expect(isHeatmapDataEqual(heatmapE, ctx.heatmapA)).to.be(false); + + expect(isHeatmapDataEqual(empty, ctx.heatmapA)).to.be(false); + expect(isHeatmapDataEqual(ctx.heatmapA, empty)).to.be(false); + + expect(isHeatmapDataEqual(emptyValues, ctx.heatmapA)).to.be(false); + expect(isHeatmapDataEqual(ctx.heatmapA, emptyValues)).to.be(false); }); }); @@ -123,3 +140,53 @@ describe('HeatmapDataConverter', () => { }); }); }); + +describe('ES Histogram converter', () => { + let ctx: any = {}; + + beforeEach(() => { + ctx.series = []; + ctx.series.push(new TimeSeries({ + datapoints: [[1, 1422774000000], [0, 1422774060000]], + alias: '1', label: '1' + })); + ctx.series.push(new TimeSeries({ + datapoints: [[1, 1422774000000], [3, 1422774060000]], + alias: '2', label: '2' + })); + ctx.series.push(new TimeSeries({ + datapoints: [[0, 1422774000000], [1, 1422774060000]], + alias: '3', label: '3' + })); + }); + + describe('when converting ES histogram', () => { + + beforeEach(() => { + }); + + it('should build proper heatmap data', () => { + let expectedHeatmap = { + '1422774000000': { + x: 1422774000000, + buckets: { + '1': { y: 1, values: [1] }, + '2': { y: 2, values: [2] } + } + }, + '1422774060000': { + x: 1422774060000, + buckets: { + '2': { y: 2, values: [2, 2, 2] }, + '3': { y: 3, values: [3] } + } + }, + }; + + let heatmap = elasticHistogramToHeatmap(ctx.series); + console.log(heatmap); + expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).to.be(true); + }); + }); +}); +