Commit f361f324 by Torkel Ödegaard

feat(elasticsearch): more polish to editor, made interval configurable per query, #1034

parent 3999a3ca
......@@ -15,6 +15,7 @@ function (angular, _, queryDef) {
$scope.bucketAggTypes = queryDef.bucketAggTypes;
$scope.orderOptions = queryDef.orderOptions;
$scope.sizeOptions = queryDef.sizeOptions;
$scope.intervalOptions = queryDef.intervalOptions;
$rootScope.onAppEvent('elastic-query-updated', function() {
$scope.validateModel();
......@@ -27,36 +28,52 @@ function (angular, _, queryDef) {
};
$scope.onChangeInternal = function() {
if ($scope.validateModel()) {
$scope.onChange();
}
$scope.onChange();
};
$scope.onTypeChanged = function() {
$scope.agg.settings = {};
$scope.showOptions = false;
$scope.validateModel();
$scope.onChange();
};
$scope.validateModel = function() {
$scope.index = _.indexOf(bucketAggs, $scope.agg);
$scope.isFirst = $scope.index === 0;
$scope.isLast = $scope.index === bucketAggs.length - 1;
$scope.settingsLinkText = "";
if ($scope.agg.type === "terms") {
$scope.agg.order = $scope.agg.order || "asc";
$scope.agg.size = $scope.agg.size || "0";
$scope.agg.orderBy = $scope.agg.orderBy || "_term";
var settingsLinkText = "";
var settings = $scope.agg.settings || {};
if ($scope.agg.size !== '0') {
$scope.settingsLinkText = queryDef.describeOrder($scope.agg.order) + ' ' + $scope.agg.size + ', ';
}
switch($scope.agg.type) {
case 'terms': {
settings.order = settings.order || "asc";
settings.size = settings.size || "0";
settings.orderBy = settings.orderBy || "_term";
$scope.settingsLinkText += 'Order by: ' + queryDef.describeOrderBy($scope.agg.orderBy, $scope.target);
if (settings.size !== '0') {
settingsLinkText = queryDef.describeOrder(settings.order) + ' ' + settings.size + ', ';
}
if ($scope.agg.size === '0') {
$scope.settingsLinkText += ' (' + $scope.agg.order + ')';
settingsLinkText += 'Order by: ' + queryDef.describeOrderBy(settings.orderBy, $scope.target);
if (settings.size === '0') {
settingsLinkText += ' (' + settings.order + ')';
}
break;
}
case 'date_histogram': {
settings.interval = settings.interval || 'auto';
$scope.agg.field = $scope.target.timeField;
settingsLinkText = 'Interval: ' + settings.interval;
}
} else if ($scope.agg.type === 'date_histogram') {
$scope.agg.field = $scope.target.timeField;
}
$scope.settingsLinkText = settingsLinkText;
$scope.agg.settings = settings;
return true;
};
......
......@@ -5,10 +5,10 @@
<span ng-hide="isFirst">Then by</span>
</li>
<li>
<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onChangeInternal()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
</li>
<li>
<metric-segment-model property="agg.field" get-options="getFields()" on-change="onChangeInternal()" css-class="tight-form-item-xxlarge"></metric-segment>
<metric-segment-model property="agg.field" get-options="getFields()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment>
</li>
<li class="tight-form-item last" ng-if="settingsLinkText">
<a ng-click="toggleOptions()">{{settingsLinkText}}</a>
......@@ -27,6 +27,19 @@
</div>
<div class="tight-form" ng-if="showOptions">
<div class="tight-form-inner-box" ng-if="agg.type === 'date_histogram'">
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 60px">
Interval
</li>
<li>
<metric-segment-model property="agg.settings.interval" options="intervalOptions" on-change="onChangeInternal()" css-class="last" custom="true"></metric-segment-model>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
<div class="tight-form-inner-box" ng-if="agg.type === 'terms'">
<div class="tight-form">
<ul class="tight-form-list">
......@@ -34,7 +47,7 @@
Order
</li>
<li>
<metric-segment-model property="agg.order" options="orderOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
<metric-segment-model property="agg.settings.order" options="orderOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
</li>
</ul>
<div class="clearfix"></div>
......@@ -45,7 +58,7 @@
Size
</li>
<li>
<metric-segment-model property="agg.size" options="sizeOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
<metric-segment-model property="agg.settings.size" options="sizeOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
</li>
</ul>
<div class="clearfix"></div>
......@@ -56,7 +69,7 @@
Order By
</li>
<li>
<metric-segment-model property="agg.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
<metric-segment-model property="agg.settings.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
</li>
</ul>
<div class="clearfix"></div>
......
......@@ -74,63 +74,4 @@
</elastic-bucket-agg>
</div>
<!-- <div class="tight&#45;form"> -->
<!-- <ul class="tight&#45;form&#45;list"> -->
<!-- <li class="tight&#45;form&#45;item query&#45;keyword tight&#45;form&#45;align" style="width: 75px;"> -->
<!-- <span ng&#45;show="$first">Group by</span> -->
<!-- <span ng&#45;show="!$first">Then by</span> -->
<!-- </li> -->
<!-- <li> -->
<!-- <elastic&#45;query&#45;component model="agg" get&#45;fields="getFields()" on&#45;change="queryUpdated()"></elastic&#45;query&#45;component> -->
<!-- </li> -->
<!-- </ul> -->
<!-- -->
<!-- <ul class="tight&#45;form&#45;list pull&#45;right"> -->
<!-- <li class="tight&#45;form&#45;item" ng&#45;if="$index === 0"> -->
<!-- <a class="pointer" ng&#45;click="addBucketAgg()"><i class="fa fa&#45;plus"></i></a> -->
<!-- </li> -->
<!-- <li class="tight&#45;form&#45;item" ng&#45;if="!$last"> -->
<!-- <a class="pointer" ng&#45;click="removeBucketAgg($index)"><i class="fa fa&#45;minus"></i></a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- <div class="clearfix"></div> -->
<!-- </div> -->
<!-- -->
<!-- <div class="tight&#45;form" ng&#45;if="agg.showOptions"> -->
<!-- <div style="margin: 20px 0 20px 148px;display: inline&#45;block"> -->
<!-- <div class="tight&#45;form"> -->
<!-- <ul class="tight&#45;form&#45;list"> -->
<!-- <li class="tight&#45;form&#45;item" style="width: 60px"> -->
<!-- Order -->
<!-- </li> -->
<!-- <li> -->
<!-- <metric&#45;segment segment="" get&#45;alt&#45;segments="getFields()" on&#45;value&#45;changed="timeFieldChanged()"></metric&#45;segment> -->
<!-- </li> -->
<!-- </ul> -->
<!-- <div class="clearfix"></div> -->
<!-- </div> -->
<!-- <div class="tight&#45;form"> -->
<!-- <ul class="tight&#45;form&#45;list"> -->
<!-- <li class="tight&#45;form&#45;item" style="width: 60px"> -->
<!-- Size -->
<!-- </li> -->
<!-- <li> -->
<!-- <input type="text" class="input&#45;mini tight&#45;form&#45;input" ng&#45;model="agg.options.size" spellcheck='false' placeholder="0" ng&#45;blur="get_data()"> -->
<!-- </li> -->
<!-- </ul> -->
<!-- <div class="clearfix"></div> -->
<!-- </div> -->
<!-- <div class="tight&#45;form last"> -->
<!-- <ul class="tight&#45;form&#45;list"> -->
<!-- <li class="tight&#45;form&#45;item" style="width: 60px"> -->
<!-- Order by -->
<!-- </li> -->
<!-- <li> -->
<!-- <metric&#45;segment segment="timeSegment" get&#45;alt&#45;segments="getFields()" on&#45;value&#45;changed="timeFieldChanged()"></metric&#45;segment> -->
<!-- </li> -->
<!-- </ul> -->
<!-- <div class="clearfix"></div> -->
<!-- </div> -->
<!-- </div> -->
</div>
<section class="grafana-metric-options">
<div class="tight-form">
<ul class="tight-form-list">
<li class="tight-form-item tight-form-item-icon">
<i class="fa fa-wrench"></i>
</li>
<li class="tight-form-item">
Group by time interval
</li>
<li>
<input type="text" class="input-medium tight-form-input" ng-model="panel.interval" ng-blur="get_data();"
spellcheck='false' placeholder="example: >10s">
</li>
<li class="tight-form-item">
<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >60s'" data-placement="right"></i>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="tight-form">
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item tight-form-item-icon">
<i class="fa fa-info-circle"></i>
......@@ -28,16 +9,6 @@
alias patterns
</a>
</li>
<li class="tight-form-item">
<a ng-click="toggleEditorHelp(2)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
stacking &amp; and fill
</a>
</li>
<li class="tight-form-item">
<a ng-click="toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
group by time
</a>
</li>
</ul>
<div class="clearfix"></div>
</div>
......@@ -56,30 +27,6 @@
</ul>
</div>
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 2">
<h5>Stacking and fill</h5>
<ul>
<li>When stacking is enabled it important that points align</li>
<li>If there are missing points for one series it can cause gaps or missing bars</li>
<li>You must use fill(0), and select a group by time low limit</li>
<li>Use the group by time option below your queries and specify for example &gt;10s if your metrics are written every 10 seconds</li>
<li>This will insert zeros for series that are missing measurements and will make stacking work properly</li>
</ul>
</div>
<div class="grafana-info-box span6" ng-if="editorHelpIndex === 3">
<h5>Group by time</h5>
<ul>
<li>Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana</li>
<li>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</li>
<li>If you use fill(0) or fill(null) set a low limit for the auto group by time interval</li>
<li>The low limit can only be set in the group by time option below your queries</li>
<li>You set a low limit by adding a greater sign before the interval</li>
<li>Example: &gt;60s if you write metrics to ElasticDB every 60 seconds</li>
</ul>
</div>
</div>
</div>
......
......@@ -16,21 +16,24 @@ function (angular) {
ElasticQueryBuilder.prototype.buildTermsAgg = function(aggDef, queryNode, target) {
var metricRef, metric, size, y;
queryNode.terms = { "field": aggDef.field };
size = parseInt(aggDef.size, 10);
if (!aggDef.settings) {
return queryNode;
}
size = parseInt(aggDef.settings.size, 10);
if (size > 0) { queryNode.terms.size = size; }
if (aggDef.orderBy !== void 0) {
if (aggDef.settings.orderBy !== void 0) {
queryNode.terms.order = {};
queryNode.terms.order[aggDef.orderBy] = aggDef.order;
queryNode.terms.order[aggDef.settings.orderBy] = aggDef.settings.order;
// if metric ref, look it up and add it to this agg level
metricRef = parseInt(aggDef.orderBy, 10);
metricRef = parseInt(aggDef.settings.orderBy, 10);
if (!isNaN(metricRef)) {
for (y = 0; y < target.metrics.length; y++) {
metric = target.metrics[y];
if (metric.id === aggDef.orderBy) {
if (metric.id === aggDef.settings.orderBy) {
queryNode.aggs = {};
queryNode.aggs[metric.id] = {};
queryNode.aggs[metric.id][metric.type] = {field: metric.field};
......@@ -43,6 +46,14 @@ function (angular) {
return queryNode;
};
ElasticQueryBuilder.prototype.getInterval = function(agg) {
if (agg.settings && agg.settings.interval !== 'auto') {
return agg.settings.interval;
} else {
return '$interval';
}
};
ElasticQueryBuilder.prototype.build = function(target) {
if (target.rawQuery) {
return angular.fromJson(target.rawQuery);
......@@ -77,7 +88,7 @@ function (angular) {
switch(aggDef.type) {
case 'date_histogram': {
esAgg["date_histogram"] = {
"interval": target.interval || "$interval",
"interval": this.getInterval(aggDef),
"field": this.timeField,
"min_doc_count": 1,
"extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
......
......@@ -14,7 +14,7 @@ function (angular, _) {
if (!target) { return; }
target.metrics = target.metrics || [{ type: 'count', id: '1' }];
target.bucketAggs = target.bucketAggs || [{ type: 'date_histogram', id: '2'}];
target.bucketAggs = target.bucketAggs || [{type: 'date_histogram', id: '2', settings: {interval: 'auto'}}];
target.timeField = $scope.datasource.timeField;
};
......
......@@ -53,6 +53,17 @@ function (_) {
{text: 'Std Dev Lower', value: 'std_deviation_bounds_lower'},
],
intervalOptions: [
{text: 'auto', value: 'auto'},
{text: '10s', value: '10s'},
{text: '1m', value: '1m'},
{text: '5m', value: '5m'},
{text: '10m', value: '10m'},
{text: '20m', value: '20m'},
{text: '1h', value: '1h'},
{text: '1d', value: '1d'},
],
getOrderByOptions: function(target) {
var self = this;
var metricRefs = [];
......
......@@ -48,9 +48,12 @@ define([
it('with term agg and order by metric agg', function() {
var query = builder.build({
metrics: [{type: 'count', id: '1'}, {type: 'avg', field: '@value', id: '5'}],
metrics: [
{type: 'count', id: '1'},
{type: 'avg', field: '@value', id: '5'}
],
bucketAggs: [
{type: 'terms', field: '@host', size: 5, order: 'asc', orderBy: '5', id: '2' },
{type: 'terms', field: '@host', settings: {size: 5, order: 'asc', orderBy: '5'}, id: '2' },
{type: 'date_histogram', field: '@timestamp', id: '3'}
],
}, 100, 1000);
......
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