Skip to content

Commit

Permalink
add percentiles panel, only support elasticsearch 1.1 or above.
Browse files Browse the repository at this point in the history
	new file:   app/panels/percentiles/editor.html
	new file:   app/panels/percentiles/module.html
	new file:   app/panels/percentiles/module.js
	modified:   config.js
  • Loading branch information
chenryn committed May 17, 2014
1 parent c19105c commit c27b449
Show file tree
Hide file tree
Showing 4 changed files with 303 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/app/panels/percentiles/editor.html
@@ -0,0 +1,38 @@
<div class="row-fluid">
<h5>Details</h5>
<div class="editor-option">
<label class="small">Featured Stat</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in modes"></select>
</div>
<div class="editor-option">
<label class="small">Field <tip>This field must contain a numeric value</tip></label>
<input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-large" ng-model="panel.field">
</div>
<div class="editor-option">
<label class="small">Unit</label>
<input type="text" class="input-large" ng-model="panel.unit">
</div>

<h5>Columns</h5>
<div class="editor-option" ng-repeat="stat in modes">
<label class="small">{{stat}}</label><input type="checkbox" ng-model="panel.show[stat]" ng-checked="panel.show[stat]">
</div>

<h5>Formating</h5>
<div class="editor-option">
<label class="small">Format</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.format" ng-options="f for f in ['number','float','money','bytes']"></select>
</div>
<div class="editor-option">
<label class="small">Font Size</label>
<select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
</div>
<div class="editor-option">
<label class="small">Display Breakdowns</label>
<select class="input-mini" ng-model="panel.display_breakdown" ng-options="f for f in ['yes', 'no']"></select></span>
</div>
<div class="editor-option">
<label class="small">Query column name</label>
<input type="text" class="input-large" ng-model="panel.label_name">
</div>
</div>
37 changes: 37 additions & 0 deletions src/app/panels/percentiles/module.html
@@ -0,0 +1,37 @@
<div ng-controller="percentiles" ng-init="init()">
<style>
table.stats-table th, table.stats-table td {
text-align: right;
}

table.stats-table th:first-child, table.stats-table td:first-child {
text-align: left !important;
}


</style>

<h1 ng-style="panel.style" style="text-align: center; line-height: .6em">{{data.value|formatstats:panel.format}} <small style="font-size: .5em; line-height: 0;">{{panel.unit}} ({{panel.mode}})</small></h1>
<table ng-show="panel.display_breakdown == 'yes'" cellspacing="0" class="table-hover table table-condensed stats-table" style="margin-top: 38px;">
<tbody>
<thead>
<tr>
<th><a href="" ng-click="set_sort('label')" ng-class="{'icon-chevron-down': panel.sort_field == 'label' && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == 'label' && panel.sort_reverse == false}"> {{panel.label_name}} </a></th>
<th ng-repeat="stat in modes" ng-show="panel.show[stat]">
<a href=""
ng-click="set_sort(stat)"
ng-class="{'icon-chevron-down': panel.sort_field == stat && panel.sort_reverse == true, 'icon-chevron-up': panel.sort_field == stat && panel.sort_reverse == false}">
{{stat}}%
</a>
</th>
</tr>
</thead>
<tr ng-repeat="item in data.rows | orderBy:(panel.sort_field == 'label' ? 'label' : 'value.'+panel.sort_field):panel.sort_reverse">
<td><i class="icon-circle" ng-style="{color:item.color}"></i> {{item.label}}</td>
<td ng-repeat="stat in modes" ng-show="panel.show[stat]">{{item.value[stat]|formatstats:panel.format}} {{panel.unit}}</td>

