mirror of https://github.com/grafana/grafana.git
				
				
				
			Refactored panels to used dashboard, query and filter services and store them on the dashboard
This commit is contained in:
		
							parent
							
								
									0d560c24e6
								
							
						
					
					
						commit
						0d0d3f4a36
					
				|  | @ -123,8 +123,6 @@ | |||
|   max-width: 500px; | ||||
| } | ||||
| 
 | ||||
| .popover-title { display: none; } | ||||
| 
 | ||||
| .tiny { | ||||
|   font-size: 50%; | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,8 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| angular.module('kibana.controllers', []) | ||||
| .controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus, fields, dashboard) { | ||||
| .controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus,  | ||||
|   fields, dashboard) { | ||||
| 
 | ||||
|   var _d = { | ||||
|     title: "", | ||||
|  |  | |||
							
								
								
									
										227
									
								
								js/services.js
								
								
								
								
							
							
						
						
									
										227
									
								
								js/services.js
								
								
								
								
							|  | @ -84,6 +84,7 @@ angular.module('kibana.services', []) | |||
| 
 | ||||
| }) | ||||
| .service('kbnIndex',function($http) { | ||||
| 
 | ||||
|   // returns a promise containing an array of all indices matching the index
 | ||||
|   // pattern that exist in a given range
 | ||||
|   this.indices = function(from,to,pattern,interval) { | ||||
|  | @ -233,14 +234,14 @@ angular.module('kibana.services', []) | |||
|       var _query = { | ||||
|         query: '*', | ||||
|         alias: '', | ||||
|         color: colorAt(_id) | ||||
|         color: colorAt(_id), | ||||
|         id: _id | ||||
|       } | ||||
|       _.defaults(query,_query) | ||||
|       self.list[_id] = query; | ||||
|       self.ids.push(_id) | ||||
|       return id; | ||||
|       return _id; | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   this.remove = function(id) { | ||||
|  | @ -256,6 +257,10 @@ angular.module('kibana.services', []) | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   this.findQuery = function(queryString) { | ||||
|     return _.findWhere(self.list,{query:queryString}) | ||||
|   } | ||||
| 
 | ||||
|   var nextId = function() { | ||||
|     if(_q.idQueue.length > 0) { | ||||
|       return _q.idQueue.shift() | ||||
|  | @ -271,14 +276,185 @@ angular.module('kibana.services', []) | |||
|   init(); | ||||
| 
 | ||||
| }) | ||||
| .service('dashboard', function($routeParams, $http, $rootScope, ejsResource, timer) { | ||||
| .service('filterSrv', function(dashboard, ejsResource) { | ||||
|   // Create an object to hold our service state on the dashboard
 | ||||
|   dashboard.current.services.filter = dashboard.current.services.filter || {}; | ||||
|   _.defaults(dashboard.current.services.filter,{ | ||||
|     idQueue : [], | ||||
|     list : {}, | ||||
|     ids : [], | ||||
|   }); | ||||
| 
 | ||||
|   // For convenience
 | ||||
|   var ejs = ejsResource(config.elasticsearch);   | ||||
|   var _f = dashboard.current.services.filter; | ||||
| 
 | ||||
|   // Save a reference to this
 | ||||
|   var self = this; | ||||
| 
 | ||||
|   // Accessors
 | ||||
|   this.list = dashboard.current.services.filter.list; | ||||
|   this.ids = dashboard.current.services.filter.ids; | ||||
| 
 | ||||
|   // This is used both for adding filters and modifying them. 
 | ||||
|   // If an id is passed, the filter at that id is updated
 | ||||
|   this.set = function(filter,id) { | ||||
|     _.defaults(filter,{mandate:'must'}) | ||||
|     if(!_.isUndefined(id)) { | ||||
|       if(!_.isUndefined(self.list[id])) { | ||||
|         _.extend(self.list[id],filter); | ||||
|         return id; | ||||
|       } else { | ||||
|         return false; | ||||
|       } | ||||
|     } else { | ||||
|       if(_.isUndefined(filter.type)) { | ||||
|         return false; | ||||
|       } else { | ||||
|         var _id = nextId(); | ||||
|         var _filter = { | ||||
|           alias: '', | ||||
|           id: _id | ||||
|         } | ||||
|         _.defaults(filter,_filter) | ||||
|         self.list[_id] = filter; | ||||
|         self.ids.push(_id) | ||||
|         return _id; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   this.getBoolFilter = function(ids) { | ||||
|     // A default match all filter, just in case there are no other filters
 | ||||
|     var bool = ejs.BoolFilter().must(ejs.MatchAllFilter()); | ||||
|     _.each(ids,function(id) { | ||||
|       switch(self.list[id].mandate)  | ||||
|       { | ||||
|       case 'mustNot': | ||||
|         bool = bool.mustNot(self.getEjsObj(id)); | ||||
|         break; | ||||
|       case 'should': | ||||
|         bool = bool.should(self.getEjsObj(id)); | ||||
|         break; | ||||
|       default: | ||||
|         bool = bool.must(self.getEjsObj(id)); | ||||
|       } | ||||
|     }) | ||||
|     return bool; | ||||
|   } | ||||
| 
 | ||||
|   this.getEjsObj = function(id) { | ||||
|     return self.toEjsObj(self.list[id]) | ||||
|   } | ||||
| 
 | ||||
|   this.toEjsObj = function (filter) { | ||||
|     switch(filter.type) | ||||
|     { | ||||
|     case 'time': | ||||
|       return ejs.RangeFilter(filter.field) | ||||
|         .from(filter.from) | ||||
|         .to(filter.to) | ||||
|       break; | ||||
|     case 'range': | ||||
|       return ejs.RangeFilter(filter.field) | ||||
|         .from(filter.from) | ||||
|         .to(filter.to) | ||||
|       break; | ||||
|     case 'querystring': | ||||
|       console.log(filter.query) | ||||
|       return ejs.QueryFilter(ejs.QueryStringQuery(filter.query)) | ||||
|       break; | ||||
|     case 'terms': | ||||
|       return ejs.TermsFilter(filter.field,filter.value) | ||||
|       break; | ||||
|     case 'exists': | ||||
|       return ejs.ExistsFilter(filter.field) | ||||
|       break; | ||||
|     case 'missing': | ||||
|       return ejs.MissingFilter(filter.field) | ||||
|       break; | ||||
|     default: | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   this.getByType = function(type) { | ||||
|     return _.pick(self.list,self.idsByType(type)) | ||||
|   } | ||||
| 
 | ||||
|   this.removeByType = function(type) { | ||||
|     var ids = self.idsByType(type) | ||||
|     _.each(ids,function(id) { | ||||
|       self.remove(id) | ||||
|     }) | ||||
|     return ids; | ||||
|   } | ||||
| 
 | ||||
|   this.idsByType = function(type) { | ||||
|     return _.pluck(_.where(self.list,{type:type}),'id') | ||||
|   } | ||||
| 
 | ||||
|   // This special function looks for all time filters, and returns a time range according to the mode
 | ||||
|   this.timeRange = function(mode) { | ||||
|     var _t = _.where(self.list,{type:'time'}) | ||||
|     if(_t.length == 0) { | ||||
|       return false; | ||||
|     } | ||||
|     switch(mode) { | ||||
|     case "min": | ||||
|       return { | ||||
|         from: new Date(_.max(_.pluck(_t,'from'))), | ||||
|         to: new Date(_.min(_.pluck(_t,'to'))) | ||||
|       } | ||||
|       break; | ||||
|     case "max": | ||||
|       return { | ||||
|         from: new Date(_.min(_.pluck(_t,'from'))), | ||||
|         to: new Date(_.max(_.pluck(_t,'to'))) | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|   }  | ||||
| 
 | ||||
|   this.remove = function(id) { | ||||
|     if(!_.isUndefined(self.list[id])) { | ||||
|       delete self.list[id]; | ||||
|       // This must happen on the full path also since _.without returns a copy
 | ||||
|       self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id) | ||||
|       _f.idQueue.unshift(id) | ||||
|       _f.idQueue.sort(function(a,b){return a-b}); | ||||
|       return true; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   var nextId = function() { | ||||
|     if(_f.idQueue.length > 0) { | ||||
|       return _f.idQueue.shift() | ||||
|     } else { | ||||
|       return self.ids.length; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| }) | ||||
| .service('dashboard', function($routeParams, $http, $rootScope, $injector, ejsResource, timer, kbnIndex) { | ||||
|   // A hash of defaults to use when loading a dashboard
 | ||||
| 
 | ||||
|   var _dash = { | ||||
|     title: "", | ||||
|     editable: true, | ||||
|     rows: [], | ||||
|     services: {} | ||||
|     services: {}, | ||||
|     index: { | ||||
|       interval: 'none', | ||||
|       pattern: '_all', | ||||
|       default: '_all' | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
|   // An elasticJS client to use
 | ||||
|  | @ -288,9 +464,11 @@ angular.module('kibana.services', []) | |||
|   // Empty dashboard object
 | ||||
|   this.current = {}; | ||||
|   this.last = {}; | ||||
|   this.indices = []; | ||||
| 
 | ||||
|   // Store a reference to this
 | ||||
|   var self = this; | ||||
|   var filterSrv,query; | ||||
| 
 | ||||
|   $rootScope.$on('$routeChangeSuccess',function(){ | ||||
|     route(); | ||||
|  | @ -326,6 +504,33 @@ angular.module('kibana.services', []) | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Since the dashboard is responsible for index computation, we can compute and assign the indices 
 | ||||
|   // here before telling the panels to refresh
 | ||||
|   this.refresh = function() { | ||||
|     if(self.current.index.interval !== 'none') { | ||||
|       if(filterSrv.idsByType('time').length > 0) { | ||||
|         var _range = filterSrv.timeRange('min'); | ||||
|         kbnIndex.indices(_range.from,_range.to, | ||||
|           self.current.index.pattern,self.current.index.interval | ||||
|         ).then(function (p) { | ||||
|           if(p.length > 0) { | ||||
|             self.indices = p;           | ||||
|           } else { | ||||
|             self.indices = [self.current.index.default] | ||||
|           } | ||||
|           $rootScope.$broadcast('refresh') | ||||
|         }); | ||||
|       } else { | ||||
|         // This is not optimal, we should be getting the entire index list here, or at least every
 | ||||
|         // index that possibly matches the pattern
 | ||||
|         self.indices = [self.current.index.default] | ||||
|       } | ||||
|     } else { | ||||
|       self.indices = [self.current.index.pattern] | ||||
|       $rootScope.$broadcast('refresh') | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   this.to_file = function() { | ||||
|     var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"}); | ||||
|     // from filesaver.js
 | ||||
|  | @ -422,8 +627,6 @@ angular.module('kibana.services', []) | |||
|         return false; | ||||
|       } | ||||
|     ); | ||||
| 
 | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   this.elasticsearch_delete = function(id) { | ||||
|  | @ -497,8 +700,18 @@ angular.module('kibana.services', []) | |||
|   } | ||||
| 
 | ||||
|   this.dash_load = function(dashboard) { | ||||
| 
 | ||||
|     if(dashboard.index.interval === 'none') { | ||||
|       self.indices = [dashboard.index.pattern] | ||||
|     } | ||||
| 
 | ||||
|     self.current = dashboard; | ||||
| 
 | ||||
|     timer.cancel_all(); | ||||
| 
 | ||||
|     // Ok, now that we've setup the current dashboard, we can inject our services
 | ||||
|     query = $injector.get('query'); | ||||
|     filterSrv = $injector.get('filterSrv') | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,17 +12,10 @@ | |||
|   * field :: field containing a 2 element array in the format [lon,lat] | ||||
|   * tooltip :: field to extract the tool tip value from | ||||
|   * spyable :: Show the 'eye' icon that reveals the last ES query | ||||
| 
 | ||||
|   ### Group Events | ||||
|   #### Sends | ||||
|   * get_time :: On panel initialization get time range to query | ||||
|   #### Receives | ||||
|   * time :: An object containing the time range to use and the index(es) to query | ||||
|   * query :: An Array of queries, this panel uses only the first one | ||||
| */ | ||||
| 
 | ||||
| angular.module('kibana.bettermap', []) | ||||
| .controller('bettermap', function($scope, eventBus, query) { | ||||
| .controller('bettermap', function($scope, eventBus, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -37,50 +30,53 @@ angular.module('kibana.bettermap', []) | |||
|   _.defaults($scope.panel,_d) | ||||
| 
 | ||||
|   $scope.init = function() { | ||||
| 
 | ||||
|     $scope.$on('refresh',function(){ | ||||
|       $scope.get_data(); | ||||
|     }) | ||||
| 
 | ||||
|     eventBus.register($scope,'time', function(event,time){set_time(time)}); | ||||
| 
 | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
| $scope.get_data = function(segment,query_id) { | ||||
|   $scope.get_data = function(segment,query_id) { | ||||
|     $scope.panel.error =  false; | ||||
| 
 | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|       return | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     if(_.isUndefined($scope.panel.field)) { | ||||
|       $scope.panel.error = "Please select a field that contains geo point in [lon,lat] format" | ||||
|       return | ||||
|     } | ||||
|      | ||||
|     //$scope.panel.loading = true;
 | ||||
|     // Determine the field to sort on
 | ||||
|     var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field')) | ||||
|     if(timeField.length > 1) { | ||||
|       $scope.panel.error = "Time field must be consistent amongst time filters" | ||||
|     } else if(timeField.length == 0) { | ||||
|       timeField = null; | ||||
|     } else { | ||||
|       timeField = timeField[0] | ||||
|     } | ||||
| 
 | ||||
|     var _segment = _.isUndefined(segment) ? 0 : segment | ||||
|     $scope.segment = _segment; | ||||
| 
 | ||||
|     var boolQuery = ejs.BoolQuery(); | ||||
|     _.each(query.list,function(q) { | ||||
|       boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*') + " AND _exists_:"+$scope.panel.field)) | ||||
|       boolQuery = boolQuery.should(ejs.QueryStringQuery((q.query || '*'))) | ||||
|     }) | ||||
| 
 | ||||
|     var request = $scope.ejs.Request().indices($scope.index[_segment]) | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices[_segment]) | ||||
|       .query(ejs.FilteredQuery( | ||||
|         boolQuery, | ||||
|         ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to) | ||||
|         ) | ||||
|       ) | ||||
|         filterSrv.getBoolFilter(filterSrv.ids).must(ejs.ExistsFilter($scope.panel.field)) | ||||
|       )) | ||||
|       .fields([$scope.panel.field,$scope.panel.tooltip]) | ||||
|       .size($scope.panel.size) | ||||
|       .sort($scope.time.field,'desc'); | ||||
| 
 | ||||
|     if(!_.isNull(timeField)) { | ||||
|       request = request.sort(timeField,'desc'); | ||||
|     } | ||||
| 
 | ||||
|     $scope.populate_modal(request) | ||||
| 
 | ||||
|  | @ -125,7 +121,7 @@ $scope.get_data = function(segment,query_id) { | |||
|       $scope.$emit('draw') | ||||
| 
 | ||||
|       // Get $size results then stop querying
 | ||||
|       if($scope.data.length < $scope.panel.size && _segment+1 < $scope.index.length) | ||||
|       if($scope.data.length < $scope.panel.size && _segment+1 < dashboard.indices.length) | ||||
|         $scope.get_data(_segment+1,$scope.query_id) | ||||
| 
 | ||||
|     }); | ||||
|  | @ -136,18 +132,12 @@ $scope.get_data = function(segment,query_id) { | |||
|     $scope.modal = { | ||||
|       title: "Inspector", | ||||
|       body : "<h5>Last Elasticsearch Query</h5><pre>"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+ | ||||
|           angular.toJson(JSON.parse(request.toString()),true)+ | ||||
|         "'</pre>",  | ||||
|     }  | ||||
|   } | ||||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
| }) | ||||
| .directive('bettermap', function() { | ||||
|   return { | ||||
|  |  | |||
|  | @ -1,22 +1,26 @@ | |||
| <div> | ||||
|   <div class="row-fluid">     | ||||
|     <div class="span12"> | ||||
|       The derive queries panel takes a query and a field, then runs a terms facet against both and generates a list of terms to query on. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p> | ||||
|       Query Mode allows to optionally append original query to each term in the list. | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row-fluid"> | ||||
|     <div class="span1"> | ||||
|       <label class="small">Length</label> | ||||
|       <input type="number" style="width:80%" ng-model="panel.size" ng-change="set_refresh(true)"> | ||||
|     </div> | ||||
|     <div class="span3"> | ||||
|       <label class="small">Field</label> | ||||
|       <input type="text" bs-typeahead="fields.list" style="width:80%" ng-change="set_refresh(true)" ng-model='panel.field'></select> | ||||
|     </div> | ||||
|     <div class="span3"> | ||||
|       <label class="small">Query Mode</label> | ||||
|       <select style="width:80%" ng-change="set_refresh(true)" ng-model='panel.mode' ng-options="f for f in ['terms only','AND', 'OR']"></select> | ||||
|     </div> | ||||
|     <div class="span8"> | ||||
|     <div class="span5"> | ||||
|       <label class="small">Exclude Terms(s) (comma seperated)</label> | ||||
|       <input array-join type="text" style="width:90%" ng-change="set_refresh(true)" ng-model='panel.exclude'></input> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row-fluid">     | ||||
|     <div class="span12"> | ||||
|       The derive queries panel takes a query and a field, runs a terms facet, then creates queries based on them. For example, you might want to see a histogram of the top 5 requests that return a 404. <strong>You should be careful not to select a high cardinality field</strong> as Elasticsearch must load all of these values into memory.<p> | ||||
|       Query Mode allows to optionally append original query to each term in the list. | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  |  | |||
|  | @ -1,8 +1,23 @@ | |||
| <kibana-panel ng-controller='derivequeries' ng-init="init()"> | ||||
| <style> | ||||
|   .end-derive { | ||||
|     position:absolute; | ||||
|     right:15px; | ||||
|     top:5px; | ||||
|   } | ||||
|   .panel-derive { | ||||
|     padding-right: 35px !important; | ||||
|     height: 31px !important; | ||||
|     -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ | ||||
|     -moz-box-sizing: border-box;    /* Firefox, other Gecko */ | ||||
|     box-sizing: border-box;         /* Opera/IE 8+ */ | ||||
|   } | ||||
| </style> | ||||
|   <span ng-show='panel.spyable' style="position:absolute;right:0px;top:0px" class='panelextra pointer'> | ||||
|       <i bs-modal="'partials/modal.html'" class="icon-eye-open"></i> | ||||
|   </span> | ||||
|   <div ng-show="!panel.multi"> | ||||
|   <!-- | ||||
|   <div> | ||||
|     <form> | ||||
|       <table class="form-horizontal"> | ||||
|         <tr> | ||||
|  | @ -29,4 +44,16 @@ | |||
|       </table> | ||||
|     </form> | ||||
|   </div> | ||||
|   --> | ||||
| 
 | ||||
| 
 | ||||
|   <label class="small">Create new queries from <strong>{{panel.field}}</strong> ({{panel.mode}} mode)</label> | ||||
|   <div> | ||||
|     <form class="form-search" style="position:relative" ng-submit="get_data()"> | ||||
|       <input class="search-query panel-derive input-block-level" bs-typeahead="panel.history" data-min-length=0 data-items=100 type="text" ng-model="panel.query"/> | ||||
|       <span class="end-derive"> | ||||
|         <i class="icon-search pointer" ng-click="get_data()"></i> | ||||
|       </span | ||||
|     </form> | ||||
|   </div> | ||||
| </kibana-panel> | ||||
|  | @ -1,6 +1,6 @@ | |||
| /* | ||||
| 
 | ||||
|   ## Termsquery | ||||
|   ## Derivequeries | ||||
| 
 | ||||
|   Broadcasts an array of queries based on the results of a terms facet | ||||
| 
 | ||||
|  | @ -23,13 +23,15 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.derivequeries', []) | ||||
| .controller('derivequeries', function($scope, eventBus) { | ||||
| .controller('derivequeries', function($scope, $rootScope, query, eventBus, fields, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|     loading : false, | ||||
|     status  : "Beta", | ||||
|     label   : "Search", | ||||
|     query   : "*", | ||||
|     ids     : [], | ||||
|     group   : "default", | ||||
|     field   : '_type', | ||||
|     fields  : [], | ||||
|  | @ -43,26 +45,19 @@ angular.module('kibana.derivequeries', []) | |||
|   _.defaults($scope.panel,_d); | ||||
| 
 | ||||
|   $scope.init = function() { | ||||
|     eventBus.register($scope,'fields', function(event, fields) { | ||||
|       $scope.panel.fields = fields.all; | ||||
|     }); | ||||
|     eventBus.register($scope,'time', function(event,time){set_time(time)}); | ||||
|     eventBus.register($scope,'query', function(event, query) { | ||||
|       $scope.panel.query = _.isArray(query) ? query[0] : query; | ||||
|       $scope.get_data(); | ||||
|     }); | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|     $scope.panel.fields = fields.list | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function() { | ||||
|     update_history($scope.panel.query); | ||||
|      | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     $scope.panel.loading = true; | ||||
|     var request = $scope.ejs.Request().indices($scope.index); | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices); | ||||
| 
 | ||||
|     // Terms mode
 | ||||
|     request = request | ||||
|  | @ -73,9 +68,7 @@ angular.module('kibana.derivequeries', []) | |||
|         .facetFilter(ejs.QueryFilter( | ||||
|           ejs.FilteredQuery( | ||||
|             ejs.QueryStringQuery($scope.panel.query || '*'), | ||||
|             ejs.RangeFilter($scope.time.field) | ||||
|               .from($scope.time.from) | ||||
|               .to($scope.time.to) | ||||
|             filterSrv.getBoolFilter(filterSrv.ids) | ||||
|             )))).size(0) | ||||
| 
 | ||||
|     $scope.populate_modal(request); | ||||
|  | @ -93,10 +86,22 @@ angular.module('kibana.derivequeries', []) | |||
|       } else if ($scope.panel.mode === 'OR') { | ||||
|         var suffix = ' OR (' + $scope.panel.query + ')'; | ||||
|       } | ||||
|       var ids = []; | ||||
|       _.each(results.facets.query.terms, function(v) { | ||||
|         data.push($scope.panel.field+':"'+v.term+'"'+suffix) | ||||
|         var _q = $scope.panel.field+':"'+v.term+'"'+suffix; | ||||
|         // if it isn't in the list, remove it
 | ||||
|         var _iq = query.findQuery(_q) | ||||
|         if(!_iq) { | ||||
|           ids.push(query.set({query:_q})); | ||||
|         } else { | ||||
|           ids.push(_iq.id); | ||||
|         } | ||||
|       }); | ||||
|       $scope.send_query(data) | ||||
|       _.each(_.difference($scope.panel.ids,ids),function(id){ | ||||
|         query.remove(id) | ||||
|       }) | ||||
|       $scope.panel.ids = ids; | ||||
|       dashboard.refresh(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|  | @ -114,23 +119,12 @@ angular.module('kibana.derivequeries', []) | |||
|     $scope.modal = { | ||||
|       title: "Inspector", | ||||
|       body : "<h5>Last Elasticsearch Query</h5><pre>"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+ | ||||
|           angular.toJson(JSON.parse(request.toString()),true)+ | ||||
|         "'</pre>",  | ||||
|     }  | ||||
|   } | ||||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.send_query = function(query) { | ||||
|     var _query = _.isArray(query) ? query : [query] | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'query',_query) | ||||
|   } | ||||
| 
 | ||||
|   var update_history = function(query) { | ||||
|     query = _.isArray(query) ? query : [query]; | ||||
|     if($scope.panel.remember > 0) { | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| <a class="close" ng-click="dismiss()" href="">×</a> | ||||
| <h4> | ||||
|   Micro Analysis of {{micropanel.field}}  | ||||
|   <i class="pointer icon-search" ng-click="build_search('_exists_',micropanel.field);dismiss();"></i> | ||||
|   <i class="pointer icon-ban-circle" ng-click="build_search('_missing_',micropanel.field);dismiss();"></i> | ||||
|   <i class="pointer icon-search" ng-click="fieldExists(micropanel.field,'exists');dismiss();"></i> | ||||
|   <i class="pointer icon-ban-circle" ng-click="fieldExists(micropanel.field,'missing');dismiss();"></i> | ||||
|   <br><small>{{micropanel.count}} events in the table set</small> | ||||
| </h4> | ||||
| <table style="width:480px" class='table table-bordered table-striped table-condensed'> | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ | |||
| 
 | ||||
| */ | ||||
| angular.module('kibana.fields', []) | ||||
| .controller('fields', function($scope, eventBus, $timeout) { | ||||
| .controller('fields', function($scope, eventBus, $timeout, dashboard, query, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -79,9 +79,15 @@ angular.module('kibana.fields', []) | |||
|     eventBus.broadcast($scope.$id,$scope.panel.group,"selected_fields",$scope.active) | ||||
|   } | ||||
| 
 | ||||
|   $scope.build_search = function(field, value,negate) { | ||||
|     $scope.panel.query = [add_to_query($scope.panel.query,field,value,negate)] | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query); | ||||
|   $scope.build_search = function(field,value,negate) { | ||||
|     var query = (negate ? '-':'+')+field+":\""+value+"\"" | ||||
|     filterSrv.set({type:'querystring',query:query}) | ||||
|     dashboard.refresh(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.fieldExists = function(field,mode) { | ||||
|     filterSrv.set({type:mode,field:field}) | ||||
|     dashboard.refresh(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.is_active = function(field) { | ||||
|  |  | |||
|  | @ -4,13 +4,19 @@ | |||
|       <label class="small">Mode</label>  | ||||
|       <select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select> | ||||
|     </div> | ||||
|     <div class="span3" ng-show="panel.mode != 'count'"> | ||||
|       <label class="small">Field</label> | ||||
|     <div class="span2"> | ||||
|       <label class="small">Time Field</label> | ||||
|       <form> | ||||
|         <input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.time_field"> | ||||
|       </form> | ||||
|     </div> | ||||
|     <div class="span2" ng-show="panel.mode != 'count'"> | ||||
|       <label class="small">Value Field</label> | ||||
|       <form> | ||||
|         <input ng-change="set_refresh(true)" placeholder="Start typing" bs-typeahead="fields.list" type="text" class="input-small" ng-model="panel.value_field"> | ||||
|       </form> | ||||
|     </div> | ||||
|     <div class="span5" ng-show="panel.mode != 'count'"> | ||||
|     <div class="span3" ng-show="panel.mode != 'count'"> | ||||
|       <label class="small">Note</label><small> In <strong>{{panel.mode}}</strong> mode the configured field <strong>must</strong> be a numeric type</small> | ||||
|     </div> | ||||
|   </div> | ||||
|  |  | |||
|  | @ -22,12 +22,12 @@ | |||
|   </span> | ||||
|   <div> | ||||
|   <span ng-show='panel.zoomlinks && data'> | ||||
|     <a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a> | ||||
|     <!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>--> | ||||
|     <a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |  | ||||
|   </span> | ||||
|   <span ng-show="panel.legend" ng-repeat='series in plot.getData()' class="histogram-legend"> | ||||
|     <div class="histogram-legend-dot" style="background:{{series.color}};"></div> | ||||
|     <div class='small histogram-legend-item'>{{series.label}} ({{series.hits}})</div> | ||||
|   <span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend"> | ||||
|     <div class="histogram-legend-dot" style="background:{{series.info.color}};"></div> | ||||
|     <div class='small histogram-legend-item'>{{series.info.alias}} ({{series.hits}})</div> | ||||
|   </span> | ||||
|   <span ng-show="panel.legend" class="small"><span ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> hits)</span> | ||||
|   </div> | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.histogram', []) | ||||
| .controller('histogram', function($scope, eventBus,query) { | ||||
| .controller('histogram', function($scope, eventBus, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -50,6 +50,7 @@ angular.module('kibana.histogram', []) | |||
|     group       : "default", | ||||
|     query       : [ {query: "*", label:"Query"} ], | ||||
|     mode        : 'count', | ||||
|     time_field  : '@timestamp', | ||||
|     value_field : null, | ||||
|     auto_int    : true, | ||||
|     resolution  : 100,  | ||||
|  | @ -75,50 +76,46 @@ angular.module('kibana.histogram', []) | |||
| 
 | ||||
|     $scope.queries = query; | ||||
| 
 | ||||
|     eventBus.register($scope,'time', function(event,time){$scope.set_time(time)}); | ||||
| 
 | ||||
|     $scope.$on('refresh',function(){ | ||||
|       $scope.get_data(); | ||||
|     }) | ||||
| 
 | ||||
|     // Now that we're all setup, request the time from our group if we don't 
 | ||||
|     // have it yet
 | ||||
|     if(_.isUndefined($scope.time)) | ||||
|       eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function(segment,query_id) { | ||||
|     delete $scope.panel.error | ||||
| 
 | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     var _range = $scope.range = filterSrv.timeRange('min'); | ||||
|      | ||||
|     if ($scope.panel.auto_int) | ||||
|       $scope.panel.interval = secondsToHms(calculate_interval($scope.time.from,$scope.time.to,$scope.panel.resolution,0)/1000); | ||||
|       $scope.panel.interval = secondsToHms(calculate_interval(_range.from,_range.to,$scope.panel.resolution,0)/1000); | ||||
|      | ||||
|     $scope.panel.loading = true; | ||||
|     var _segment = _.isUndefined(segment) ? 0 : segment | ||||
|     var request = $scope.ejs.Request().indices($scope.index[_segment]); | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices[_segment]); | ||||
| 
 | ||||
|     // Build the query
 | ||||
|     _.each($scope.queries.ids, function(id) { | ||||
|       var query = $scope.ejs.FilteredQuery( | ||||
|         ejs.QueryStringQuery($scope.queries.list[id].query || '*'), | ||||
|         ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to) | ||||
|         filterSrv.getBoolFilter(filterSrv.ids) | ||||
|       ) | ||||
| 
 | ||||
|       var facet = $scope.ejs.DateHistogramFacet(id) | ||||
|        | ||||
|       if($scope.panel.mode === 'count') { | ||||
|         facet = facet.field($scope.time.field) | ||||
|         facet = facet.field($scope.panel.time_field) | ||||
|       } else { | ||||
|         if(_.isNull($scope.panel.value_field)) { | ||||
|           $scope.panel.error = "In " + $scope.panel.mode + " mode a field must be specified"; | ||||
|           return | ||||
|         } | ||||
|         facet = facet.keyField($scope.time.field).valueField($scope.panel.value_field) | ||||
|         facet = facet.keyField($scope.panel.time_field).valueField($scope.panel.value_field) | ||||
|       } | ||||
|       facet = facet.interval($scope.panel.interval).facetFilter($scope.ejs.QueryFilter(query)) | ||||
|       request = request.facet(facet).size(0) | ||||
|  | @ -132,6 +129,7 @@ angular.module('kibana.histogram', []) | |||
| 
 | ||||
|     // Populate scope when we have results
 | ||||
|     results.then(function(results) { | ||||
| 
 | ||||
|       $scope.panel.loading = false; | ||||
|       if(_segment == 0) { | ||||
|         $scope.hits = 0; | ||||
|  | @ -145,15 +143,21 @@ angular.module('kibana.histogram', []) | |||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       // Make sure we're still on the same query
 | ||||
|       if($scope.query_id === query_id) { | ||||
|       // Convert facet ids to numbers
 | ||||
|       var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);}) | ||||
| 
 | ||||
|       // Make sure we're still on the same query/queries
 | ||||
|       if($scope.query_id === query_id &&  | ||||
|         _.intersection(facetIds,query.ids).length == query.ids.length | ||||
|         ) { | ||||
| 
 | ||||
|         var i = 0; | ||||
|         _.each(results.facets, function(v, id) { | ||||
|         _.each(query.ids, function(id) { | ||||
|           var v = results.facets[id]; | ||||
| 
 | ||||
|           // Null values at each end of the time range ensure we see entire range
 | ||||
|           if(_.isUndefined($scope.data[i]) || _segment == 0) { | ||||
|             var data = [[$scope.time.from.getTime(), null],[$scope.time.to.getTime(), null]]; | ||||
|             var data = [[_range.from.getTime(), null],[_range.to.getTime(), null]]; | ||||
|             var hits = 0; | ||||
|           } else { | ||||
|             var data = $scope.data[i].data | ||||
|  | @ -172,7 +176,7 @@ angular.module('kibana.histogram', []) | |||
|           // Create the flot series object
 | ||||
|           var series = {  | ||||
|             data: { | ||||
|               id: id, | ||||
|               info: $scope.queries.list[id], | ||||
|               data: data, | ||||
|               hits: hits | ||||
|             }, | ||||
|  | @ -187,7 +191,7 @@ angular.module('kibana.histogram', []) | |||
|         $scope.$emit('render') | ||||
| 
 | ||||
|         // If we still have segments left, get them
 | ||||
|         if(_segment < $scope.index.length-1) { | ||||
|         if(_segment < dashboard.indices.length-1) { | ||||
|           $scope.get_data(_segment+1,query_id) | ||||
|         } | ||||
|        | ||||
|  | @ -198,7 +202,32 @@ angular.module('kibana.histogram', []) | |||
|   // function $scope.zoom
 | ||||
|   // factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
 | ||||
|   $scope.zoom = function(factor) { | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'zoom',factor); | ||||
|     var _now = Date.now(); | ||||
|     var _range = filterSrv.timeRange('min'); | ||||
|     var _timespan = (_range.to.valueOf() - _range.from.valueOf()); | ||||
|     var _center = _range.to.valueOf() - _timespan/2 | ||||
| 
 | ||||
|     var _to = (_center + (_timespan*factor)/2) | ||||
|     var _from = (_center - (_timespan*factor)/2) | ||||
| 
 | ||||
|     // If we're not already looking into the future, don't.
 | ||||
|     if(_to > Date.now() && _range.to < Date.now()) { | ||||
|       var _offset = _to - Date.now() | ||||
|       _from = _from - _offset | ||||
|       _to = Date.now(); | ||||
|     } | ||||
| 
 | ||||
|     if(factor > 1) { | ||||
|       filterSrv.removeByType('time') | ||||
|     } | ||||
|     filterSrv.set({ | ||||
|       type:'time', | ||||
|       from:moment.utc(_from), | ||||
|       to:moment.utc(_to), | ||||
|       field:$scope.panel.time_field | ||||
|     }) | ||||
|     dashboard.refresh(); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   // I really don't like this function, too much dom manip. Break out into directive?
 | ||||
|  | @ -223,14 +252,8 @@ angular.module('kibana.histogram', []) | |||
|     $scope.$emit('render'); | ||||
|   } | ||||
| 
 | ||||
|   $scope.set_time = function(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = time.index || $scope.index     | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
| }) | ||||
| .directive('histogramChart', function(eventBus) { | ||||
| .directive('histogramChart', function(dashboard, eventBus, filterSrv, $rootScope) { | ||||
|   return { | ||||
|     restrict: 'A', | ||||
|     link: function(scope, elem, attrs, ctrl) { | ||||
|  | @ -249,10 +272,12 @@ angular.module('kibana.histogram', []) | |||
|       function render_panel() { | ||||
|   | ||||
|         // Populate from the query service
 | ||||
|         try { | ||||
|           _.each(scope.data,function(series) { | ||||
|           series.label = scope.queries.list[series.id].alias, | ||||
|           series.color = scope.queries.list[series.id].color | ||||
|             series.label = series.info.alias, | ||||
|             series.color = series.info.color | ||||
|           }) | ||||
|         } catch(e) {return} | ||||
| 
 | ||||
|         // Set barwidth based on specified interval
 | ||||
|         var barwidth = interval_to_seconds(scope.panel.interval)*1000 | ||||
|  | @ -366,9 +391,13 @@ angular.module('kibana.histogram', []) | |||
|       }); | ||||
| 
 | ||||
|       elem.bind("plotselected", function (event, ranges) { | ||||
|         scope.time.from = moment(ranges.xaxis.from); | ||||
|         scope.time.to   = moment(ranges.xaxis.to) | ||||
|         eventBus.broadcast(scope.$id,scope.panel.group,'set_time',scope.time) | ||||
|         var _id = filterSrv.set({ | ||||
|           type  : 'time', | ||||
|           from  : moment.utc(ranges.xaxis.from), | ||||
|           to    : moment.utc(ranges.xaxis.to), | ||||
|           field : scope.panel.time_field | ||||
|         }) | ||||
|         dashboard.refresh(); | ||||
|       }); | ||||
|     } | ||||
|   }; | ||||
|  |  | |||
|  | @ -3,14 +3,14 @@ | |||
|   <div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'> | ||||
|     <!-- vertical legend --> | ||||
|     <table class="small" ng-show="panel.arrangement == 'vertical'">   | ||||
|       <tr ng-repeat="query in plot.getData()"> | ||||
|         <td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td> | ||||
|       <tr ng-repeat="query in data"> | ||||
|         <td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td> | ||||
|       </tr> | ||||
|     </table> | ||||
| 
 | ||||
|     <!-- horizontal legend --> | ||||
|     <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;"> | ||||
|      <span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span> | ||||
|     <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;"> | ||||
|      <span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span> | ||||
|     </div><br> | ||||
| 
 | ||||
|   </div> | ||||
|  | @ -20,23 +20,26 @@ | |||
|   <div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div> | ||||
| 
 | ||||
|   <div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'> | ||||
| 
 | ||||
|     <!-- vertical legend --> | ||||
|     <table class="small" ng-show="panel.arrangement == 'vertical'">   | ||||
|       <tr ng-repeat="query in plot.getData()"> | ||||
|         <td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td> | ||||
|       <tr ng-repeat="query in data"> | ||||
|         <td><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.info.alias}}</td><td>{{query.data[0][1]}}</td> | ||||
|       </tr> | ||||
|     </table> | ||||
| 
 | ||||
|     <!-- horizontal legend --> | ||||
|     <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;"> | ||||
|      <span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span> | ||||
|     <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in data" style="float:left;padding-left: 10px;"> | ||||
|      <span><div style="display:inline-block;border-radius:5px;background:{{query.info.color}};height:10px;width:10px"></div></span> {{query.info.alias}} ({{query.data[0][1]}}) </span> | ||||
|     </div><br> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|   <div ng-show="panel.chart == 'total'"><div ng-style="panel.style" style="line-height:{{panel.style['font-size']}}">{{hits}}</div></div> | ||||
| 
 | ||||
|   <span ng-show="panel.chart == 'list'"><span ng-style="panel.style" style="line-height:{{panel.style['font-size']}}" ng-repeat="query in data">{{query.label}} ({{query.hits}})<span></span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'"> | ||||
|   <span ng-show="panel.chart == 'list'"> | ||||
|     <div ng-style="panel.style" style="display:inline-block;line-height:{{panel.style['font-size']}}" ng-repeat="query in data"> | ||||
|       <i class="icon-circle" style="color:{{query.info.color}}"></i> {{query.info.alias}} ({{query.hits}}) | ||||
|     </div> | ||||
|   </span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'"> | ||||
| 
 | ||||
| </kibana-panel>          | ||||
|  | @ -22,7 +22,7 @@ | |||
| 
 | ||||
| */ | ||||
| angular.module('kibana.hits', []) | ||||
| .controller('hits', function($scope, eventBus, query) { | ||||
| .controller('hits', function($scope, eventBus, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -30,8 +30,8 @@ angular.module('kibana.hits', []) | |||
|     query   : ["*"], | ||||
|     group   : "default", | ||||
|     style   : { "font-size": '10pt'}, | ||||
|     arrangement : 'vertical', | ||||
|     chart       : 'none', | ||||
|     arrangement : 'horizontal', | ||||
|     chart       : 'bar', | ||||
|     counter_pos : 'above', | ||||
|     donut   : false, | ||||
|     tilt    : false, | ||||
|  | @ -40,22 +40,13 @@ angular.module('kibana.hits', []) | |||
|   _.defaults($scope.panel,_d) | ||||
| 
 | ||||
|   $scope.init = function () { | ||||
|     $scope.queries = query; | ||||
| 
 | ||||
|     $scope.hits = 0; | ||||
|     eventBus.register($scope,'time', function(event,time){ | ||||
|       set_time(time) | ||||
|     }); | ||||
| 
 | ||||
|     | ||||
|     $scope.$on('refresh',function(){ | ||||
|       console.log($scope.queries) | ||||
|       console.log(query) | ||||
|       $scope.get_data(); | ||||
|     }) | ||||
|     $scope.get_data(); | ||||
| 
 | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function(segment,query_id) { | ||||
|  | @ -63,23 +54,22 @@ angular.module('kibana.hits', []) | |||
|     $scope.panel.loading = true; | ||||
| 
 | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     var _segment = _.isUndefined(segment) ? 0 : segment | ||||
|     var request = $scope.ejs.Request().indices($scope.index[_segment]); | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices[_segment]); | ||||
|      | ||||
|     // Build the question part of the query
 | ||||
|     _.each($scope.queries.ids, function(id) { | ||||
|       var query = $scope.ejs.FilteredQuery( | ||||
|         ejs.QueryStringQuery($scope.queries.list[id].query || '*'), | ||||
|         ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to)) | ||||
|     _.each(query.ids, function(id) { | ||||
|       var _q = $scope.ejs.FilteredQuery( | ||||
|         ejs.QueryStringQuery(query.list[id].query || '*'), | ||||
|         filterSrv.getBoolFilter(filterSrv.ids)); | ||||
|      | ||||
|       request = request | ||||
|         .facet($scope.ejs.QueryFacet(id) | ||||
|           .query(query) | ||||
|           .query(_q) | ||||
|         ).size(0) | ||||
|     }); | ||||
| 
 | ||||
|  | @ -91,7 +81,6 @@ angular.module('kibana.hits', []) | |||
| 
 | ||||
|     // Populate scope when we have results
 | ||||
|     results.then(function(results) { | ||||
| 
 | ||||
|       $scope.panel.loading = false; | ||||
|       if(_segment == 0) { | ||||
|         $scope.hits = 0; | ||||
|  | @ -104,16 +93,24 @@ angular.module('kibana.hits', []) | |||
|         $scope.panel.error = $scope.parse_error(results.error); | ||||
|         return; | ||||
|       } | ||||
|       if($scope.query_id === query_id) { | ||||
| 
 | ||||
|       // Convert facet ids to numbers
 | ||||
|       var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k);}) | ||||
| 
 | ||||
|       // Make sure we're still on the same query/queries
 | ||||
|       if($scope.query_id === query_id &&  | ||||
|         _.intersection(facetIds,query.ids).length == query.ids.length | ||||
|         ) { | ||||
|         var i = 0; | ||||
|         _.each(results.facets, function(v, id) { | ||||
|         _.each(query.ids, function(id) { | ||||
|           var v = results.facets[id] | ||||
|           var hits = _.isUndefined($scope.data[i]) || _segment == 0 ?  | ||||
|             v.count : $scope.data[i].hits+v.count | ||||
|           $scope.hits += v.count | ||||
| 
 | ||||
|           // Create series
 | ||||
|           $scope.data[i] = {  | ||||
|             //label: $scope.panel.query[i].label || "query"+(parseInt(i)+1), 
 | ||||
|             info: query.list[id], | ||||
|             id: id, | ||||
|             hits: hits, | ||||
|             data: [[i,hits]] | ||||
|  | @ -122,26 +119,13 @@ angular.module('kibana.hits', []) | |||
|           i++; | ||||
|         }); | ||||
|         $scope.$emit('render'); | ||||
|         if(_segment < $scope.index.length-1)  | ||||
|         if(_segment < dashboard.indices.length-1)  | ||||
|           $scope.get_data(_segment+1,query_id) | ||||
|          | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   $scope.remove_query = function(q) { | ||||
|     $scope.panel.query = _.without($scope.panel.query,q); | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.add_query = function(label,query) { | ||||
|     $scope.panel.query.unshift({ | ||||
|       query: query, | ||||
|       label: label,  | ||||
|     }); | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.set_refresh = function (state) {  | ||||
|     $scope.refresh = state;  | ||||
|   } | ||||
|  | @ -155,11 +139,10 @@ angular.module('kibana.hits', []) | |||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
| }).directive('hitsChart', function(eventBus) { | ||||
| }).directive('hitsChart', function(eventBus, query) { | ||||
|   return { | ||||
|     restrict: 'A', | ||||
|     link: function(scope, elem, attrs, ctrl) { | ||||
|  | @ -177,10 +160,12 @@ angular.module('kibana.hits', []) | |||
|       // Function for rendering panel
 | ||||
|       function render_panel() { | ||||
| 
 | ||||
|         try { | ||||
|           _.each(scope.data,function(series) { | ||||
|           series.label = scope.queries.list[series.id].alias, | ||||
|           series.color = scope.queries.list[series.id].color | ||||
|             series.label = series.info.alias, | ||||
|             series.color = series.info.color | ||||
|           }) | ||||
|         } catch(e) {return} | ||||
| 
 | ||||
|         var scripts = $LAB.script("common/lib/panels/jquery.flot.js").wait() | ||||
|                           .script("common/lib/panels/jquery.flot.pie.js") | ||||
|  | @ -201,13 +186,12 @@ angular.module('kibana.hits', []) | |||
|                 yaxis: { show: true, min: 0, color: "#c8c8c8" }, | ||||
|                 xaxis: { show: false }, | ||||
|                 grid: { | ||||
|                   backgroundColor: '#272b30', | ||||
|                   borderWidth: 0, | ||||
|                   borderColor: '#eee', | ||||
|                   color: "#eee", | ||||
|                   hoverable: true, | ||||
|                 }, | ||||
|                 colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D'] | ||||
|                 colors: query.colors | ||||
|               }) | ||||
|             if(scope.panel.chart === 'pie') | ||||
|               scope.plot = $.plot(elem, scope.data, { | ||||
|  | @ -223,7 +207,6 @@ angular.module('kibana.hits', []) | |||
|                       label: 'The Rest' | ||||
|                     }, | ||||
|                     stroke: { | ||||
|                       color: '#272b30', | ||||
|                       width: 0 | ||||
|                     }, | ||||
|                     label: {  | ||||
|  | @ -239,7 +222,7 @@ angular.module('kibana.hits', []) | |||
|                 }, | ||||
|                 //grid: { hoverable: true, clickable: true },
 | ||||
|                 grid:   { hoverable: true, clickable: true }, | ||||
|                 colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D'] | ||||
|                 colors: query.colors | ||||
|               }); | ||||
| 
 | ||||
|             // Compensate for the height of the  legend. Gross
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
|     .jvectormap-label { | ||||
|         position: absolute; | ||||
|         display: none; | ||||
|         visibility: hidden; | ||||
|         border: solid 1px #CDCDCD; | ||||
|         -webkit-border-radius: 3px; | ||||
|         -moz-border-radius: 3px; | ||||
|  | @ -30,6 +31,10 @@ | |||
|         text-align: center; | ||||
|     } | ||||
| 
 | ||||
|     .jvectormap { | ||||
|         position: relative; | ||||
|     } | ||||
| 
 | ||||
|     .jvectormap-zoomin { | ||||
|         display: none; | ||||
|         top: 10px; | ||||
|  | @ -39,9 +44,23 @@ | |||
|         display: none; | ||||
|         top: 30px; | ||||
|     } | ||||
| 
 | ||||
|     .map-legend { | ||||
|         color   : #c8c8c8; | ||||
|         padding : 10px; | ||||
|         font-size: 11pt; | ||||
|         font-weight: 200; | ||||
|         background-color: #1f1f1f; | ||||
|         border-radius: 5px; | ||||
|         position: absolute; | ||||
|         right: 0px; | ||||
|         top: 15px; | ||||
|         display: none; | ||||
|         z-index: 99; | ||||
|     } | ||||
|   </style> | ||||
|   <span ng-show="panel.spyable" class='spy panelextra pointer'> | ||||
|     <i bs-modal="'partials/modal.html'" class="icon-eye-open"></i> | ||||
|   </span> | ||||
|   <div map params="{{panel}}" style="height:{{panel.height || row.height}}"></div> | ||||
|   <div class="jvectormap" map params="{{panel}}" style="height:{{panel.height || row.height}}"></div> | ||||
| </kibana-panel> | ||||
|  | @ -28,7 +28,7 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.map', []) | ||||
| .controller('map', function($scope, eventBus) { | ||||
| .controller('map', function($scope, $rootScope, eventBus, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -45,22 +45,24 @@ angular.module('kibana.map', []) | |||
|   _.defaults($scope.panel,_d) | ||||
| 
 | ||||
|   $scope.init = function() { | ||||
|     eventBus.register($scope,'time', function(event,time){set_time(time)}); | ||||
|     eventBus.register($scope,'query', function(event, query) { | ||||
|       $scope.panel.query = _.isArray(query) ? query[0] : query; | ||||
|     $scope.$on('refresh',function(){$scope.get_data()}) | ||||
|     $scope.get_data(); | ||||
|     }); | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function() { | ||||
|      | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     $scope.panel.loading = true; | ||||
|     var request = $scope.ejs.Request().indices($scope.index); | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices); | ||||
| 
 | ||||
|     var boolQuery = ejs.BoolQuery(); | ||||
|     _.each(query.list,function(q) { | ||||
|       boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*')) | ||||
|     }) | ||||
| 
 | ||||
|     // Then the insert into facet and make the request
 | ||||
|     var request = request | ||||
|  | @ -70,10 +72,8 @@ angular.module('kibana.map', []) | |||
|         .exclude($scope.panel.exclude) | ||||
|         .facetFilter(ejs.QueryFilter( | ||||
|           ejs.FilteredQuery( | ||||
|             ejs.QueryStringQuery($scope.panel.query || '*'), | ||||
|             ejs.RangeFilter($scope.time.field) | ||||
|               .from($scope.time.from) | ||||
|               .to($scope.time.to) | ||||
|             boolQuery, | ||||
|             filterSrv.getBoolFilter(filterSrv.ids) | ||||
|             )))).size(0); | ||||
| 
 | ||||
|     $scope.populate_modal(request); | ||||
|  | @ -97,22 +97,17 @@ angular.module('kibana.map', []) | |||
|     $scope.modal = { | ||||
|       title: "Inspector", | ||||
|       body : "<h5>Last Elasticsearch Query</h5><pre>"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+ | ||||
|           angular.toJson(JSON.parse(request.toString()),true)+ | ||||
|         "'</pre>",  | ||||
|     }  | ||||
|   } | ||||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.build_search = function(field,value) { | ||||
|     $scope.panel.query = add_to_query($scope.panel.query,field,value,false) | ||||
|     $scope.get_data(); | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]); | ||||
|     _.each(query.list,function(q) { | ||||
|       q.query = add_to_query(q.query,field,value,false); | ||||
|     }) | ||||
|     dashboard.refresh(); | ||||
|   } | ||||
| 
 | ||||
| }) | ||||
|  | @ -155,20 +150,12 @@ angular.module('kibana.map', []) | |||
|               }] | ||||
|             }, | ||||
|             onRegionLabelShow: function(event, label, code){ | ||||
|               $('.jvectormap-label').css({ | ||||
|                 "position"    : "absolute", | ||||
|                 "display"     : "none", | ||||
|                 'color'       : "#c8c8c8", | ||||
|                 'padding'     : '10px', | ||||
|                 'font-size'   : '11pt', | ||||
|                 'font-weight' : 200, | ||||
|                 'background-color': '#1f1f1f', | ||||
|                 'border-radius': '5px' | ||||
|               }) | ||||
|               elem.children('.map-legend').show() | ||||
|               var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code]; | ||||
|               $('.jvectormap-label').text(label.text() + ": " + count); | ||||
|               elem.children('.map-legend').text(label.text() + ": " + count); | ||||
|             }, | ||||
|             onRegionOut: function(event, code) { | ||||
|               $('.map-legend').hide(); | ||||
|             }, | ||||
|             onRegionClick: function(event, code) { | ||||
|               var count = _.isUndefined(scope.data[code]) ? 0 : scope.data[code]; | ||||
|  | @ -176,6 +163,8 @@ angular.module('kibana.map', []) | |||
|                 scope.build_search(scope.panel.field,code) | ||||
|             } | ||||
|           }); | ||||
|           elem.prepend('<span class="map-legend"></span>'); | ||||
|           $('.map-legend').hide(); | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|  |  | |||
|  | @ -1,61 +1,39 @@ | |||
| <div class="row-fluid" ng-switch="panel.mode"> | ||||
| <div class="span3"> | ||||
| <div> | ||||
|   <div class="row-fluid" ng-switch="panel.mode"> | ||||
|     <div class="row-fluid"> | ||||
|       <div class="span2"> | ||||
|         <label class="small">Mode</label>  | ||||
|   <select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select> | ||||
| </div>  | ||||
|         <select class="input-small" ng-change="set_mode(panel.mode);set_refresh(true)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select> | ||||
|       </div>  | ||||
|     </div> | ||||
|     <div ng-switch-when="terms"> | ||||
|       <div class="row-fluid"> | ||||
|       <div class="span3"> | ||||
|         <form style="margin-bottom: 0px"> | ||||
|         <div class="span2"> | ||||
|           <label class="small">Field</label> | ||||
|           <input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field"> | ||||
|         </form> | ||||
|           <input type="text" class="input-small" bs-typeahead="fields.list" ng-model="panel.query.field" ng-change="set_refresh(true)"> | ||||
|         </div> | ||||
|       <div class="span5"> | ||||
|         <form class="input-append" style="margin-bottom: 0px"> | ||||
|           <label class="small">Query</label> | ||||
|           <input type="text" style="width:80%" ng-model="panel.query.query"> | ||||
|           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button> | ||||
|         </form> | ||||
|       </div> | ||||
|     </div>   | ||||
|     <div class="row-fluid"> | ||||
|       <div class="span3"> | ||||
|         <div class="span2"> | ||||
|           <label class="small">Length</label> | ||||
|         <input type="number" style="width:80%" ng-model="panel.size" ng-change="get_data()"> | ||||
|           <input class="input-small" type="number" ng-model="panel.size" ng-change="set_refresh(true)"> | ||||
|         </div> | ||||
|       <div class="span8"> | ||||
|         <form class="input-append" style="margin-bottom: 0px"> | ||||
|         <div class="span6"> | ||||
|           <label class="small">Exclude Terms(s) (comma seperated)</label> | ||||
|           <input array-join type="text" style="width:90%"  ng-model='panel.exclude'></input> | ||||
|           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button> | ||||
|         </form> | ||||
|           <input array-join type="text" ng-model='panel.exclude'></input> | ||||
|         </div> | ||||
|       </div>   | ||||
|     </div> | ||||
|     <div ng-switch-when="goal"> | ||||
|       <div class="row-fluid"> | ||||
|       <div class="span3"> | ||||
|         <label class="small">Mode</label>  | ||||
|         <select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select> | ||||
|       </div>    | ||||
|         <div class="span2"> | ||||
|           <form style="margin-bottom: 0px"> | ||||
|             <label class="small">Goal</label> | ||||
|           <input type="number" style="width:90%" ng-model="panel.query.goal"> | ||||
|         </form> | ||||
|       </div> | ||||
|       <div class="span5"> | ||||
|         <form class="input-append" style="margin-bottom: 0px"> | ||||
|           <label class="small">Query</label> | ||||
|           <input type="text" style="width:80%" ng-model="panel.query.query"> | ||||
|           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button> | ||||
|             <input type="number" style="width:90%" ng-model="panel.query.goal" ng-change="set_refresh(true)"> | ||||
|           </form> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
| </div> | ||||
| <div class="row-fluid">     | ||||
|   </div> | ||||
|   <div class="row-fluid">     | ||||
|     <div class="span1"> | ||||
|       <label class="small"> Donut </label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut"> | ||||
|     </div> | ||||
|  | @ -69,9 +47,9 @@ | |||
|       <label class="small">Legend</label>  | ||||
|       <select class="input-small" ng-model="panel.legend" ng-options="f for f in ['above','below','none']"></select></span> | ||||
|     </div> | ||||
| </div> | ||||
| <h5>Panel Spy</h5> | ||||
| <div class="row-fluid"> | ||||
|   </div> | ||||
|   <h5>Panel Spy</h5> | ||||
|   <div class="row-fluid"> | ||||
|     <div class="span2">  | ||||
|       <label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable"> | ||||
|     </div> | ||||
|  | @ -80,4 +58,5 @@ | |||
|       be accessed by clicking the <i class='icon-eye-open'></i> in the top right | ||||
|       of the panel. | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | @ -33,17 +33,17 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.pie', []) | ||||
| .controller('pie', function($scope, eventBus) { | ||||
| .controller('pie', function($scope, $rootScope, eventBus, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|     status  : "Deprecating Soon", | ||||
|     query   : { field:"_all", query:"*", goal: 1},  | ||||
|     query   : { field:"_type", goal: 100},  | ||||
|     size    : 10, | ||||
|     exclude : [], | ||||
|     donut   : false, | ||||
|     tilt    : false, | ||||
|     legend  : true, | ||||
|     legend  : "above", | ||||
|     labels  : true, | ||||
|     mode    : "terms", | ||||
|     group   : "default", | ||||
|  | @ -53,30 +53,7 @@ angular.module('kibana.pie', []) | |||
|   _.defaults($scope.panel,_d) | ||||
| 
 | ||||
|   $scope.init = function() { | ||||
|     eventBus.register($scope,'time', function(event,time){set_time(time)}); | ||||
|     eventBus.register($scope,'query', function(event, query) { | ||||
|       $scope.panel.query.query = _.isArray(query) ? query[0] : query; | ||||
|       $scope.get_data(); | ||||
|     }); | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   $scope.remove_query = function(q) { | ||||
|     if($scope.panel.mode !== 'query')  | ||||
|       return false; | ||||
|     $scope.panel.query = _.without($scope.panel.query,q); | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.add_query = function(label,query) { | ||||
|     if($scope.panel.mode !== 'query')  | ||||
|       return false; | ||||
|     $scope.panel.query.unshift({ | ||||
|       query: query, | ||||
|       label: label,  | ||||
|     }); | ||||
|     $scope.$on('refresh',function(){$scope.get_data()}) | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|  | @ -84,21 +61,40 @@ angular.module('kibana.pie', []) | |||
|     switch(mode) | ||||
|     { | ||||
|     case 'terms': | ||||
|       $scope.panel.query = {query:"*",field:"_all"}; | ||||
|       $scope.panel.query = {field:"_all"}; | ||||
|       break; | ||||
|     case 'goal': | ||||
|       $scope.panel.query = {query:"*",goal:100}; | ||||
|       $scope.panel.query = {goal:100}; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   $scope.set_refresh = function (state) {  | ||||
|     $scope.refresh = state;  | ||||
|   } | ||||
| 
 | ||||
|   $scope.close_edit = function() { | ||||
|     if($scope.refresh) | ||||
|       $scope.get_data(); | ||||
|     $scope.refresh =  false; | ||||
|     $scope.$emit('render'); | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function() { | ||||
|      | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     }  | ||||
| 
 | ||||
|     $scope.panel.loading = true; | ||||
|     var request = $scope.ejs.Request().indices($scope.index); | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices); | ||||
| 
 | ||||
|     // This could probably be changed to a BoolFilter 
 | ||||
|     var boolQuery = ejs.BoolQuery(); | ||||
|     _.each(query.list,function(q) { | ||||
|       boolQuery = boolQuery.should(ejs.QueryStringQuery(q.query || '*')) | ||||
|     }) | ||||
| 
 | ||||
|     // Terms mode
 | ||||
|     if ($scope.panel.mode == "terms") { | ||||
|  | @ -109,10 +105,8 @@ angular.module('kibana.pie', []) | |||
|           .exclude($scope.panel.exclude) | ||||
|           .facetFilter(ejs.QueryFilter( | ||||
|             ejs.FilteredQuery( | ||||
|               ejs.QueryStringQuery($scope.panel.query.query || '*'), | ||||
|               ejs.RangeFilter($scope.time.field) | ||||
|                 .from($scope.time.from) | ||||
|                 .to($scope.time.to) | ||||
|               boolQuery, | ||||
|               filterSrv.getBoolFilter(filterSrv.ids) | ||||
|               )))).size(0) | ||||
| 
 | ||||
|       $scope.populate_modal(request); | ||||
|  | @ -141,11 +135,8 @@ angular.module('kibana.pie', []) | |||
|     // Goal mode
 | ||||
|     } else { | ||||
|       request = request | ||||
|         .query(ejs.QueryStringQuery($scope.panel.query.query || '*')) | ||||
|         .filter(ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to) | ||||
|           .cache(false)) | ||||
|         .query(boolQuery) | ||||
|         .filter(filterSrv.getBoolFilter(filterSrv.ids)) | ||||
|         .size(0) | ||||
|        | ||||
|       $scope.populate_modal(request); | ||||
|  | @ -169,26 +160,14 @@ angular.module('kibana.pie', []) | |||
|     $scope.modal = { | ||||
|       title: "Inspector", | ||||
|       body : "<h5>Last Elasticsearch Query</h5><pre>"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+ | ||||
|           'curl -XGET '+config.elasticsearch+'/'+dashboard.indices+"/_search?pretty -d'\n"+ | ||||
|           angular.toJson(JSON.parse(request.toString()),true)+ | ||||
|         "'</pre>",  | ||||
|     }  | ||||
|   } | ||||
| 
 | ||||
|   $scope.build_search = function(field,value) { | ||||
|     $scope.panel.query.query = add_to_query($scope.panel.query.query,field,value,false) | ||||
|     $scope.get_data(); | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query.query]); | ||||
|   } | ||||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
|    | ||||
| }) | ||||
| .directive('pie', function() { | ||||
| .directive('pie', function(query, filterSrv, dashboard) { | ||||
|   return { | ||||
|     restrict: 'A', | ||||
|     link: function(scope, elem, attrs) { | ||||
|  | @ -228,8 +207,8 @@ angular.module('kibana.pie', []) | |||
|             show: scope.panel.labels, | ||||
|             radius: 2/3, | ||||
|             formatter: function(label, series){ | ||||
|               return '<div ng-click="build_search(panel.query.field,\''+label+'\') "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+ | ||||
|                 label+'<br/>'+Math.round(series.percent)+'%</div>'; | ||||
|               return '<div "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+ | ||||
|                 series.info.alias+'<br/>'+Math.round(series.percent)+'%</div>'; | ||||
|             }, | ||||
|             threshold: 0.1  | ||||
|           } | ||||
|  | @ -258,7 +237,7 @@ angular.module('kibana.pie', []) | |||
|             clickable: true  | ||||
|           }, | ||||
|           legend: { show: false }, | ||||
|           colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D'] | ||||
|           colors: query.colors | ||||
|         }; | ||||
| 
 | ||||
|         // Populate element
 | ||||
|  | @ -269,7 +248,7 @@ angular.module('kibana.pie', []) | |||
|         } | ||||
|       } | ||||
| 
 | ||||
|       function piett(x, y, contents) { | ||||
|       function tt(x, y, contents) { | ||||
|         var tooltip = $('#pie-tooltip').length ?  | ||||
|           $('#pie-tooltip') : $('<div id="pie-tooltip"></div>'); | ||||
| 
 | ||||
|  | @ -287,16 +266,19 @@ angular.module('kibana.pie', []) | |||
|       } | ||||
| 
 | ||||
|       elem.bind("plotclick", function (event, pos, object) { | ||||
|         if (!object) | ||||
|         if (!object) { | ||||
|           return; | ||||
|         if(scope.panel.mode === 'terms') | ||||
|           scope.build_search(scope.panel.query.field,object.series.label); | ||||
|         } | ||||
|         if(scope.panel.mode === 'terms') { | ||||
|           filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label}) | ||||
|           dashboard.refresh(); | ||||
|         } | ||||
|       }); | ||||
| 
 | ||||
|       elem.bind("plothover", function (event, pos, item) { | ||||
|         if (item) { | ||||
|           var percent = parseFloat(item.series.percent).toFixed(1) + "%"; | ||||
|           piett(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +  | ||||
|           tt(pos.pageX, pos.pageY, "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> " +  | ||||
|             (item.series.label||"")+ " " + percent); | ||||
|         } else { | ||||
|           $("#pie-tooltip").remove(); | ||||
|  |  | |||
|  | @ -1,5 +1,15 @@ | |||
| <style> | ||||
| </style> | ||||
| <a class="close" ng-click="render();dismiss();" href="">×</a> | ||||
| <input class="input-medium" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' /> | ||||
| <i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i> | ||||
| <div> | ||||
|   <style> | ||||
|     .input-query-alias { | ||||
|       margin-bottom: 5px !important; | ||||
|     } | ||||
|   </style> | ||||
|   <a class="close" ng-click="render();dismiss();" href="">×</a> | ||||
|   <h6>Query Alias</h6> | ||||
|   <form> | ||||
|     <input class="input-medium input-query-alias" type="text" ng-model="queries.list[id].alias" placeholder='Alias...' /> | ||||
|     <div> | ||||
|       <i ng-repeat="color in queries.colors" class="pointer" ng-class="{'icon-circle-blank':queries.list[id].color == color,'icon-circle':queries.list[id].color != color}" style="color:{{color}}" ng-click="queries.list[id].color = color;render();"> </i> | ||||
|     </div> | ||||
|   </form> | ||||
| </div> | ||||
|  | @ -39,12 +39,10 @@ angular.module('kibana.query', []) | |||
|   } | ||||
| 
 | ||||
|   $scope.refresh = function(query) { | ||||
|     console.log('refresh') | ||||
|     $rootScope.$broadcast('refresh') | ||||
|   } | ||||
| 
 | ||||
|   $scope.render = function(query) { | ||||
|     console.log('render') | ||||
|     $rootScope.$broadcast('render') | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.table', []) | ||||
| .controller('table', function($rootScope, $scope, eventBus, fields, query) { | ||||
| .controller('table', function($rootScope, $scope, eventBus, fields, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -61,10 +61,6 @@ angular.module('kibana.table', []) | |||
| 
 | ||||
|   $scope.set_listeners = function(group) { | ||||
|     $scope.$on('refresh',function(){$scope.get_data()}) | ||||
|     eventBus.register($scope,'time',function(event,time) { | ||||
|       $scope.panel.offset = 0; | ||||
|       set_time(time) | ||||
|     }); | ||||
|     eventBus.register($scope,'sort', function(event,sort){ | ||||
|       $scope.panel.sort = _.clone(sort); | ||||
|       $scope.get_data(); | ||||
|  | @ -112,26 +108,26 @@ angular.module('kibana.table', []) | |||
|   } | ||||
| 
 | ||||
|   $scope.build_search = function(field,value,negate) { | ||||
|     _.each(query.list,function(q) { | ||||
|       q.query = add_to_query(q.query,field,value,negate); | ||||
|     }) | ||||
|     var query = (negate ? '-':'+')+field+":\""+value+"\"" | ||||
|     filterSrv.set({type:'querystring',query:query}) | ||||
|     $scope.panel.offset = 0; | ||||
|     $rootScope.$broadcast('refresh') | ||||
|     dashboard.refresh(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.get_data = function(segment,query_id) { | ||||
|     $scope.panel.error =  false; | ||||
| 
 | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } | ||||
|      | ||||
|     $scope.panel.loading = true; | ||||
| 
 | ||||
|     var _segment = _.isUndefined(segment) ? 0 : segment | ||||
|     $scope.segment = _segment; | ||||
| 
 | ||||
|     var request = $scope.ejs.Request().indices($scope.index[_segment]) | ||||
|     var request = $scope.ejs.Request().indices(dashboard.indices[_segment]) | ||||
| 
 | ||||
|     var boolQuery = ejs.BoolQuery(); | ||||
|     _.each(query.list,function(q) { | ||||
|  | @ -141,11 +137,8 @@ angular.module('kibana.table', []) | |||
|     request = request.query( | ||||
|       ejs.FilteredQuery( | ||||
|         boolQuery, | ||||
|         ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to) | ||||
|         ) | ||||
|       ) | ||||
|         filterSrv.getBoolFilter(filterSrv.ids) | ||||
|       )) | ||||
|       .highlight( | ||||
|         ejs.Highlight($scope.panel.highlight) | ||||
|         .fragmentSize(2147483647) // Max size of a 32bit unsigned int
 | ||||
|  | @ -209,10 +202,10 @@ angular.module('kibana.table', []) | |||
|       // If we're not sorting in reverse chrono order, query every index for
 | ||||
|       // size*pages results
 | ||||
|       // Otherwise, only get size*pages results then stop querying
 | ||||
|       if( | ||||
|           ($scope.data.length < $scope.panel.size*$scope.panel.pages ||  | ||||
|             !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc')) &&  | ||||
|           _segment+1 < $scope.index.length | ||||
|       if($scope.data.length < $scope.panel.size*$scope.panel.pages | ||||
|         //($scope.data.length < $scope.panel.size*$scope.panel.pages
 | ||||
|          // || !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc'))
 | ||||
|         && _segment+1 < dashboard.indices.length | ||||
|       ) { | ||||
|         $scope.get_data(_segment+1,$scope.query_id) | ||||
|       } | ||||
|  | @ -265,12 +258,6 @@ angular.module('kibana.table', []) | |||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = _.isUndefined(time.index) ? $scope.index : time.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
| }) | ||||
| .filter('highlight', function() { | ||||
|   return function(text) { | ||||
|  |  | |||
|  | @ -8,40 +8,6 @@ | |||
|       <input type="text" class="input-small" ng-model="panel.timefield"> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row-fluid">     | ||||
|     <h5>Index Settings</h5> | ||||
|     <div ng-show="panel.index_interval != 'none'" class="row-fluid">  | ||||
|        <div class="span12"> | ||||
|         <p class="small"> | ||||
|           Time stamped indices use your selected time range to create a list of  | ||||
|           indices that match a specified timestamp pattern. This can be very  | ||||
|           efficient for some data sets (eg, logs) For example, to match the  | ||||
|           default logstash index pattern you might use  | ||||
|           <code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are  | ||||
|           important as they instruct Kibana not to treat those letters as a  | ||||
|           pattern. | ||||
|         </p> | ||||
|         <p class="small"> | ||||
|           See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a> | ||||
|           for documentation on date formatting. | ||||
|         </p> | ||||
| 
 | ||||
|        </div> | ||||
|     </div> | ||||
|     <div class="row-fluid">  | ||||
|       <div class="span2"> | ||||
|         <h6>Timestamp</h6><select class="input-mini" ng-model="panel.index_interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select> | ||||
|       </div> | ||||
|       <div class="span5"> | ||||
|         <h6>Index <span ng-show="panel.index_interval != 'none'">pattern <small>Absolutes in []</small></span></h6> | ||||
|         <input type="text" class="input-medium" ng-model="panel.index"> | ||||
|       </div> | ||||
|       <div class="span4"> | ||||
|         <h6>Failover Index <small>If index not found</small></h6> | ||||
|         <input type="text" class="input-medium" ng-model="panel.defaultindex"> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="row-fluid"> | ||||
|     <h5>Relative mode <small>settings</small></h5>   | ||||
|     <div class="span6"> | ||||
|  |  | |||
|  | @ -11,9 +11,6 @@ | |||
|   * time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d'] | ||||
|   * timespan :: The default options selected for the relative view. Default: '15m' | ||||
|   * timefield :: The field in which time is stored in the document. | ||||
|   * index :: Index pattern to match. Literals should be double quoted. Default: '_all' | ||||
|   * defaultindex :: Index to failover to if index not found | ||||
|   * index_interval :: Time between timestamped indices (can be 'none') for static index | ||||
|   * refresh: Object containing refresh parameters | ||||
|     * enable :: true/false, enable auto refresh by default. Default: false | ||||
|     * interval :: Seconds between auto refresh. Default: 30 | ||||
|  | @ -28,7 +25,7 @@ | |||
| */ | ||||
| 
 | ||||
| angular.module('kibana.timepicker', []) | ||||
| .controller('timepicker', function($scope, eventBus, $timeout, timer, $http, kbnIndex) { | ||||
| .controller('timepicker', function($scope, $rootScope, eventBus, $timeout, timer, $http, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -37,9 +34,6 @@ angular.module('kibana.timepicker', []) | |||
|     time_options  : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'], | ||||
|     timespan      : '15m', | ||||
|     timefield     : '@timestamp', | ||||
|     index         : '_all', | ||||
|     defaultindex  : "_all", | ||||
|     index_interval: "none", | ||||
|     timeformat    : "", | ||||
|     group         : "default", | ||||
|     refresh       : { | ||||
|  | @ -58,6 +52,7 @@ angular.module('kibana.timepicker', []) | |||
|     // unnecessary refreshes during changes
 | ||||
|     $scope.refresh_interval = $scope.panel.refresh.interval | ||||
| 
 | ||||
| 
 | ||||
|     // Init a private time object with Date() objects depending on mode
 | ||||
|     switch($scope.panel.mode) { | ||||
|       case 'absolute': | ||||
|  | @ -86,35 +81,20 @@ angular.module('kibana.timepicker', []) | |||
|     if ($scope.panel.refresh.enable) | ||||
|       $scope.set_interval($scope.panel.refresh.interval); | ||||
| 
 | ||||
|     // In the case that a panel is not ready to receive a time event, it may
 | ||||
|     // request one be sent by broadcasting a 'get_time' with its _id to its group
 | ||||
|     // This panel can handle multiple groups
 | ||||
|     eventBus.register($scope,"get_time", function(event,id) { | ||||
|       eventBus.broadcast($scope.$id,id,'time',compile_time($scope.time)) | ||||
|     }); | ||||
| 
 | ||||
|     // In case some other panel broadcasts a time, set us to an absolute range
 | ||||
|     eventBus.register($scope,"set_time", function(event,time) { | ||||
|       $scope.panel.mode = 'absolute'; | ||||
|       set_timepicker(moment(time.from),moment(time.to)) | ||||
|       $scope.time_apply() | ||||
|     }); | ||||
|     $scope.$on('refresh', function() { | ||||
|       var time = filterSrv.timeRange('min') | ||||
| 
 | ||||
|     eventBus.register($scope,"zoom", function(event,factor) { | ||||
|       var _timespan = ($scope.time.to.valueOf() - $scope.time.from.valueOf()); | ||||
|       try { | ||||
|         if($scope.panel.mode != 'absolute') { | ||||
|           $scope.panel.mode = 'since' | ||||
|           set_timepicker(moment($scope.time.to.valueOf() - _timespan*factor),$scope.time.to) | ||||
|         } else { | ||||
|           var _center = $scope.time.to.valueOf() - _timespan/2 | ||||
|           set_timepicker(moment(_center - (_timespan*factor)/2), | ||||
|                          moment(_center + (_timespan*factor)/2))         | ||||
|       if($scope.time.from.diff(moment.utc(time.from)) != 0  | ||||
|         || $scope.time.to.diff(moment.utc(time.to)) != 0) | ||||
|       { | ||||
|         $scope.panel.mode = 'absolute'; | ||||
| 
 | ||||
|         // These 3 statements basicly do everything time_apply() does
 | ||||
|         set_timepicker(moment(time.from),moment(time.to)) | ||||
|         $scope.time = $scope.time_calc(); | ||||
|         update_panel() | ||||
|       } | ||||
|       } catch (e) { | ||||
|         console.log(e) | ||||
|       }      | ||||
|       $scope.time_apply(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|  | @ -146,10 +126,26 @@ angular.module('kibana.timepicker', []) | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   var update_panel = function() { | ||||
|     // Update panel's string representation of the time object.Don't update if
 | ||||
|     // we're in relative mode since we dont want to store the time object in the
 | ||||
|     // json for relative periods
 | ||||
|     if($scope.panel.mode !== 'relative') { | ||||
|       $scope.panel.time = {  | ||||
|         from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"), | ||||
|         to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"), | ||||
|       }; | ||||
|     } else { | ||||
|       delete $scope.panel.time; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   $scope.set_mode = function(mode) { | ||||
|     $scope.panel.mode = mode; | ||||
|     $scope.panel.refresh.enable = mode === 'absolute' ?  | ||||
|       false : $scope.panel.refresh.enable | ||||
| 
 | ||||
|     update_panel(); | ||||
|   } | ||||
| 
 | ||||
|   $scope.to_now = function() { | ||||
|  | @ -204,42 +200,33 @@ angular.module('kibana.timepicker', []) | |||
|   $scope.time_apply = function() {  | ||||
|     $scope.panel.error = "";    | ||||
|     // Update internal time object
 | ||||
| 
 | ||||
|     // Remove all other time filters
 | ||||
|     filterSrv.removeByType('time') | ||||
| 
 | ||||
|     $scope.time = $scope.time_calc(); | ||||
|     $scope.time.field = $scope.panel.timefield | ||||
|     update_panel() | ||||
| 
 | ||||
|     // Get indices for the time period, then broadcast time range and index list
 | ||||
|     // in a single object. Not sure if I like this.
 | ||||
|     if($scope.panel.index_interval !== 'none') { | ||||
|       kbnIndex.indices($scope.time.from, | ||||
|         $scope.time.to, | ||||
|         $scope.panel.index, | ||||
|         $scope.panel.index_interval | ||||
|       ).then(function (p) { | ||||
|         if(p.length > 0) { | ||||
|           $scope.time.index = p; | ||||
|           eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time)) | ||||
|         } else { | ||||
|           $scope.panel.error = "Could not match index pattern to any ElasticSearch indices" | ||||
|         } | ||||
|       }); | ||||
|     } else { | ||||
|       $scope.time.index = [$scope.panel.index]; | ||||
|       eventBus.broadcast($scope.$id,$scope.panel.group,'time',compile_time($scope.time)) | ||||
|     } | ||||
|     set_time_filter($scope.time) | ||||
|     dashboard.refresh(); | ||||
| 
 | ||||
|     // Update panel's string representation of the time object.Don't update if
 | ||||
|     // we're in relative mode since we dont want to store the time object in the
 | ||||
|     // json for relative periods
 | ||||
|     if($scope.panel.mode !== 'relative') { | ||||
|       $scope.panel.time = {  | ||||
|         from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"), | ||||
|         to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"), | ||||
|         index : $scope.time.index, | ||||
|   }; | ||||
| 
 | ||||
| 
 | ||||
|   function set_time_filter(time) { | ||||
|     time.type = 'time' | ||||
|     // Check if there's a time filter we remember, if not, set one and remember it
 | ||||
|     if(!_.isUndefined($scope.panel.filter_id) &&  | ||||
|       !_.isUndefined(filterSrv.list[$scope.panel.filter_id]) &&  | ||||
|       filterSrv.list[$scope.panel.filter_id].type == 'time')  | ||||
|     { | ||||
|       filterSrv.set(compile_time(time),$scope.panel.filter_id) | ||||
|     } else { | ||||
|       delete $scope.panel.time; | ||||
|       $scope.panel.filter_id = filterSrv.set(compile_time(time)) | ||||
|     } | ||||
|     return $scope.panel.filter_id; | ||||
|   } | ||||
|   }; | ||||
| 
 | ||||
|   // Prefer to pass around Date() objects in the EventBus since interacting with
 | ||||
|   // moment objects in libraries that are expecting Date()s can be tricky
 | ||||
|  | @ -247,8 +234,6 @@ angular.module('kibana.timepicker', []) | |||
|     time = _.clone(time) | ||||
|     time.from = time.from.toDate() | ||||
|     time.to   = time.to.toDate() | ||||
|     time.interval = $scope.panel.index_interval | ||||
|     time.pattern = $scope.panel.index  | ||||
|     return time; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
|     <span ng-class="{'text-success': query.hits.new >= query.hits.old, 'text-error': query.hits.old > query.hits.new}" class='strong'> | ||||
|       <i class='large' ng-class="{'icon-caret-up': query.hits.new >= query.hits.old, 'icon-caret-down': query.hits.old > query.hits.new}"></i> {{query.percent}}%  | ||||
|     </span> | ||||
|     <span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-show="query.label != ''">({{query.info.alias}})</span> | ||||
|     <span class="tiny pointer light" bs-tooltip="'Then: '+query.hits.old+', Now: '+query.hits.new" ng-show="query.info.alias != ''">({{query.info.alias}})</span> | ||||
|     <br ng-show="panel.arrangement == 'vertical'"> | ||||
|   </div> | ||||
| </kibana-panel>          | ||||
|  | @ -19,7 +19,7 @@ | |||
| 
 | ||||
| */ | ||||
| angular.module('kibana.trends', []) | ||||
| .controller('trends', function($scope, eventBus, kbnIndex, query) { | ||||
| .controller('trends', function($scope, eventBus, kbnIndex, query, dashboard, filterSrv) { | ||||
| 
 | ||||
|   // Set and populate defaults
 | ||||
|   var _d = { | ||||
|  | @ -34,15 +34,12 @@ angular.module('kibana.trends', []) | |||
| 
 | ||||
|   $scope.init = function () { | ||||
|     $scope.hits = 0; | ||||
| 
 | ||||
|     $scope.$on('refresh',function(){$scope.get_data()}) | ||||
| 
 | ||||
|     eventBus.register($scope,'time', function(event,time){ | ||||
|       set_time(time) | ||||
|     }); | ||||
|     eventBus.register($scope,'query', function(event, query) { | ||||
|       $scope.panel.query = _.map(query,function(q) { | ||||
|         return {query: q, label: q}; | ||||
|       }) | ||||
|       $scope.get_data(); | ||||
|     }); | ||||
|     // Now that we're all setup, request the time from our group
 | ||||
|     eventBus.broadcast($scope.$id,$scope.panel.group,'get_time') | ||||
|   } | ||||
|  | @ -52,8 +49,11 @@ angular.module('kibana.trends', []) | |||
|     $scope.panel.loading = true; | ||||
| 
 | ||||
|     // Make sure we have everything for the request to complete
 | ||||
|     if(_.isUndefined($scope.index) || _.isUndefined($scope.time)) | ||||
|     if(dashboard.indices.length == 0) { | ||||
|       return | ||||
|     } else { | ||||
|       $scope.index = dashboard.indices | ||||
|     } | ||||
| 
 | ||||
|     $scope.old_time = { | ||||
|       from : new Date($scope.time.from.getTime() - interval_to_seconds($scope.panel.ago)*1000), | ||||
|  | @ -67,9 +67,8 @@ angular.module('kibana.trends', []) | |||
|     _.each(query.ids, function(id) { | ||||
|       var q = $scope.ejs.FilteredQuery( | ||||
|         ejs.QueryStringQuery(query.list[id].query || '*'), | ||||
|         ejs.RangeFilter($scope.time.field) | ||||
|           .from($scope.time.from) | ||||
|           .to($scope.time.to)) | ||||
|         filterSrv.getBoolFilter(filterSrv.ids)) | ||||
| 
 | ||||
|       request = request | ||||
|         .facet($scope.ejs.QueryFacet(id) | ||||
|           .query(q) | ||||
|  | @ -110,7 +109,6 @@ angular.module('kibana.trends', []) | |||
|     // Populate scope when we have results
 | ||||
|     function process_results(results) {  | ||||
|       results.then(function(results) { | ||||
|         console.log(results) | ||||
| 
 | ||||
|         $scope.panel.loading = false; | ||||
|         if(_segment == 0) { | ||||
|  | @ -124,9 +122,17 @@ angular.module('kibana.trends', []) | |||
|           $scope.panel.error = $scope.parse_error(results.error); | ||||
|           return; | ||||
|         } | ||||
|         if($scope.query_id === query_id) { | ||||
| 
 | ||||
|         // Convert facet ids to numbers
 | ||||
|         var facetIds = _.map(_.keys(results.facets),function(k){if(!isNaN(k)){return parseInt(k)}}) | ||||
| 
 | ||||
|         // Make sure we're still on the same query/queries
 | ||||
|         if($scope.query_id === query_id &&  | ||||
|           _.intersection(facetIds,query.ids).length == query.ids.length | ||||
|           ) { | ||||
|           var i = 0; | ||||
|           _.each(query.ids, function(id) { | ||||
|             var v = results.facets[id] | ||||
|             var n = results.facets[id].count | ||||
|             var o = results.facets['old_'+id].count | ||||
| 
 | ||||
|  | @ -193,7 +199,6 @@ angular.module('kibana.trends', []) | |||
| 
 | ||||
|   function set_time(time) { | ||||
|     $scope.time = time; | ||||
|     $scope.index = time.index || $scope.index | ||||
|     $scope.get_data(); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,8 +13,63 @@ | |||
|     </div> | ||||
|   </div> | ||||
|   <div class="row-fluid">     | ||||
|     <h4>Index Settings</h4> | ||||
|     <div ng-show="dashboard.current.index.interval != 'none'" class="row-fluid">  | ||||
|        <div class="span12"> | ||||
|         <p class="small"> | ||||
|           Time stamped indices use your selected time range to create a list of  | ||||
|           indices that match a specified timestamp pattern. This can be very  | ||||
|           efficient for some data sets (eg, logs) For example, to match the  | ||||
|           default logstash index pattern you might use  | ||||
|           <code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are  | ||||
|           important as they instruct Kibana not to treat those letters as a  | ||||
|           pattern. | ||||
|         </p> | ||||
|         <p class="small"> | ||||
|           See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a> | ||||
|           for documentation on date formatting. | ||||
|         </p> | ||||
| 
 | ||||
|        </div> | ||||
|     </div> | ||||
|     <div class="row-fluid">  | ||||
|       <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> | ||||
|       </div> | ||||
|       <div class="span5"> | ||||
|         <h6>Index <span ng-show="dashboard.current.index.interval != 'none'">pattern <small>Absolutes in []</small></span></h6> | ||||
|         <input type="text" class="input-medium" ng-model="dashboard.current.index.pattern"> | ||||
|       </div> | ||||
|       <div class="span4"> | ||||
|         <h6>Failover Index <small>If index not found</small></h6> | ||||
|         <input type="text" class="input-medium" ng-model="dashboard.current.index.default"> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <hr/> | ||||
|   <h4>Rows</h4> | ||||
|   <div class="row-fluid"> | ||||
|     <form> | ||||
|       <div class="span5">       | ||||
|         <label class="small">Title</label> | ||||
|         <input type="text" class="input-large" ng-model='row.title'></input> | ||||
|       </div> | ||||
|       <div class="span2"> | ||||
|         <label class="small">Height</label> | ||||
|         <input type="text" class="input-mini" ng-model='row.height'></input> | ||||
|       </div> | ||||
|       <div class="span1"> | ||||
|         <label class="small"> Editable </label> | ||||
|         <input type="checkbox" ng-model="row.editable" ng-checked="row.editable" /> | ||||
|       </div> | ||||
|       <div class="span4"> | ||||
|         <label> </label> | ||||
|         <button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button> | ||||
|       </div> | ||||
|     </form> | ||||
|   </div> | ||||
|   <div class="row-fluid"> | ||||
|     <div class="span12"> | ||||
|       <table class="table table-condensed table-striped"> | ||||
|         <thead> | ||||
|           <th>Title</th> | ||||
|  | @ -30,23 +85,6 @@ | |||
|       </table> | ||||
|     </div> | ||||
|   </div> | ||||
|   <h4>New row</h4> | ||||
|     <div class="row-fluid"> | ||||
|       <div class="span8">       | ||||
|         <label class="small">Title</label> | ||||
|         <input type="text" class="input-large" ng-model='row.title'></input> | ||||
|       </div> | ||||
|       <div class="span2"> | ||||
|         <label class="small">Height</label> | ||||
|         <input type="text" class="input-mini" ng-model='row.height'></input> | ||||
|       </div> | ||||
|       <div class="span1"> | ||||
|         <label class="small"> Editable </label> | ||||
|         <input type="checkbox" ng-model="row.editable" ng-checked="row.editable" /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button><br> | ||||
|   </div> | ||||
| </div> | ||||
| <div class="modal-footer"> | ||||
|   <button type="button" class="btn btn-success" ng-click="dismiss();reset_panel();">Close</button> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue