diff --git a/ui/spec/utils/timeSeriesToDygraphSpec.js b/ui/spec/utils/timeSeriesToDygraphSpec.js index c071889c82..85da96160b 100644 --- a/ui/spec/utils/timeSeriesToDygraphSpec.js +++ b/ui/spec/utils/timeSeriesToDygraphSpec.js @@ -16,6 +16,10 @@ describe('timeSeriesToDygraph', () => { "name":"m1", "columns": ["time","f1"], "values": [[1000, 1],[2000, 2]], + "tags": { + tk1: "tv1", + tk2: "tv2", + }, }, ] }, @@ -25,6 +29,9 @@ describe('timeSeriesToDygraph', () => { "name":"m1", "columns": ["time","f2"], "values": [[2000, 3],[4000, 4]], + "tags": { + tk3: "tv3", + }, }, ] }, @@ -38,8 +45,8 @@ describe('timeSeriesToDygraph', () => { const expected = { labels: [ 'time', - `m1.f1`, - `m1.f2`, + `m1.f1[tk1=tv1][tk2=tv2]`, + `m1.f2[tk3=tv3]`, ], timeSeries: [ [new Date(1000), 1, null], @@ -47,11 +54,11 @@ describe('timeSeriesToDygraph', () => { [new Date(4000), null, 4], ], dygraphSeries: { - 'm1.f1': { + 'm1.f1[tk1=tv1][tk2=tv2]': { axis: 'y', strokeWidth, }, - 'm1.f2': { + 'm1.f2[tk3=tv3]': { axis: 'y', strokeWidth, }, @@ -147,18 +154,18 @@ describe('timeSeriesToDygraph', () => { const actual = timeSeriesToDygraph(influxResponse); const expected = { - 'm1.f1': { - axis: 'y', - strokeWidth, - }, - 'm1.f2': { - axis: 'y', - strokeWidth, - }, - 'm3.f3': { - axis: 'y2', - strokeWidth, - }, + 'm1.f1': { + axis: 'y', + strokeWidth, + }, + 'm1.f2': { + axis: 'y', + strokeWidth, + }, + 'm3.f3': { + axis: 'y2', + strokeWidth, + }, }; expect(actual.dygraphSeries).to.deep.equal(expected); @@ -206,19 +213,19 @@ describe('timeSeriesToDygraph', () => { labels: [ 'time', `m1.f1`, - `m1.f1-1`, + `m1.f1`, ], timeSeries: [ [new Date(1000), 1, null], [new Date(2000), 2, 3], - [new Date(4000), 4, null], + [new Date(4000), null, 4], ], dygraphSeries: { 'm1.f1': { axis: 'y', strokeWidth, }, - 'm1.f1-1': { + 'm1.f1': { axis: 'y2', strokeWidth, }, @@ -322,7 +329,7 @@ describe('timeSeriesToDygraph', () => { expect(dygraphSeries["m2.f2"].strokeWidth).to.be.above(dygraphSeries["m1.f1"].strokeWidth); }); - it('parses a raw InfluxDB response into a dygraph friendly data format', () => { + it('parses labels alphabetically with the correct field values for multiple series', () => { const influxResponse = [ { "response": @@ -332,8 +339,22 @@ describe('timeSeriesToDygraph', () => { "series": [ { "name":"mb", - "columns": ["time","f1"], - "values": [[1000, 1],[2000, 2]], + "columns": ["time","fa"], + "values": [ + [1000, 200], + [2000, 300], + [4000, 400], + ], + }, + { + "name":"mc", + "columns": ["time","fa"], + "values": [ + [1000, 400], + [2000, 600], + [3000, 800], + [5000, 1000], + ], }, ] }, @@ -341,26 +362,12 @@ describe('timeSeriesToDygraph', () => { "series": [ { "name":"ma", - "columns": ["time","f1"], - "values": [[1000, 1],[2000, 2]], - }, - ] - }, - { - "series": [ - { - "name":"mc", - "columns": ["time","f2"], - "values": [[2000, 3],[4000, 4]], - }, - ] - }, - { - "series": [ - { - "name":"mc", - "columns": ["time","f1"], - "values": [[2000, 3],[4000, 4]], + "columns": ["time","fa","fc","fb"], + "values": [ + [1000, 20, 10, 10], + [2000, 30, 15, 9], + [3000, 40, 20, 8], + ], }, ] }, @@ -371,14 +378,25 @@ describe('timeSeriesToDygraph', () => { const actual = timeSeriesToDygraph(influxResponse); - const expected = [ - 'time', - `ma.f1`, - `mb.f1`, - `mc.f1`, - `mc.f2`, - ]; + const expected = { + labels: [ + 'time', + `ma.fa`, + `ma.fb`, + `ma.fc`, + `mb.fa`, + `mc.fa`, + ], + timeSeries: [ + [new Date(1000), 20, 10, 10, 200, 400], + [new Date(2000), 30, 9, 15, 300, 600], + [new Date(3000), 40, 8, 20, null, 800], + [new Date(4000), null, null, null, 400, null], + [new Date(5000), null, null, null, null, 1000], + ], + }; - expect(actual.labels).to.deep.equal(expected); + expect(actual.labels).to.deep.equal(expected.labels); + expect(actual.timeSeries).to.deep.equal(expected.timeSeries); }); }); diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js index a5815c202a..5d275f1e81 100644 --- a/ui/src/utils/timeSeriesToDygraph.js +++ b/ui/src/utils/timeSeriesToDygraph.js @@ -1,181 +1,113 @@ +import _ from 'lodash'; import {STROKE_WIDTH} from 'src/shared/constants'; /** * Accepts an array of raw influxdb responses and returns a format * that Dygraph understands. */ -// activeQueryIndex is an optional argument that indicated which query's series -// we want highlighted. +// activeQueryIndex is an optional argument that indicated which query's series we want highlighted. export default function timeSeriesToDygraph(raw = [], activeQueryIndex, isInDataExplorer) { - const labels = []; // all of the effective field names (i.e. .) - const fieldToIndex = {}; // see parseSeries - const dates = {}; // map of date as string to date value to minimize string coercion - const dygraphSeries = {}; // dygraphSeries is a graph legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'}; + // collect results from each influx response + const results = raw.reduce((acc, rawResponse, responseIndex) => { + const responses = _.get(rawResponse, 'response.results', []); + const indexedResponses = responses.map((response) => ({...response, responseIndex})); + return [...acc, ...indexedResponses]; + }, []); - /** - * dateToFieldValue will look like: - * - * { - * Date1: { - * effectiveFieldName_1: ValueForField1AtDate1, - * effectiveFieldName_2: ValueForField2AtDate1, - * ... - * }, - * Date2: { - * effectiveFieldName_1: ValueForField1AtDate2, - * effectiveFieldName_2: ValueForField2AtDate2, - * ... - * } - * } - */ - const dateToFieldValue = {}; + // collect each series + const serieses = results.reduce((acc, {series = [], responseIndex}, index) => { + return [...acc, ...series.map((item) => ({...item, responseIndex, index}))]; + }, []); - raw.forEach(({response}, queryIndex) => { - // If a response is an empty result set or a query returned an error - // from InfluxDB, don't try and parse. - if (response.results.length) { - if (isEmpty(response) || hasError(response)) { - return; - } - } + // convert series into cells with rows and columns + const cells = serieses.reduce((acc, {name, columns, values, index, responseIndex, tags = {}}) => { + const rows = values.map((vals) => ({ + name, + columns, + vals, + index, + })); - /** - * response looks like: - * { - * results: [ - * { series: [...] }, - * { series: [...] }, - * ] - * } - */ - response.results.forEach(parseResult); + // tagSet is each tag key and value for a series + const tagSet = Object.keys(tags).map((tag) => `[${tag}=${tags[tag]}]`).sort().join(''); - function parseResult(s) { - /* - * s looks like: - * { - * series: [ - * { - * name: "", - * columns: ["time", "", "", ...], - * values: [