<!--<td style="text-align: right;">{{item.value|formatstats:panel.format}} {{panel.unit}}</td>-->
</tr>
</tbody>
</table>
</div>
227 changes: 227 additions & 0 deletions src/app/panels/percentiles/module.js
@@ -0,0 +1,227 @@
/*
## Percentiles Module
### Parameters
* format :: The format of the value returned. (Default: number)
* style :: The font size of the main number to be displayed.
* mode :: The aggergate value to use for display
* spyable :: Dislay the 'eye' icon that show the last elasticsearch query
*/
define([
'angular',
'app',
'lodash',
'jquery',
'kbn',
'numeral',
'config'
], function (
angular,
app,
_,
$,
kbn,
numeral,
config
) {

'use strict';

var module = angular.module('kibana.panels.percentiles', []);
app.useModule(module);

module.controller('percentiles', function ($scope, querySrv, dashboard, filterSrv, $http) {

$scope.panelMeta = {
modals : [
{
description: "Inspect",
icon: "icon-info-sign",
partial: "app/partials/inspector.html",
show: $scope.panel.spyable
}
],
editorTabs : [
{title:'Queries', src:'app/partials/querySelect.html'}
],
status: 'Beta',
description: 'A percentiles panel for displaying aggregations using the Elastic Search percentiles aggregation query.'
};

$scope.modes = ['50.0','75.0','90.0','95.0','99.0'];

var defaults = {
queries : {
mode : 'all',
ids : []
},
style : { "font-size": '24pt'},
format: 'number',
mode: 'count',
display_breakdown: 'yes',
sort_field: '',
sort_reverse: false,
label_name: 'Query',
value_name: 'Value',
spyable : true,
show: {
'50.0': true,
'75.0': true,
'90.0': true,
'95.0': true,
'99.0': true,
}
};

_.defaults($scope.panel, defaults);

$scope.init = function () {
$scope.ready = false;
$scope.$on('refresh', function () {
$scope.get_data();
});
$scope.get_data();
};

$scope.set_sort = function(field) {
console.log(field);
if($scope.panel.sort_field === field && $scope.panel.sort_reverse === false) {
$scope.panel.sort_reverse = true;
} else if($scope.panel.sort_field === field && $scope.panel.sort_reverse === true) {
$scope.panel.sort_field = '';
$scope.panel.sort_reverse = false;
} else {
$scope.panel.sort_field = field;
$scope.panel.sort_reverse = false;
}
};

$scope.get_data = function () {
if(dashboard.indices.length === 0) {
return;
}

$scope.panelMeta.loading = true;

var request,
results,
boolQuery,
queries;

$scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
queries = querySrv.getQueryObjs($scope.panel.queries.ids);

// This could probably be changed to a BoolFilter
boolQuery = $scope.ejs.BoolQuery();
_.each(queries,function(q) {
boolQuery = boolQuery.should(querySrv.toEjsObj(q));
});

request = {
'stats': {
'filter': JSON.parse($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
boolQuery,
filterSrv.getBoolFilter(filterSrv.ids())
)
).toString(), true),
'aggs': {
'stats': {
'percentiles': {
'field': $scope.panel.field,
'percents': $scope.modes
}
}
}
}
};

$.each(queries, function (i, q) {
var query = $scope.ejs.BoolQuery();
query.should(querySrv.toEjsObj(q));
var qname = 'stats_'+i;
var aggsquery = {};
aggsquery[qname] = {
'percentiles': {
'field': $scope.panel.field,
'percents': $scope.modes
}
};
request[qname] = {
'filter': JSON.parse($scope.ejs.QueryFilter(
$scope.ejs.FilteredQuery(
query,
filterSrv.getBoolFilter(filterSrv.ids())
)
).toString(), true),
'aggs': aggsquery
};
});
// Populate the inspector panel
$scope.inspector = angular.toJson({aggs:request},true);

results = $http({
url: config.elasticsearch + '/' + dashboard.indices + '/_search?size=0',
method: "POST",
data: { aggs: request }
});

results.then(function(results) {
$scope.panelMeta.loading = false;
var value = results.data.aggregations.stats['doc_count'];

var rows = queries.map(function (q, i) {
var alias = q.alias || q.query;
var obj = _.clone(q);
obj.label = alias;
obj.Label = alias.toLowerCase(); //sort field
obj.value = results.data.aggregations['stats_'+i]['stats_'+i];
obj.Value = results.data.aggregations['stats_'+i]['stats_'+i]; //sort field
return obj;
});

$scope.data = {
value: value,
rows: rows
};

$scope.$emit('render');
});
};

$scope.set_refresh = function (state) {
$scope.refresh = state;
};

$scope.close_edit = function() {
if($scope.refresh) {
$scope.get_data();
}
$scope.refresh = false;
$scope.$emit('render');
};

});

module.filter('formatstats', function(){
return function (value,format) {
switch (format) {
case 'money':
value = numeral(value).format('$0,0.00');
break;
case 'bytes':
value = numeral(value).format('0.00b');
break;
case 'float':
value = numeral(value).format('0.000');
break;
default:
value = numeral(value).format('0,0');
}
return value;
};
});

});
1 change: 1 addition & 0 deletions src/config.js
Expand Up @@ -74,6 +74,7 @@ function (Settings) {
'query',
'terms',
'stats',
'percentiles',
'sparklines'
]
});
Expand Down

0 comments on commit c27b449

Please sign in to comment.