diff --git a/src/app/directives/graphiteSegment.js b/src/app/directives/graphiteSegment.js index 577bbf5d6b7..c8ad131e6c7 100644 --- a/src/app/directives/graphiteSegment.js +++ b/src/app/directives/graphiteSegment.js @@ -37,12 +37,14 @@ function (angular, app, _, $) { if (selected) { segment.value = selected.value; segment.html = selected.html; + segment.fake = false; segment.expandable = selected.expandable; } else { segment.value = value; segment.html = $sce.trustAsHtml(value); segment.expandable = true; + segment.fake = false; } $scope.segmentValueChanged(segment, $scope.$index); }); @@ -71,7 +73,7 @@ function (angular, app, _, $) { options = _.map($scope.altSegments, function(alt) { return alt.value; }); // add custom values - if (segment.value !== 'select metric' && _.indexOf(options, segment.value) === -1) { + if (!segment.fake && _.indexOf(options, segment.value) === -1) { options.unshift(segment.value); } diff --git a/src/app/plugins/datasource/graphite/queryCtrl.js b/src/app/plugins/datasource/graphite/queryCtrl.js index d878386d461..069fa815797 100644 --- a/src/app/plugins/datasource/graphite/queryCtrl.js +++ b/src/app/plugins/datasource/graphite/queryCtrl.js @@ -113,7 +113,7 @@ function (angular, _, config, gfunc, Parser) { function checkOtherSegments(fromIndex) { if (fromIndex === 0) { - $scope.segments.push(new MetricSegment('select metric')); + $scope.segments.push(MetricSegment.newSelectMetric()); return; } @@ -123,13 +123,13 @@ function (angular, _, config, gfunc, Parser) { if (segments.length === 0) { if (path !== '') { $scope.segments = $scope.segments.splice(0, fromIndex); - $scope.segments.push(new MetricSegment('select metric')); + $scope.segments.push(MetricSegment.newSelectMetric()); } return; } if (segments[0].expandable) { if ($scope.segments.length === fromIndex) { - $scope.segments.push(new MetricSegment('select metric')); + $scope.segments.push(MetricSegment.newSelectMetric()); } else { return checkOtherSegments(fromIndex + 1); @@ -238,7 +238,7 @@ function (angular, _, config, gfunc, Parser) { $scope.moveAliasFuncLast(); $scope.smartlyHandleNewAliasByNode(newFunc); - if ($scope.segments.length === 1 && $scope.segments[0].value === 'select metric') { + if ($scope.segments.length === 1 && $scope.segments[0].fake) { $scope.segments = []; } @@ -298,18 +298,17 @@ function (angular, _, config, gfunc, Parser) { return; } - if (_.isString(options)) { - this.value = options; - this.html = $sce.trustAsHtml(this.value); - return; - } - + this.fake = options.fake; this.value = options.value; this.type = options.type; this.expandable = options.expandable; this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); } + MetricSegment.newSelectMetric = function() { + return new MetricSegment({value: 'select metric', fake: true}); + }; + }); module.directive('focusMe', function($timeout, $parse) { diff --git a/src/app/plugins/datasource/influxdb/datasource.js b/src/app/plugins/datasource/influxdb/datasource.js index 26bacfe2c59..dbaf443affd 100644 --- a/src/app/plugins/datasource/influxdb/datasource.js +++ b/src/app/plugins/datasource/influxdb/datasource.js @@ -36,7 +36,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { var timeFilter = getTimeFilter(options); var promises = _.map(options.targets, function(target) { - if (target.hide || !((target.series && target.column) || target.query)) { + if (target.hide || !target.query) { return []; } @@ -73,40 +73,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { }); }; - InfluxDatasource.prototype.listColumns = function(seriesName) { - seriesName = templateSrv.replace(seriesName); - - if(!seriesName.match('^/.*/') && !seriesName.match(/^merge\(.*\)/)) { - seriesName = '"' + seriesName+ '"'; - } - - return this._seriesQuery('select * from ' + seriesName + ' limit 1').then(function(data) { - if (!data) { - return []; - } - return data[0].columns.map(function(item) { - return /^\w+$/.test(item) ? item : ('"' + item + '"'); - }); - }); - }; - - InfluxDatasource.prototype.listSeries = function(query) { - // wrap in regex - if (query && query.length > 0 && query[0] !== '/') { - query = '/' + query + '/'; - } - - return this._seriesQuery('SHOW MEASUREMENTS').then(function(data) { - if (!data || data.length === 0) { - return []; - } - return _.map(data[0].points, function(point) { - return point[1]; - }); - }); - }; - - InfluxDatasource.prototype.metricFindQuery = function (query) { + InfluxDatasource.prototype.metricFindQuery = function (query, queryType) { var interpolated; try { interpolated = templateSrv.replace(query); @@ -115,17 +82,30 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { return $q.reject(err); } - return this._seriesQuery(interpolated) - .then(function (results) { - if (!results || results.length === 0) { return []; } + console.log('metricFindQuery called with: ' + [query, queryType].join(', ')); - return _.map(results[0].points, function (metric) { - return { - text: metric[1], - expandable: false - }; - }); - }); + return this._seriesQuery(interpolated, queryType).then(function (results) { + if (!results || results.results.length === 0) { return []; } + + var influxResults = results.results[0]; + if (!influxResults.series) { + return []; + } + + console.log('metric find query response', results); + var series = influxResults.series[0]; + + switch (queryType) { + case 'MEASUREMENTS': + return _.map(series.values, function(value) { return { text: value[0], expandable: true }; }); + case 'TAG_KEYS': + var tagKeys = _.flatten(series.values); + return _.map(tagKeys, function(tagKey) { return { text: tagKey, expandable: true }; }); + case 'TAG_VALUES': + var tagValues = _.flatten(series.values); + return _.map(tagValues, function(tagValue) { return { text: tagValue, expandable: true }; }); + } + }); }; function retry(deferred, callback, delay) { @@ -143,9 +123,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) { } InfluxDatasource.prototype._seriesQuery = function(query) { - return this._influxRequest('GET', '/query', { - q: query, - }); + return this._influxRequest('GET', '/query', {q: query}); }; InfluxDatasource.prototype._influxRequest = function(method, url, data) { diff --git a/src/app/plugins/datasource/influxdb/partials/query.editor.html b/src/app/plugins/datasource/influxdb/partials/query.editor.html index d3d7d0ff95d..c91f13c57c6 100644 --- a/src/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/src/app/plugins/datasource/influxdb/partials/query.editor.html @@ -1,18 +1,47 @@
-
-
-
+ +
+
-
-
- -
-
- -
- -
-
-
-
- -
-
Alias patterns
-
    -
  • $s = series name
  • -
  • $g = group by
  • -
  • $[0-9] part of series name for series names seperated by dots.
  • -
-
- -
-
Stacking and fill
-
    -
  • When stacking is enabled it important that points align
  • -
  • If there are missing points for one series it can cause gaps or missing bars
  • -
  • You must use fill(0), and select a group by time low limit
  • -
  • Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds
  • -
  • This will insert zeros for series that are missing measurements and will make stacking work properly
  • -
-
- -
-
Group by time
-
    -
  • Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana
  • -
  • Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph
  • -
  • If you use fill(0) or fill(null) set a low limit for the auto group by time interval
  • -
  • The low limit can only be set in the group by time option below your queries
  • -
  • You set a low limit by adding a greater sign before the interval
  • -
  • Example: >60s if you write metrics to InfluxDB every 60 seconds
  • -
-
- -
- - diff --git a/src/app/plugins/datasource/influxdb/queryCtrl.js b/src/app/plugins/datasource/influxdb/queryCtrl.js index 608b5845d88..517ed79e1a1 100644 --- a/src/app/plugins/datasource/influxdb/queryCtrl.js +++ b/src/app/plugins/datasource/influxdb/queryCtrl.js @@ -7,93 +7,23 @@ function (angular, _) { var module = angular.module('grafana.controllers'); - var seriesList = null; - - module.controller('InfluxQueryCtrl', function($scope, $timeout) { + module.controller('InfluxQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) { $scope.init = function() { - var target = $scope.target; + $scope.segments = $scope.target.segments || []; - target.function = target.function || 'mean'; - target.column = target.column || 'value'; - - // backward compatible correction of schema - if (target.condition_value) { - target.condition = target.condition_key + ' ' + target.condition_op + ' ' + target.condition_value; - delete target.condition_key; - delete target.condition_op; - delete target.condition_value; - } - - if (target.groupby_field_add === false) { - target.groupby_field = ''; - delete target.groupby_field_add; - } - - $scope.rawQuery = true; - - $scope.functions = [ + $scope.functionsSelect = [ 'count', 'mean', 'sum', 'min', 'max', 'mode', 'distinct', 'median', 'derivative', 'stddev', 'first', 'last', 'difference' ]; - $scope.operators = ['=', '=~', '>', '<', '!~', '<>']; - $scope.oldSeries = target.series; - $scope.$on('typeahead-updated', function() { - $timeout($scope.get_data); - }); + checkOtherSegments(0); }; - $scope.showQuery = function () { - $scope.target.rawQuery = true; - }; - - $scope.hideQuery = function () { - $scope.target.rawQuery = false; - }; - - // Cannot use typeahead and ng-change on blur at the same time - $scope.seriesBlur = function() { - if ($scope.oldSeries !== $scope.target.series) { - $scope.oldSeries = $scope.target.series; - $scope.columnList = null; - $scope.get_data(); - } - }; - - $scope.changeFunction = function(func) { - $scope.target.function = func; - $scope.get_data(); - }; - - // called outside of digest - $scope.listColumns = function(query, callback) { - if (!$scope.columnList) { - $scope.$apply(function() { - $scope.datasource.listColumns($scope.target.series).then(function(columns) { - $scope.columnList = columns; - callback(columns); - }); - }); - } - else { - return $scope.columnList; - } - }; - - $scope.listSeries = function(query, callback) { - if (query !== '') { - seriesList = []; - $scope.datasource.listSeries(query).then(function(series) { - seriesList = series; - callback(seriesList); - }); - } - else { - return seriesList; - } + $scope.toggleQueryMode = function () { + $scope.target.rawQuery = !$scope.target.rawQuery; }; $scope.moveMetricQuery = function(fromIndex, toIndex) { @@ -105,6 +35,127 @@ function (angular, _) { $scope.panel.targets.push(clone); }; + $scope.getAltSegments = function (index) { + $scope.altSegments = []; + + var measurement = $scope.segments[0].value; + var queryType, query; + if (index === 0) { + queryType = 'MEASUREMENTS'; + query = 'SHOW MEASUREMENTS'; + } else if (index % 2 === 1) { + queryType = 'TAG_KEYS'; + query = 'SHOW TAG KEYS FROM ' + measurement; + } else { + queryType = 'TAG_VALUES'; + query = "SHOW TAG VALUES FROM " + measurement + " WITH KEY = " + $scope.segments[$scope.segments.length - 2].value; + } + + console.log('getAltSegments: query' , query); + + return $scope.datasource.metricFindQuery(query, queryType).then(function(results) { + console.log('get alt segments: response', results); + $scope.altSegments = _.map(results, function(segment) { + return new MetricSegment({ value: segment.text, expandable: segment.expandable }); + }); + + _.each(templateSrv.variables, function(variable) { + $scope.altSegments.unshift(new MetricSegment({ + type: 'template', + value: '$' + variable.name, + expandable: true, + })); + }); + }, function(err) { + $scope.parserError = err.message || 'Failed to issue metric query'; + }); + }; + + $scope.segmentValueChanged = function (segment, segmentIndex) { + delete $scope.parserError; + + if (segment.expandable) { + return checkOtherSegments(segmentIndex + 1).then(function () { + setSegmentFocus(segmentIndex + 1); + $scope.targetChanged(); + }); + } + else { + $scope.segments = $scope.segments.splice(0, segmentIndex + 1); + } + + setSegmentFocus(segmentIndex + 1); + $scope.targetChanged(); + }; + + $scope.targetChanged = function() { + if ($scope.parserError) { + return; + } + + $scope.$parent.get_data(); + }; + + function checkOtherSegments(fromIndex) { + if (fromIndex === 0) { + $scope.segments.push(MetricSegment.newSelectMetric()); + return; + } + + if ($scope.segments.length === 0) { + throw('should always have a scope segment?'); + } + + if (_.last($scope.segments).fake) { + return $q.when([]); + } else if ($scope.segments.length % 2 === 1) { + $scope.segments.push(MetricSegment.newSelectTag()); + return $q.when([]); + } else { + $scope.segments.push(MetricSegment.newSelectTagValue()); + return $q.when([]); + } + } + + function setSegmentFocus(segmentIndex) { + _.each($scope.segments, function(segment, index) { + segment.focus = segmentIndex === index; + }); + } + + function MetricSegment(options) { + if (options === '*' || options.value === '*') { + this.value = '*'; + this.html = $sce.trustAsHtml(''); + this.expandable = true; + return; + } + + if (_.isString(options)) { + this.value = options; + this.html = $sce.trustAsHtml(this.value); + return; + } + + this.fake = options.fake; + this.value = options.value; + this.type = options.type; + this.expandable = options.expandable; + this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + } + + MetricSegment.newSelectMetric = function() { + return new MetricSegment({value: 'select metric', fake: true}); + }; + + MetricSegment.newSelectTag = function() { + return new MetricSegment({value: 'select tag', fake: true}); + }; + + MetricSegment.newSelectTagValue = function() { + return new MetricSegment({value: 'select tag value', fake: true}); + }; + }); });