Commit cf1f43dc by Torkel Ödegaard

feat(influxdb): support for table queries, closes #3409, #3219

parent 8eb3e48b
import {transformers} from './transformers';
export class TableModel {
class TableModel {
columns: any[];
rows: any[];
type: string;
constructor() {
this.columns = [];
this.rows = [];
this.type = 'table';
}
sort(options) {
......@@ -33,20 +34,6 @@ export class TableModel {
this.columns[options.col].desc = true;
}
}
static transform(data, panel) {
var model = new TableModel();
if (!data || data.length === 0) {
return model;
}
var transformer = transformers[panel.transform];
if (!transformer) {
throw {message: 'Transformer ' + panel.transformer + ' not found'};
}
transformer.transform(data, panel, model);
return model;
}
}
export = TableModel;
......@@ -5,7 +5,7 @@ import _ = require('lodash');
import moment = require('moment');
import PanelMeta = require('app/features/panel/panel_meta');
import {TableModel} from './table_model';
import {transformDataToTable} from './transformers';
export class TablePanelCtrl {
......@@ -104,7 +104,23 @@ export class TablePanelCtrl {
};
$scope.render = function() {
$scope.table = TableModel.transform($scope.dataRaw, $scope.panel);
// automatically correct transform mode
// based on data
if ($scope.dataRaw && $scope.dataRaw.length) {
if ($scope.dataRaw[0].type === 'table') {
$scope.panel.transform = 'table';
} else {
if ($scope.dataRaw[0].type === 'docs') {
$scope.panel.transform = 'json';
} else {
if ($scope.panel.transform === 'table' || $scope.panel.transform === 'json') {
$scope.panel.transform = 'timeseries_to_rows';
}
}
}
}
$scope.table = transformDataToTable($scope.dataRaw, $scope.panel);
$scope.table.sort($scope.panel.sort);
panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw);
};
......
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
import {TableModel} from '../table_model';
import TableModel = require('app/core/table_model');
import {TableRenderer} from '../renderer';
describe('when rendering table', () => {
......
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
import {TableModel} from '../table_model';
import {transformers} from '../transformers';
import {transformers, transformDataToTable} from '../transformers';
describe('when transforming time series table', () => {
var table;
......@@ -26,7 +25,7 @@ describe('when transforming time series table', () => {
};
beforeEach(() => {
table = TableModel.transform(timeSeries, panel);
table = transformDataToTable(timeSeries, panel);
});
it('should return 3 rows', () => {
......@@ -51,7 +50,7 @@ describe('when transforming time series table', () => {
};
beforeEach(() => {
table = TableModel.transform(timeSeries, panel);
table = transformDataToTable(timeSeries, panel);
});
it ('should return 3 columns', () => {
......@@ -80,7 +79,7 @@ describe('when transforming time series table', () => {
};
beforeEach(() => {
table = TableModel.transform(timeSeries, panel);
table = transformDataToTable(timeSeries, panel);
});
it('should return 2 rows', () => {
......@@ -133,7 +132,7 @@ describe('when transforming time series table', () => {
describe('transform', function() {
beforeEach(() => {
table = TableModel.transform(rawData, panel);
table = transformDataToTable(rawData, panel);
});
it ('should return 2 columns', () => {
......@@ -164,7 +163,7 @@ describe('when transforming time series table', () => {
];
beforeEach(() => {
table = TableModel.transform(rawData, panel);
table = transformDataToTable(rawData, panel);
});
it ('should return 4 columns', () => {
......
......@@ -4,6 +4,7 @@ import moment = require('moment');
import _ = require('lodash');
import flatten = require('app/core/utils/flatten');
import TimeSeries = require('app/core/time_series');
import TableModel = require('app/core/table_model');
var transformers = {};
......@@ -136,6 +137,27 @@ transformers['annotations'] = {
}
};
transformers['table'] = {
description: 'Table',
getColumns: function(data) {
if (!data || data.length === 0) {
return [];
}
},
transform: function(data, panel, model) {
if (!data || data.length === 0) {
return;
}
if (data[0].type !== 'table') {
throw {message: 'Query result is not in table format, try using another transform.'};
}
model.columns = data[0].columns;
model.rows = data[0].rows;
}
};
transformers['json'] = {
description: 'JSON Data',
getColumns: function(data) {
......@@ -197,4 +219,20 @@ transformers['json'] = {
}
};
export {transformers}
function transformDataToTable(data, panel) {
var model = new TableModel();
if (!data || data.length === 0) {
return model;
}
var transformer = transformers[panel.transform];
if (!transformer) {
throw {message: 'Transformer ' + panel.transformer + ' not found'};
}
transformer.transform(data, panel, model);
return model;
}
export {transformers, transformDataToTable}
......@@ -53,6 +53,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQuery) {
// replace templated variables
allQueries = templateSrv.replace(allQueries, options.scopedVars);
return this._seriesQuery(allQueries).then(function(data) {
if (!data || !data.results) {
return [];
......@@ -63,13 +64,26 @@ function (angular, _, dateMath, InfluxSeries, InfluxQuery) {
var result = data.results[i];
if (!result || !result.series) { continue; }
var alias = (queryTargets[i] || {}).alias;
var target = queryTargets[i];
var alias = target.alias;
if (alias) {
alias = templateSrv.replace(alias, options.scopedVars);
alias = templateSrv.replace(target.alias, options.scopedVars);
}
var targetSeries = new InfluxSeries({ series: data.results[i].series, alias: alias }).getTimeSeries();
for (y = 0; y < targetSeries.length; y++) {
seriesList.push(targetSeries[y]);
var influxSeries = new InfluxSeries({ series: data.results[i].series, alias: alias });
switch(target.resultFormat) {
case 'table': {
seriesList.push(influxSeries.getTable());
break;
}
default: {
var timeSeries = influxSeries.getTimeSeries();
for (y = 0; y < timeSeries.length; y++) {
seriesList.push(timeSeries[y]);
}
break;
}
}
}
......
......@@ -12,6 +12,8 @@ class InfluxQuery {
constructor(target) {
this.target = target;
target.dsType = 'influxdb';
target.resultFormat = target.resultFormat || 'time_series';
target.tags = target.tags || [];
target.groupBy = target.groupBy || [
{type: 'time', params: ['$interval']},
......
define([
'lodash',
'app/core/table_model',
],
function (_) {
function (_, TableModel) {
'use strict';
function InfluxSeries(options) {
......@@ -108,5 +109,44 @@ function (_) {
return list;
};
p.getTable = function() {
var table = new TableModel();
var self = this;
var i, j;
if (self.series.length === 0) {
return table;
}
_.each(self.series, function(series, seriesIndex) {
if (seriesIndex === 0) {
table.columns.push({text: 'Time', type: 'time'});
_.each(_.keys(series.tags), function(key) {
table.columns.push({text: key});
});
for (j = 1; j < series.columns.length; j++) {
table.columns.push({text: series.columns[j]});
}
}
if (series.values) {
for (i = 0; i < series.values.length; i++) {
var values = series.values[i];
if (series.tags) {
for (var key in series.tags) {
if (series.tags.hasOwnProperty(key)) {
values.splice(1, 0, series.tags[key]);
}
}
}
table.rows.push(values);
}
}
});
return table;
};
return InfluxSeries;
});
......@@ -103,6 +103,12 @@
<li>
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="get_data()">
</li>
<li class="tight-form-item">
Format as
</li>
<li>
<select class="input-small tight-form-input" style="width: 104px" ng-model="target.resultFormat" ng-options="f.value as f.text for f in resultFormats" ng-change="get_data()"></select>
</li>
</ul>
<div class="clearfix"></div>
</div>
......
......@@ -20,6 +20,11 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
$scope.queryModel = new InfluxQuery($scope.target);
$scope.queryBuilder = new InfluxQueryBuilder($scope.target);
$scope.groupBySegment = uiSegmentSrv.newPlusButton();
$scope.resultFormats = [
{text: 'Time series', value: 'time_series'},
{text: 'Table', value: 'table'},
{text: 'JSON field', value: 'json_field'},
];
if (!$scope.target.measurement) {
$scope.measurementSegment = uiSegmentSrv.newSelectMeasurement();
......
......@@ -186,5 +186,28 @@ describe('when generating timeseries from influxdb response', function() {
});
});
describe('given table response', function() {
var options = {
alias: '',
series: [
{
name: 'app.prod.server1.count',
tags: {},
columns: ['time', 'datacenter', 'value'],
values: [[1431946625000, 'America', 10], [1431946626000, 'EU', 12]]
}
]
};
it('should return table', function() {
var series = new InfluxSeries(options);
var table = series.getTable();
expect(table.type).to.be('table');
expect(table.columns.length).to.be(3);
expect(table.rows[0]).to.eql([1431946625000, 'America', 10]);;
});
});
});
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
import {TableModel} from '../table_model';
import TableModel = require('app/core/table_model');
describe('when sorting table desc', () => {
var table;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment