Commit ae7093e0 by Torkel Ödegaard

feat(elasticsearch): small progress on new elasticsearch metric query capability

parent 9e1a9723
......@@ -19,17 +19,9 @@ function (angular, _) {
$scope.target.region = $scope.target.region || $scope.datasource.getDefaultRegion();
$scope.target.errors = validateTarget();
$scope.regionSegment = $scope.getSegmentForValue($scope.target.region, 'select region');
$scope.namespaceSegment = $scope.getSegmentForValue($scope.target.namespace, 'select namespace');
$scope.metricSegment = $scope.getSegmentForValue($scope.target.metricName, 'select metric');
};
$scope.getSegmentForValue = function(value, fallbackText) {
if (value) {
return uiSegmentSrv.newSegment(value);
} else {
return uiSegmentSrv.newSegment({value: fallbackText, fake: true});
}
$scope.regionSegment = uiSegmentSrv.getSegmentForValue($scope.target.region, 'select region');
$scope.namespaceSegment = uiSegmentSrv.getSegmentForValue($scope.target.namespace, 'select namespace');
$scope.metricSegment = uiSegmentSrv.getSegmentForValue($scope.target.metricName, 'select metric');
};
$scope.getRegions = function() {
......
......@@ -138,13 +138,21 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
var queryBuilder = new ElasticQueryBuilder;
var query = queryBuilder.build(options.targets);
query = query.replace(/\$interval/g, options.interval);
query = query.replace(/\$rangeFrom/g, options.range.from);
query = query.replace(/\$rangeTo/g, options.range.to);
query = query.replace(/\$rangeFrom/g, this.translateTime(options.range.from));
query = query.replace(/\$rangeTo/g, this.translateTime(options.range.to));
query = query.replace(/\$maxDataPoints/g, options.maxDataPoints);
query = templateSrv.replace(query, options.scopedVars);
return this._post('/_search?search_type=count', query).then(this._getTimeSeries);
};
ElasticDatasource.prototype.translateTime = function(date) {
if (_.isString(date)) {
return date;
}
return date.toJSON();
};
ElasticDatasource.prototype._getTimeSeries = function(results) {
var _aggregation2timeSeries = function(aggregation) {
var datapoints = aggregation.date_histogram.buckets.map(function(entry) {
......@@ -169,6 +177,26 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
return { data: data };
};
ElasticDatasource.prototype.metricFindQuery = function(query) {
var region;
var namespace;
var metricName;
var transformSuggestData = function(suggestData) {
return _.map(suggestData, function(v) {
return { text: v };
});
};
var d = $q.defer();
var regionQuery = query.match(/^region\(\)/);
if (regionQuery) {
d.resolve(transformSuggestData(this.performSuggestRegion()));
return d.promise;
}
};
return ElasticDatasource;
});
});
......@@ -37,32 +37,12 @@
</li>
</ul>
<input type="text" class="tight-form-clear-input" style="width: 80%" ng-model="target.query" focus-me="target.rawQuery" spellcheck='false' ng-model-onblur ng-change="get_data()" ng-show="target.rawQuery"/>
<ul class="tight-form-list" role="menu" ng-hide="target.rawQuery">
<li class="tight-form-item query-keyword" style="width: 75px;">
Function
</li>
<li class="dropdown tight-form-item">
<a gf-dropdown="functionMenu" class="dropdown-toggle" data-toggle="dropdown">
{{target.function}}<span>(value)</span>
</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-hide="target.rawQuery">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-eye invisible"></i>
</li>
<li class="tight-form-item query-keyword" style="width: 75px;">
Key Field
<li class="tight-form-item query-keyword">
Timestamp field
</li>
<li>
<metric-segment segment="keyFieldSegment" on-value-changed="keyFieldChanged()"></metric-segment>
<metric-segment segment="timestampSegment" get-alt-segments="getFields()" on-value-changed="timestampChanged()"></metric-segment>
</li>
</ul>
......@@ -71,14 +51,11 @@
<div class="tight-form" ng-hide="target.rawQuery">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-eye invisible"></i>
</li>
<li class="tight-form-item query-keyword" style="width: 75px;">
Value Field
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
Value field
</li>
<li>
<metric-segment segment="valueFieldSegment" on-value-changed="valueFieldChanged()"></metric-segment>
<metric-segment segment="valueFieldSegment" get-alt-segments="getFields()" on-value-changed="valueFieldChanged()"></metric-segment>
</li>
</ul>
......@@ -87,11 +64,7 @@
<div class="tight-form" ng-hide="target.rawQuery">
<ul class="tight-form-list">
<li class="tight-form-item">
<i class="fa fa-eye invisible"></i>
</li>
<li class="tight-form-item query-keyword" style="width: 75px;">
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
Term
</li>
......
......@@ -3,52 +3,55 @@ define([
function () {
'use strict';
function ElasticQueryBuilder() {
}
function ElasticQueryBuilder() { }
ElasticQueryBuilder.prototype.build = function(targets) {
var query = {
"aggs": {},
"size": "$maxDataPoints"
};
var self = this;
targets.forEach(function(target) {
if (!target.hide) {
query["aggs"][target.termKey + "_" + target.termValue] = {
"filter": {
"and": [
self._buildRangeFilter(target),
self._buildTermFilter(target)
]
},
"aggs": {
targets.forEach(function(target, index) {
if (target.hide) {
return;
}
var esQuery = {
"filter": {
"and": [
self._buildRangeFilter(target)
]
},
"aggs": {
"date_histogram": {
"date_histogram": {
"date_histogram": {
"interval": target.interval || "$interval",
"field": target.keyField,
"min_doc_count": 0,
},
"aggs": {
"interval": target.interval || "$interval",
"field": target.timestampField,
"min_doc_count": 0,
},
"aggs": {
"stats": {
"stats": {
"stats": {
"field": target.valueField
}
"field": target.valueField
}
}
}
}
};
if (target.groupByField) {
query["aggs"][target.termKey + "_" + target.termValue]["aggs"] = {
"terms": {
"terms": {
"field": target.groupByField
},
"aggs": query["aggs"][target.termKey + "_" + target.termValue]["aggs"]
}
};
}
};
if (target.groupByField) {
query["aggs"][target.termKey + "_" + target.termValue]["aggs"] = {
"terms": {
"terms": {
"field": target.groupByField
},
"aggs": query["aggs"][target.termKey + "_" + target.termValue]["aggs"]
}
};
}
query["aggs"]['query:' + index] = esQuery;
});
query = JSON.stringify(query);
return query;
......@@ -56,7 +59,7 @@ function () {
ElasticQueryBuilder.prototype._buildRangeFilter = function(target) {
var filter = {"range":{}};
filter["range"][target.keyField] = {
filter["range"][target.timestampField] = {
"gte": "$rangeFrom",
"lte": "$rangeTo"
};
......
......@@ -8,7 +8,7 @@ function (angular, _, ElasticQueryBuilder) {
var module = angular.module('grafana.controllers');
module.controller('ElasticQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) {
module.controller('ElasticQueryCtrl', function($scope, $timeout, uiSegmentSrv, templateSrv, $q) {
$scope.functionList = ['count', 'min', 'max', 'total', 'mean'];
......@@ -17,63 +17,40 @@ function (angular, _, ElasticQueryBuilder) {
});
$scope.init = function() {
$scope.queryBuilder = new ElasticQueryBuilder(target);
var target = $scope.target;
target.function = target.function || 'mean';
target.timestampField = target.timestampField || '@timestamp';
target.valueField = target.valueField || 'value' ;
$scope.queryBuilder = new ElasticQueryBuilder(target);
$scope.timestampSegment = uiSegmentSrv.newSegment(target.timestampField);
$scope.valueFieldSegment = uiSegmentSrv.newSegment(target.valueField);
if (!target.keyField) {
target.keyField = '@timestamp';
}
$scope.keyFieldSegment = new MetricSegment({value: target.keyField});
if (!target.valueField) {
target.valueField = 'metric';
}
$scope.valueFieldSegment = new MetricSegment({value: target.valueField});
if (!target.termKey) {
target.termKey = 'service.raw';
}
$scope.termKeySegment = new MetricSegment({value: target.termKey});
if (!target.termValue) {
target.termValue = 'cpu-average/cpu-user';
}
$scope.termValueSegment = new MetricSegment({value: target.termValue});
if (!target.groupByField) {
target.groupByField = 'host.raw';
}
$scope.groupByFieldSegment = new MetricSegment({value: target.groupByField});
if (!target.measurement) {
$scope.measurementSegment = MetricSegment.newSelectMeasurement();
} else {
$scope.measurementSegment = new MetricSegment(target.measurement);
}
$scope.tagSegments = [];
_.each(target.tags, function(tag) {
if (tag.condition) {
$scope.tagSegments.push(MetricSegment.newCondition(tag.condition));
}
$scope.tagSegments.push(new MetricSegment({value: tag.key, type: 'key', cssClass: 'query-segment-key' }));
$scope.tagSegments.push(new MetricSegment.newOperator("="));
$scope.tagSegments.push(new MetricSegment({value: tag.value, type: 'value', cssClass: 'query-segment-value'}));
});
$scope.termKeySegment = uiSegmentSrv.getSegmentForValue(target.termKey, 'select term field');
$scope.termValueSegment = uiSegmentSrv.getSegmentForValue(target.termValue, 'select term value');
$scope.groupByFieldSegment = uiSegmentSrv.getSegmentForValue(target.groupByField, 'add group by');
};
$scope.fixTagSegments();
$scope.getFields = function() {
return $scope.datasource.metricFindQuery('fields()')
.then($scope.transformToSegments(true));
};
$scope.groupBySegments = [];
_.each(target.groupByTags, function(tag) {
$scope.groupBySegments.push(new MetricSegment(tag));
});
$scope.transformToSegments = function(addTemplateVars) {
return function(results) {
var segments = _.map(results, function(segment) {
return uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable });
});
$scope.groupBySegments.push(MetricSegment.newPlusButton());
if (addTemplateVars) {
_.each(templateSrv.variables, function(variable) {
segments.unshift(uiSegmentSrv.newSegment({ type: 'template', value: '$' + variable.name, expandable: true }));
});
}
$scope.removeTagFilterSegment = new MetricSegment({fake: true, value: '-- remove tag filter --'});
$scope.removeGroupBySegment = new MetricSegment({fake: true, value: '-- remove group by --'});
return segments;
};
};
$scope.valueFieldChanged = function() {
......@@ -101,48 +78,11 @@ function (angular, _, ElasticQueryBuilder) {
$scope.$parent.get_data();
};
$scope.fixTagSegments = function() {
var count = $scope.tagSegments.length;
var lastSegment = $scope.tagSegments[Math.max(count-1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
$scope.tagSegments.push(MetricSegment.newPlusButton());
}
};
$scope.groupByTagUpdated = function(segment, index) {
if (segment.value === $scope.removeGroupBySegment.value) {
$scope.target.groupByTags.splice(index, 1);
$scope.groupBySegments.splice(index, 1);
$scope.$parent.get_data();
return;
}
if (index === $scope.groupBySegments.length-1) {
$scope.groupBySegments.push(MetricSegment.newPlusButton());
}
segment.type = 'group-by-key';
segment.fake = false;
$scope.target.groupByTags[index] = segment.value;
$scope.$parent.get_data();
};
$scope.changeFunction = function(func) {
$scope.target.function = func;
$scope.$parent.get_data();
};
$scope.measurementChanged = function() {
$scope.target.measurement = $scope.measurementSegment.value;
$scope.$parent.get_data();
};
$scope.toggleQueryMode = function () {
$scope.target.rawQuery = !$scope.target.rawQuery;
};
$scope.handleQueryError = function(err) {
$scope.parserError = err.message || 'Failed to issue metric query';
return [];
......
......@@ -32,6 +32,14 @@ function (angular, _) {
this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
}
this.getSegmentForValue = function(value, fallbackText) {
if (value) {
return this.newSegment(value);
} else {
return this.newSegment({value: fallbackText, fake: true});
}
};
this.newSelectMeasurement = function() {
return new MetricSegment({value: 'select measurement', fake: true});
};
......
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