Commit 8eee0d57 by Torkel Ödegaard

Merge branch 'influxdb-09-fields-enhancements'

parents 3f2c1de2 150fd869
...@@ -48,7 +48,6 @@ in the main Time Picker in the upper right, but they can also have relative time ...@@ -48,7 +48,6 @@ in the main Time Picker in the upper right, but they can also have relative time
2. To edit the graph you click on the graph title to open the panel menu, then `Edit`. 2. To edit the graph you click on the graph title to open the panel menu, then `Edit`.
3. This should take you to the `Metrics` tab. In this tab you should see the editor for your default data source. 3. This should take you to the `Metrics` tab. In this tab you should see the editor for your default data source.
## Drag-and-Drop panels ## Drag-and-Drop panels
You can Drag-and-Drop Panels within and between Rows. Click and hold the Panel title, and drag it to its new location. You can Drag-and-Drop Panels within and between Rows. Click and hold the Panel title, and drag it to its new location.
......
...@@ -11,22 +11,36 @@ function (angular, _, $) { ...@@ -11,22 +11,36 @@ function (angular, _, $) {
.directive('influxdbFuncEditor', function($compile) { .directive('influxdbFuncEditor', function($compile) {
var funcSpanTemplate = '<a gf-dropdown="functionMenu" class="dropdown-toggle" ' + var funcSpanTemplate = '<a gf-dropdown="functionMenu" class="dropdown-toggle" ' +
'data-toggle="dropdown">{{target.function}}</a><span>(</span>'; 'data-toggle="dropdown">{{field.func}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + var paramTemplate = '<input type="text" style="display:none"' +
' class="input-mini tight-form-func-param"></input>'; ' class="input-mini tight-form-func-param"></input>';
var functionList = [
'count', 'mean', 'sum', 'min', 'max', 'mode', 'distinct', 'median',
'derivative', 'stddev', 'first', 'last', 'difference'
];
var functionMenu = _.map(functionList, function(func) {
return { text: func, click: "changeFunction('" + func + "');" };
});
return { return {
restrict: 'A', restrict: 'A',
scope: {
field: "=",
getFields: "&",
onChange: "&",
},
link: function postLink($scope, elem) { link: function postLink($scope, elem) {
var $funcLink = $(funcSpanTemplate); var $funcLink = $(funcSpanTemplate);
$scope.functionMenu = _.map($scope.functions, function(func) { $scope.functionMenu = functionMenu;
return {
text: func, $scope.changeFunction = function(func) {
click: "changeFunction('" + func + "');" $scope.field.func = func;
}; $scope.onChange();
}); };
function clickFuncParam() { function clickFuncParam() {
/*jshint validthis:true */ /*jshint validthis:true */
...@@ -34,7 +48,7 @@ function (angular, _, $) { ...@@ -34,7 +48,7 @@ function (angular, _, $) {
var $link = $(this); var $link = $(this);
var $input = $link.next(); var $input = $link.next();
$input.val($scope.target.column); $input.val($scope.field.name);
$input.css('width', ($link.width() + 16) + 'px'); $input.css('width', ($link.width() + 16) + 'px');
$link.hide(); $link.hide();
...@@ -58,8 +72,8 @@ function (angular, _, $) { ...@@ -58,8 +72,8 @@ function (angular, _, $) {
if ($input.val() !== '') { if ($input.val() !== '') {
$link.text($input.val()); $link.text($input.val());
$scope.target.column = $input.val(); $scope.field.name = $input.val();
$scope.$apply($scope.get_data); $scope.$apply($scope.onChange());
} }
$input.hide(); $input.hide();
...@@ -83,8 +97,10 @@ function (angular, _, $) { ...@@ -83,8 +97,10 @@ function (angular, _, $) {
$input.attr('data-provide', 'typeahead'); $input.attr('data-provide', 'typeahead');
$input.typeahead({ $input.typeahead({
source: function () { source: function (query, callback) {
return $scope.listColumns.apply(null, arguments); return $scope.getFields().then(function(results) {
callback(results);
});
}, },
minLength: 0, minLength: 0,
items: 20, items: 20,
...@@ -108,7 +124,7 @@ function (angular, _, $) { ...@@ -108,7 +124,7 @@ function (angular, _, $) {
function addElementsAndCompile() { function addElementsAndCompile() {
$funcLink.appendTo(elem); $funcLink.appendTo(elem);
var $paramLink = $('<a ng-click="" class="graphite-func-param-link">value</a>'); var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + $scope.field.name + '</a>');
var $input = $(paramTemplate); var $input = $(paramTemplate);
$paramLink.appendTo(elem); $paramLink.appendTo(elem);
......
...@@ -65,10 +65,12 @@ ...@@ -65,10 +65,12 @@
<li class="tight-form-item query-keyword" style="width: 75px;"> <li class="tight-form-item query-keyword" style="width: 75px;">
SELECT SELECT
</li> </li>
<li class="dropdown tight-form-item"> <li class="dropdown" ng-repeat="field in target.fields">
<a gf-dropdown="functionMenu" class="dropdown-toggle" data-toggle="dropdown"> <span influxdb-func-editor field="field" get-fields="getFields()" on-change="fieldChanged(field)" class="tight-form-item">
{{target.function}}<span>(value)</span> </span>
</a> </li>
<li>
<metric-segment segment="addFieldSegment" get-alt-segments="getFieldSegments()" on-value-changed="addField()"></metric-segment>
</li> </li>
</ul> </ul>
...@@ -114,8 +116,8 @@ ...@@ -114,8 +116,8 @@
<i class="fa fa-eye invisible"></i> <i class="fa fa-eye invisible"></i>
</li> </li>
<li class="tight-form-item" > <li class="tight-form-item query-keyword">
<span class="query-keyword">GROUP BY</span> GROUP BY
</li> </li>
<li class="tight-form-item"> <li class="tight-form-item">
......
...@@ -32,12 +32,15 @@ function (_) { ...@@ -32,12 +32,15 @@ function (_) {
if (type === 'TAG_KEYS') { if (type === 'TAG_KEYS') {
query = 'SHOW TAG KEYS'; query = 'SHOW TAG KEYS';
measurement= this.target.measurement; measurement = this.target.measurement;
} else if (type === 'TAG_VALUES') { } else if (type === 'TAG_VALUES') {
query = 'SHOW TAG VALUES'; query = 'SHOW TAG VALUES';
measurement= this.target.measurement; measurement = this.target.measurement;
} else if (type === 'MEASUREMENTS') { } else if (type === 'MEASUREMENTS') {
query = 'SHOW MEASUREMENTS'; query = 'SHOW MEASUREMENTS';
} else if (type === 'FIELDS') {
query = 'SHOW FIELD KEYS FROM "' + this.target.measurement + '"';
return query;
} }
if (measurement) { if (measurement) {
...@@ -73,15 +76,25 @@ function (_) { ...@@ -73,15 +76,25 @@ function (_) {
throw "Metric measurement is missing"; throw "Metric measurement is missing";
} }
if (!target.fields) {
target.fields = [{name: 'value', func: target.function || 'mean'}];
}
var query = 'SELECT '; var query = 'SELECT ';
var measurement = target.measurement; var i;
var aggregationFunc = target.function || 'mean'; for (i = 0; i < target.fields.length; i++) {
var field = target.fields[i];
if (i > 0) {
query += ', ';
}
query += field.func + '(' + field.name + ')';
}
var measurement = target.measurement;
if (!measurement.match('^/.*/') && !measurement.match(/^merge\(.*\)/)) { if (!measurement.match('^/.*/') && !measurement.match(/^merge\(.*\)/)) {
measurement = '"' + measurement+ '"'; measurement = '"' + measurement+ '"';
} }
query += aggregationFunc + '(value)';
query += ' FROM ' + measurement + ' WHERE '; query += ' FROM ' + measurement + ' WHERE ';
var conditions = _.map(target.tags, function(tag, index) { var conditions = _.map(target.tags, function(tag, index) {
return renderTagCondition(tag, index); return renderTagCondition(tag, index);
......
...@@ -10,20 +10,14 @@ function (angular, _, InfluxQueryBuilder) { ...@@ -10,20 +10,14 @@ function (angular, _, InfluxQueryBuilder) {
module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) { module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) {
$scope.functionList = [
'count', 'mean', 'sum', 'min', 'max', 'mode', 'distinct', 'median',
'derivative', 'stddev', 'first', 'last', 'difference'
];
$scope.functionMenu = _.map($scope.functionList, function(func) {
return { text: func, click: "changeFunction('" + func + "');" };
});
$scope.init = function() { $scope.init = function() {
var target = $scope.target; var target = $scope.target;
target.function = target.function || 'mean';
target.tags = target.tags || []; target.tags = target.tags || [];
target.groupByTags = target.groupByTags || []; target.groupByTags = target.groupByTags || [];
target.fields = target.fields || [{
name: 'value',
func: target.function || 'mean'
}];
$scope.queryBuilder = new InfluxQueryBuilder(target); $scope.queryBuilder = new InfluxQueryBuilder(target);
...@@ -33,6 +27,8 @@ function (angular, _, InfluxQueryBuilder) { ...@@ -33,6 +27,8 @@ function (angular, _, InfluxQueryBuilder) {
$scope.measurementSegment = new MetricSegment(target.measurement); $scope.measurementSegment = new MetricSegment(target.measurement);
} }
$scope.addFieldSegment = MetricSegment.newPlusButton();
$scope.tagSegments = []; $scope.tagSegments = [];
_.each(target.tags, function(tag) { _.each(target.tags, function(tag) {
if (tag.condition) { if (tag.condition) {
...@@ -94,6 +90,18 @@ function (angular, _, InfluxQueryBuilder) { ...@@ -94,6 +90,18 @@ function (angular, _, InfluxQueryBuilder) {
$scope.$parent.get_data(); $scope.$parent.get_data();
}; };
$scope.getFields = function() {
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
return $scope.datasource.metricFindQuery(fieldsQuery)
.then(function(results) {
var values = _.pluck(results, 'text');
if ($scope.target.fields.length > 1) {
values.splice(0, 0, "-- remove from select --");
}
return values;
});
};
$scope.toggleQueryMode = function () { $scope.toggleQueryMode = function () {
$scope.target.rawQuery = !$scope.target.rawQuery; $scope.target.rawQuery = !$scope.target.rawQuery;
}; };
...@@ -159,6 +167,25 @@ function (angular, _, InfluxQueryBuilder) { ...@@ -159,6 +167,25 @@ function (angular, _, InfluxQueryBuilder) {
.then(null, $scope.handleQueryError); .then(null, $scope.handleQueryError);
}; };
$scope.getFieldSegments = function() {
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
return $scope.datasource.metricFindQuery(fieldsQuery)
.then($scope.transformToSegments)
.then(null, $scope.handleQueryError);
};
$scope.addField = function() {
$scope.target.fields.push({name: $scope.addFieldSegment.value, func: 'mean'});
_.extend($scope.addFieldSegment, MetricSegment.newPlusButton());
};
$scope.fieldChanged = function(field) {
if (field.name === '-- remove from select --') {
$scope.target.fields = _.without($scope.target.fields, field);
}
$scope.get_data();
};
$scope.getGroupByTagSegments = function(segment) { $scope.getGroupByTagSegments = function(segment) {
var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS'); var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
......
...@@ -38,6 +38,20 @@ define([ ...@@ -38,6 +38,20 @@ define([
}); });
}); });
describe('series with multiple fields', function() {
var builder = new InfluxQueryBuilder({
measurement: 'cpu',
tags: [],
fields: [{ name: 'tx_in', func: 'sum' }, { name: 'tx_out', func: 'mean' }]
});
var query = builder.build();
it('should generate correct query', function() {
expect(query).to.be('SELECT sum(tx_in), mean(tx_out) FROM "cpu" WHERE $timeFilter GROUP BY time($interval) ORDER BY asc');
});
});
describe('series with multiple tags only', function() { describe('series with multiple tags only', function() {
var builder = new InfluxQueryBuilder({ var builder = new InfluxQueryBuilder({
measurement: 'cpu', measurement: 'cpu',
...@@ -130,6 +144,12 @@ define([ ...@@ -130,6 +144,12 @@ define([
expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" =~ /server.*/'); expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" =~ /server.*/');
}); });
it('should build show field query', function() {
var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: 'email'}]});
var query = builder.buildExploreQuery('FIELDS');
expect(query).to.be('SHOW FIELD KEYS FROM "cpu"');
});
}); });
}); });
......
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