Commit 1d4af12f by Rashid Khan

Added the ability to toggle filters

parent 803f24cb
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
"title": "Logstash Search", "title": "Logstash Search",
"services": { "services": {
"query": { "query": {
"idQueue": [], "idQueue": [
1,
2,
3,
4
],
"list": { "list": {
"0": { "0": {
"query": "*", "query": "*",
...@@ -16,14 +21,18 @@ ...@@ -16,14 +21,18 @@
] ]
}, },
"filter": { "filter": {
"idQueue": [], "idQueue": [
1,
2
],
"list": { "list": {
"0": { "0": {
"from": "2013-07-15T03:54:27.219Z", "from": "2013-07-15T16:50:45.363Z",
"to": "2013-07-15T04:09:27.219Z", "to": "2013-07-15T17:50:45.363Z",
"field": "@timestamp", "field": "@timestamp",
"type": "time", "type": "time",
"mandate": "must", "mandate": "must",
"active": true,
"alias": "", "alias": "",
"id": 0 "id": 0
} }
...@@ -63,7 +72,7 @@ ...@@ -63,7 +72,7 @@
"7d", "7d",
"30d" "30d"
], ],
"timespan": "15m", "timespan": "1h",
"timefield": "@timestamp", "timefield": "@timestamp",
"timeformat": "", "timeformat": "",
"refresh": { "refresh": {
...@@ -126,57 +135,40 @@ ...@@ -126,57 +135,40 @@
] ]
}, },
{ {
"title": "Graph", "title": "Filters",
"height": "350px", "height": "50px",
"editable": true, "editable": true,
"collapse": false, "collapse": true,
"collapsable": true, "collapsable": true,
"panels": [ "panels": [
{ {
"loading": false, "loading": false,
"span": 9, "error": false,
"span": 12,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
], ],
"type": "histogram", "type": "filtering",
"status": "Stable", "status": "Experimental"
"query": [ }
{ ]
"query": "*", },
"label": "Query" {
} "title": "Graph",
], "height": "350px",
"mode": "count", "editable": true,
"time_field": "@timestamp", "collapse": false,
"value_field": null, "collapsable": true,
"auto_int": true, "panels": [
"resolution": 100,
"interval": "10s",
"fill": 3,
"linewidth": 3,
"timezone": "browser",
"spyable": true,
"zoomlinks": true,
"bars": true,
"stack": true,
"points": false,
"lines": false,
"legend": true,
"x-axis": true,
"y-axis": true,
"percentage": false,
"interactive": true
},
{ {
"loading": false, "loading": false,
"error": false, "span": 12,
"span": 3,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
], ],
"type": "filtering", "type": "histogram",
"status": "Stable", "status": "Stable",
"query": [ "query": [
{ {
...@@ -189,22 +181,21 @@ ...@@ -189,22 +181,21 @@
"value_field": null, "value_field": null,
"auto_int": true, "auto_int": true,
"resolution": 100, "resolution": 100,
"interval": "5m", "interval": "30s",
"fill": 3, "fill": 3,
"linewidth": 3, "linewidth": 3,
"timezone": "browser", "timezone": "browser",
"spyable": true, "spyable": true,
"zoomlinks": true, "zoomlinks": true,
"bars": true, "bars": true,
"stack": true, "stack": false,
"points": false, "points": false,
"lines": false, "lines": false,
"legend": true, "legend": true,
"x-axis": true, "x-axis": true,
"y-axis": true, "y-axis": true,
"percentage": false, "percentage": false,
"interactive": true, "interactive": true
"title": "Filters"
} }
] ]
}, },
...@@ -273,6 +264,6 @@ ...@@ -273,6 +264,6 @@
"index": { "index": {
"interval": "day", "interval": "day",
"pattern": "[logstash-]YYYY.MM.DD", "pattern": "[logstash-]YYYY.MM.DD",
"default": "MISSING_INDEX" "default": "logstash-*"
} }
} }
\ No newline at end of file
...@@ -300,6 +300,7 @@ angular.module('kibana.services', []) ...@@ -300,6 +300,7 @@ angular.module('kibana.services', [])
// If an id is passed, the filter at that id is updated // If an id is passed, the filter at that id is updated
this.set = function(filter,id) { this.set = function(filter,id) {
_.defaults(filter,{mandate:'must'}) _.defaults(filter,{mandate:'must'})
filter.active = true;
if(!_.isUndefined(id)) { if(!_.isUndefined(id)) {
if(!_.isUndefined(self.list[id])) { if(!_.isUndefined(self.list[id])) {
_.extend(self.list[id],filter); _.extend(self.list[id],filter);
...@@ -328,16 +329,18 @@ angular.module('kibana.services', []) ...@@ -328,16 +329,18 @@ angular.module('kibana.services', [])
// A default match all filter, just in case there are no other filters // A default match all filter, just in case there are no other filters
var bool = ejs.BoolFilter().must(ejs.MatchAllFilter()); var bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
_.each(ids,function(id) { _.each(ids,function(id) {
switch(self.list[id].mandate) if(self.list[id].active) {
{ switch(self.list[id].mandate)
case 'mustNot': {
bool = bool.mustNot(self.getEjsObj(id)); case 'mustNot':
break; bool = bool.mustNot(self.getEjsObj(id));
case 'should': break;
bool = bool.should(self.getEjsObj(id)); case 'should':
break; bool = bool.should(self.getEjsObj(id));
default: break;
bool = bool.must(self.getEjsObj(id)); default:
bool = bool.must(self.getEjsObj(id));
}
} }
}) })
return bool; return bool;
...@@ -348,6 +351,9 @@ angular.module('kibana.services', []) ...@@ -348,6 +351,9 @@ angular.module('kibana.services', [])
} }
this.toEjsObj = function (filter) { this.toEjsObj = function (filter) {
if(!filter.active) {
return false
}
switch(filter.type) switch(filter.type)
{ {
case 'time': case 'time':
...@@ -377,8 +383,8 @@ angular.module('kibana.services', []) ...@@ -377,8 +383,8 @@ angular.module('kibana.services', [])
} }
} }
this.getByType = function(type) { this.getByType = function(type,inactive) {
return _.pick(self.list,self.idsByType(type)) return _.pick(self.list,self.idsByType(type,inactive))
} }
this.removeByType = function(type) { this.removeByType = function(type) {
...@@ -389,13 +395,13 @@ angular.module('kibana.services', []) ...@@ -389,13 +395,13 @@ angular.module('kibana.services', [])
return ids; return ids;
} }
this.idsByType = function(type) { this.idsByType = function(type,inactive) {
return _.pluck(_.where(self.list,{type:type}),'id') return _.pluck(_.where(self.list,{type:type,active:(inactive ? false:true)}),'id')
} }
// This special function looks for all time filters, and returns a time range according to the mode // This special function looks for all time filters, and returns a time range according to the mode
this.timeRange = function(mode) { this.timeRange = function(mode) {
var _t = _.where(self.list,{type:'time'}) var _t = _.where(self.list,{type:'time',active:true})
if(_t.length == 0) { if(_t.length == 0) {
return false; return false;
} }
...@@ -526,11 +532,43 @@ angular.module('kibana.services', []) ...@@ -526,11 +532,43 @@ angular.module('kibana.services', [])
$rootScope.$broadcast('refresh') $rootScope.$broadcast('refresh')
} }
} else { } else {
self.indices = [self.current.index.pattern] self.indices = [self.current.index.default]
$rootScope.$broadcast('refresh') $rootScope.$broadcast('refresh')
} }
} }
this.dash_load = function(dashboard) {
timer.cancel_all();
if(dashboard.index.interval === 'none') {
self.indices = [dashboard.index.default]
}
self.current = dashboard;
// Ok, now that we've setup the current dashboard, we can inject our services
query = $injector.get('query');
filterSrv = $injector.get('filterSrv')
if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length == 0) {
self.refresh();
}
return true;
}
this.gist_id = function(string) {
if(self.is_gist(string))
return string.match(gist_pattern)[0].replace(/.*\//, '');
}
this.is_gist = function(string) {
if(!_.isUndefined(string) && string != '' && !_.isNull(string.match(gist_pattern)))
return string.match(gist_pattern).length > 0 ? true : false;
else
return false
}
this.to_file = function() { this.to_file = function() {
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"}); var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
// from filesaver.js // from filesaver.js
...@@ -699,33 +737,6 @@ angular.module('kibana.services', []) ...@@ -699,33 +737,6 @@ angular.module('kibana.services', [])
}); });
} }
this.dash_load = function(dashboard) {
timer.cancel_all();
if(dashboard.index.interval === 'none') {
self.indices = [dashboard.index.pattern]
}
self.current = dashboard;
// Ok, now that we've setup the current dashboard, we can inject our services
query = $injector.get('query');
filterSrv = $injector.get('filterSrv')
return true;
}
this.gist_id = function(string) {
if(self.is_gist(string))
return string.match(gist_pattern)[0].replace(/.*\//, '');
}
this.is_gist = function(string) {
if(!_.isUndefined(string) && string != '' && !_.isNull(string.match(gist_pattern)))
return string.match(gist_pattern).length > 0 ? true : false;
else
return false
}
}) })
.service('keylistener', function($rootScope) { .service('keylistener', function($rootScope) {
var keys = []; var keys = [];
......
<kibana-panel ng-controller='filtering' ng-init="init()"> <kibana-panel ng-controller='filtering' ng-init="init()">
<style> <style>
.filtering-container {
margin-top: 3px;
}
.filter-panel-filter { .filter-panel-filter {
display:inline-block; display:inline-block;
vertical-align: top;
margin-left: 10px; margin-left: 10px;
width: 200px; width: 200px;
padding: 5px; padding: 5px;
...@@ -17,20 +21,25 @@ ...@@ -17,20 +21,25 @@
.filter-should { .filter-should {
border-bottom: #EF843C 3px solid; border-bottom: #EF843C 3px solid;
} }
.filter-remove { .filter-action {
float:right; float:right;
margin-bottom: 0px !important; margin-bottom: 0px !important;
margin-left: 3px;
} }
</style> </style>
<div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter"> <div class='filtering-container'>
<div class="filter-{{filterSrv.list[id].mandate}}"> <div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter">
{{filterSrv.list[id].type}} ({{filterSrv.list[id].mandate}}) <div class="filter-{{filterSrv.list[id].mandate}}">
<i class="filter-remove pointer icon-remove" ng-click="remove(id)"></i> <strong>{{filterSrv.list[id].type}}</strong> {{filterSrv.list[id].mandate}}
<i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i>
<i class="filter-action pointer" ng-class="{'icon-check': filterSrv.list[id].active,'icon-check-empty': !filterSrv.list[id].active}" bs-tooltip="'Toggle'" ng-click="toggle(id)"></i>
</div>
<ul class="unstyled">
<li ng-repeat="(key,value) in filterSrv.list[id]" ng-show="show_key(key)"><strong>{{key}}</strong> : {{value}}</li>
</ul>
</div> </div>
<ul class="unstyled">
<li ng-repeat="(key,value) in stripped(filterSrv.list[id])"><strong>{{key}}</strong> : {{value}}</li>
</ul>
</div> </div>
</kibana-panel> </kibana-panel>
\ No newline at end of file
...@@ -26,6 +26,11 @@ angular.module('kibana.filtering', []) ...@@ -26,6 +26,11 @@ angular.module('kibana.filtering', [])
dashboard.refresh(); dashboard.refresh();
} }
$scope.toggle = function(id) {
filterSrv.list[id].active = !filterSrv.list[id].active;
dashboard.refresh();
}
$scope.refresh = function(query) { $scope.refresh = function(query) {
$rootScope.$broadcast('refresh') $rootScope.$broadcast('refresh')
} }
...@@ -34,9 +39,8 @@ angular.module('kibana.filtering', []) ...@@ -34,9 +39,8 @@ angular.module('kibana.filtering', [])
$rootScope.$broadcast('render') $rootScope.$broadcast('render')
} }
$scope.stripped = function(filter) { $scope.show_key = function(key) {
var filter = _.omit(filter,'type','id','alias','mandate') return !_.contains(['type','id','alias','mandate','active'],key)
return filter
} }
}); });
\ No newline at end of file
<kibana-panel ng-controller='timepicker' ng-init="init()"> <kibana-panel ng-controller='timepicker' ng-init="init()">
<div class="row-fluid" ng-switch="panel.mode"> <div class="row-fluid" ng-switch="panel.mode" ng-show="filterSrv.idsByType('time').length > 0">
<div ng-switch-when="absolute"> <div ng-switch-when="absolute">
<div class="span5"> <div class="span5">
<form class="nomargin"> <form class="nomargin">
...@@ -47,17 +47,27 @@ ...@@ -47,17 +47,27 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row-fluid" ng-show="filterSrv.idsByType('time').length < 1">
<div>
<div class="span11">
<h4>No time filter present</h4>
</div>
</div>
</div>
<div class="row-fluid nomargin"> <div class="row-fluid nomargin">
<div class="span12 small"> <div class="span12 small" ng-show="filterSrv.idsByType('time').length > 0">
<a ng-click="set_mode('relative')" ng-class="{'strong': (panel.mode == 'relative')}">Relative</a> | <a ng-click="set_mode('relative')" ng-class="{'strong': (panel.mode == 'relative')}">Relative</a> |
<a ng-click="set_mode('absolute')" ng-class="{'strong': (panel.mode == 'absolute')}">Absolute</a> | <a ng-click="set_mode('absolute')" ng-class="{'strong': (panel.mode == 'absolute')}">Absolute</a> |
<a ng-click="set_mode('since')" ng-class="{'strong': (panel.mode == 'since')}">Since</a> <a ng-click="set_mode('since')" ng-class="{'strong': (panel.mode == 'since')}">Since</a>
<span ng-hide="panel.mode == 'absolute'"> | <span ng-hide="panel.mode == 'absolute' || panel.mode == 'none'"> |
<input type="checkbox" ng-model="panel.refresh.enable" ng-change='refresh();'> Auto-refresh <input type="checkbox" ng-model="panel.refresh.enable" ng-change='refresh();'> Auto-refresh
<span ng-class="{'ng-cloak': !panel.refresh.enable}"> <span ng-class="{'ng-cloak': !panel.refresh.enable}">
every <a data-title="<small>Auto-refresh Settings</small>" data-placement="bottom" bs-popover="'panels/timepicker/refreshctrl.html'">{{panel.refresh.interval}}s</a>. every <a data-title="<small>Auto-refresh Settings</small>" data-placement="bottom" bs-popover="'panels/timepicker/refreshctrl.html'">{{panel.refresh.interval}}s</a>.
</span> </span>
</span> </span>
</div> </div>
<div class="span12 small" ng-show="filterSrv.idsByType('time').length < 1">
<a class='btn btn-small' ng-click="time_apply()">Create a time filter</a>
</div>
</div> </div>
</kibana-panel> </kibana-panel>
\ No newline at end of file
...@@ -44,7 +44,7 @@ angular.module('kibana.timepicker', []) ...@@ -44,7 +44,7 @@ angular.module('kibana.timepicker', [])
// Private refresh interval that we can use for view display without causing // Private refresh interval that we can use for view display without causing
// unnecessary refreshes during changes // unnecessary refreshes during changes
$scope.refresh_interval = $scope.panel.refresh.interval $scope.refresh_interval = $scope.panel.refresh.interval
$scope.filterSrv = filterSrv;
// Init a private time object with Date() objects depending on mode // Init a private time object with Date() objects depending on mode
switch($scope.panel.mode) { switch($scope.panel.mode) {
...@@ -68,7 +68,12 @@ angular.module('kibana.timepicker', []) ...@@ -68,7 +68,12 @@ angular.module('kibana.timepicker', [])
break; break;
} }
$scope.time.field = $scope.panel.timefield; $scope.time.field = $scope.panel.timefield;
$scope.time_apply(); // These 3 statements basicly do everything time_apply() does
set_timepicker($scope.time.from,$scope.time.to)
update_panel()
set_time_filter($scope.time)
dashboard.refresh();
// Start refresh timer if enabled // Start refresh timer if enabled
if ($scope.panel.refresh.enable) if ($scope.panel.refresh.enable)
...@@ -76,17 +81,19 @@ angular.module('kibana.timepicker', []) ...@@ -76,17 +81,19 @@ angular.module('kibana.timepicker', [])
// In case some other panel broadcasts a time, set us to an absolute range // In case some other panel broadcasts a time, set us to an absolute range
$scope.$on('refresh', function() { $scope.$on('refresh', function() {
var time = filterSrv.timeRange('min') if(filterSrv.idsByType('time').length > 0) {
var time = filterSrv.timeRange('min')
if($scope.time.from.diff(moment.utc(time.from)) != 0
|| $scope.time.to.diff(moment.utc(time.to)) != 0) if($scope.time.from.diff(moment.utc(time.from)) != 0
{ || $scope.time.to.diff(moment.utc(time.to)) != 0)
$scope.panel.mode = 'absolute'; {
$scope.set_mode('absolute');
// These 3 statements basicly do everything time_apply() does
set_timepicker(moment(time.from),moment(time.to)) // These 3 statements basicly do everything time_apply() does
$scope.time = $scope.time_calc(); set_timepicker(moment(time.from),moment(time.to))
update_panel() $scope.time = $scope.time_calc();
update_panel()
}
} }
}); });
} }
......
...@@ -36,12 +36,12 @@ ...@@ -36,12 +36,12 @@
<div class="span3"> <div class="span3">
<h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select> <h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
</div> </div>
<div class="span5"> <div class="span5" ng-show="dashboard.current.index.interval != 'none'">
<h6>Index <span ng-show="dashboard.current.index.interval != 'none'">pattern <small>Absolutes in []</small></span></h6> <h6>Index pattern <small>Absolutes in []</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.pattern"> <input type="text" class="input-medium" ng-model="dashboard.current.index.pattern">
</div> </div>
<div class="span4"> <div class="span4">
<h6>Failover Index <small>If index not found</small></h6> <h6>Default Index <small ng-show="dashboard.current.index.interval != 'none'">If index not found</small></h6>
<input type="text" class="input-medium" ng-model="dashboard.current.index.default"> <input type="text" class="input-medium" ng-model="dashboard.current.index.default">
</div> </div>
</div> </div>
...@@ -87,5 +87,5 @@ ...@@ -87,5 +87,5 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();">Close</button> <button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();dashboard.refresh()">Close</button>
</div> </div>
\ No newline at end of file
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