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) { ...@@ -15,6 +15,7 @@ function (angular, _, queryDef) {
$scope.bucketAggTypes = queryDef.bucketAggTypes; $scope.bucketAggTypes = queryDef.bucketAggTypes;
$scope.orderOptions = queryDef.orderOptions; $scope.orderOptions = queryDef.orderOptions;
$scope.sizeOptions = queryDef.sizeOptions; $scope.sizeOptions = queryDef.sizeOptions;
$scope.intervalOptions = queryDef.intervalOptions;
$rootScope.onAppEvent('elastic-query-updated', function() { $rootScope.onAppEvent('elastic-query-updated', function() {
$scope.validateModel(); $scope.validateModel();
...@@ -27,36 +28,52 @@ function (angular, _, queryDef) { ...@@ -27,36 +28,52 @@ function (angular, _, queryDef) {
}; };
$scope.onChangeInternal = function() { $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.validateModel = function() {
$scope.index = _.indexOf(bucketAggs, $scope.agg); $scope.index = _.indexOf(bucketAggs, $scope.agg);
$scope.isFirst = $scope.index === 0; $scope.isFirst = $scope.index === 0;
$scope.isLast = $scope.index === bucketAggs.length - 1; $scope.isLast = $scope.index === bucketAggs.length - 1;
$scope.settingsLinkText = "";
if ($scope.agg.type === "terms") { var settingsLinkText = "";
$scope.agg.order = $scope.agg.order || "asc"; var settings = $scope.agg.settings || {};
$scope.agg.size = $scope.agg.size || "0";
$scope.agg.orderBy = $scope.agg.orderBy || "_term";
if ($scope.agg.size !== '0') { switch($scope.agg.type) {
$scope.settingsLinkText = queryDef.describeOrder($scope.agg.order) + ' ' + $scope.agg.size + ', '; 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') { settingsLinkText += 'Order by: ' + queryDef.describeOrderBy(settings.orderBy, $scope.target);
$scope.settingsLinkText += ' (' + $scope.agg.order + ')';
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; return true;
}; };
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
<span ng-hide="isFirst">Then by</span> <span ng-hide="isFirst">Then by</span>
</li> </li>
<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>
<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>
<li class="tight-form-item last" ng-if="settingsLinkText"> <li class="tight-form-item last" ng-if="settingsLinkText">
<a ng-click="toggleOptions()">{{settingsLinkText}}</a> <a ng-click="toggleOptions()">{{settingsLinkText}}</a>
...@@ -27,6 +27,19 @@ ...@@ -27,6 +27,19 @@
</div> </div>
<div class="tight-form" ng-if="showOptions"> <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-inner-box" ng-if="agg.type === 'terms'">
<div class="tight-form"> <div class="tight-form">
<ul class="tight-form-list"> <ul class="tight-form-list">
...@@ -34,7 +47,7 @@ ...@@ -34,7 +47,7 @@
Order Order
</li> </li>
<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> </li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
...@@ -45,7 +58,7 @@ ...@@ -45,7 +58,7 @@
Size Size
</li> </li>
<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> </li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
...@@ -56,7 +69,7 @@ ...@@ -56,7 +69,7 @@
Order By Order By
</li> </li>
<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> </li>
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
......
...@@ -74,63 +74,4 @@ ...@@ -74,63 +74,4 @@
</elastic-bucket-agg> </elastic-bucket-agg>
</div> </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> </div>
<section class="grafana-metric-options"> <section class="grafana-metric-options">
<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-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">
<ul class="tight-form-list"> <ul class="tight-form-list">
<li class="tight-form-item tight-form-item-icon"> <li class="tight-form-item tight-form-item-icon">
<i class="fa fa-info-circle"></i> <i class="fa fa-info-circle"></i>
...@@ -28,16 +9,6 @@ ...@@ -28,16 +9,6 @@
alias patterns alias patterns
</a> </a>
</li> </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> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
...@@ -56,30 +27,6 @@ ...@@ -56,30 +27,6 @@
</ul> </ul>
</div> </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>
</div> </div>
......
...@@ -16,21 +16,24 @@ function (angular) { ...@@ -16,21 +16,24 @@ function (angular) {
ElasticQueryBuilder.prototype.buildTermsAgg = function(aggDef, queryNode, target) { ElasticQueryBuilder.prototype.buildTermsAgg = function(aggDef, queryNode, target) {
var metricRef, metric, size, y; var metricRef, metric, size, y;
queryNode.terms = { "field": aggDef.field }; 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 (size > 0) { queryNode.terms.size = size; }
if (aggDef.orderBy !== void 0) { if (aggDef.settings.orderBy !== void 0) {
queryNode.terms.order = {}; 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 // 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)) { if (!isNaN(metricRef)) {
for (y = 0; y < target.metrics.length; y++) { for (y = 0; y < target.metrics.length; y++) {
metric = target.metrics[y]; metric = target.metrics[y];
if (metric.id === aggDef.orderBy) { if (metric.id === aggDef.settings.orderBy) {
queryNode.aggs = {}; queryNode.aggs = {};
queryNode.aggs[metric.id] = {}; queryNode.aggs[metric.id] = {};
queryNode.aggs[metric.id][metric.type] = {field: metric.field}; queryNode.aggs[metric.id][metric.type] = {field: metric.field};
...@@ -43,6 +46,14 @@ function (angular) { ...@@ -43,6 +46,14 @@ function (angular) {
return queryNode; 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) { ElasticQueryBuilder.prototype.build = function(target) {
if (target.rawQuery) { if (target.rawQuery) {
return angular.fromJson(target.rawQuery); return angular.fromJson(target.rawQuery);
...@@ -77,7 +88,7 @@ function (angular) { ...@@ -77,7 +88,7 @@ function (angular) {
switch(aggDef.type) { switch(aggDef.type) {
case 'date_histogram': { case 'date_histogram': {
esAgg["date_histogram"] = { esAgg["date_histogram"] = {
"interval": target.interval || "$interval", "interval": this.getInterval(aggDef),
"field": this.timeField, "field": this.timeField,
"min_doc_count": 1, "min_doc_count": 1,
"extended_bounds": { "min": "$timeFrom", "max": "$timeTo" } "extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
......
...@@ -14,7 +14,7 @@ function (angular, _) { ...@@ -14,7 +14,7 @@ function (angular, _) {
if (!target) { return; } if (!target) { return; }
target.metrics = target.metrics || [{ type: 'count', id: '1' }]; 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; target.timeField = $scope.datasource.timeField;
}; };
......
...@@ -53,6 +53,17 @@ function (_) { ...@@ -53,6 +53,17 @@ function (_) {
{text: 'Std Dev Lower', value: 'std_deviation_bounds_lower'}, {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) { getOrderByOptions: function(target) {
var self = this; var self = this;
var metricRefs = []; var metricRefs = [];
......
...@@ -48,9 +48,12 @@ define([ ...@@ -48,9 +48,12 @@ define([
it('with term agg and order by metric agg', function() { it('with term agg and order by metric agg', function() {
var query = builder.build({ 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: [ 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'} {type: 'date_histogram', field: '@timestamp', id: '3'}
], ],
}, 100, 1000); }, 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