module.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /*
  2. ## Trends
  3. ### Parameters
  4. * style :: A hash of css styles
  5. * arrangement :: How should I arrange the query results? 'horizontal' or 'vertical'
  6. * ago :: Date math formatted time to look back
  7. */
  8. define([
  9. 'angular',
  10. 'app',
  11. 'underscore',
  12. 'kbn'
  13. ],
  14. function (angular, app, _, kbn) {
  15. 'use strict';
  16. var module = angular.module('kibana.panels.trends', []);
  17. app.useModule(module);
  18. module.controller('trends', function($scope, kbnIndex, querySrv, dashboard, filterSrv) {
  19. $scope.panelMeta = {
  20. modals : [
  21. {
  22. description: "Inspect",
  23. icon: "icon-info-sign",
  24. partial: "app/partials/inspector.html",
  25. show: $scope.panel.spyable
  26. }
  27. ],
  28. editorTabs : [
  29. {title:'Queries', src:'app/partials/querySelect.html'}
  30. ],
  31. status : "Beta",
  32. description : "A stock-ticker style representation of how queries are moving over time. "+
  33. "For example, if the time is 1:10pm, your time picker was set to \"Last 10m\", and the \"Time "+
  34. "Ago\" parameter was set to '1h', the panel would show how much the query results have changed"+
  35. " since 12:00-12:10pm"
  36. };
  37. // Set and populate defaults
  38. var _d = {
  39. queries : {
  40. mode : 'all',
  41. ids : []
  42. },
  43. style : { "font-size": '14pt'},
  44. ago : '1d',
  45. arrangement : 'vertical',
  46. spyable: true
  47. };
  48. _.defaults($scope.panel,_d);
  49. $scope.init = function () {
  50. $scope.hits = 0;
  51. $scope.$on('refresh', function(){$scope.get_data();});
  52. $scope.get_data();
  53. };
  54. $scope.get_data = function(segment,query_id) {
  55. delete $scope.panel.error;
  56. $scope.panelMeta.loading = true;
  57. // Make sure we have everything for the request to complete
  58. if(dashboard.indices.length === 0) {
  59. return;
  60. } else {
  61. $scope.index = segment > 0 ? $scope.index : dashboard.indices;
  62. }
  63. $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries);
  64. // Determine a time field
  65. var timeField = _.uniq(_.pluck(filterSrv.getByType('time'),'field'));
  66. if(timeField.length > 1) {
  67. $scope.panel.error = "Time field must be consistent amongst time filters";
  68. return;
  69. } else if(timeField.length === 0) {
  70. $scope.panel.error = "A time filter must exist for this panel to function";
  71. return;
  72. } else {
  73. timeField = timeField[0];
  74. }
  75. // This logic can be simplifie greatly with the new kbn.parseDate
  76. $scope.time = filterSrv.timeRange('last');
  77. $scope.old_time = {
  78. from : new Date($scope.time.from.getTime() - kbn.interval_to_ms($scope.panel.ago)),
  79. to : new Date($scope.time.to.getTime() - kbn.interval_to_ms($scope.panel.ago))
  80. };
  81. var _segment = _.isUndefined(segment) ? 0 : segment;
  82. var request = $scope.ejs.Request();
  83. var _ids_without_time = _.difference(filterSrv.ids,filterSrv.idsByType('time'));
  84. // Build the question part of the query
  85. _.each($scope.panel.queries.ids, function(id) {
  86. var q = $scope.ejs.FilteredQuery(
  87. querySrv.getEjsObj(id),
  88. filterSrv.getBoolFilter(_ids_without_time).must(
  89. $scope.ejs.RangeFilter(timeField)
  90. .from($scope.time.from)
  91. .to($scope.time.to)
  92. ));
  93. request = request
  94. .facet($scope.ejs.QueryFacet(id)
  95. .query(q)
  96. ).size(0);
  97. });
  98. // And again for the old time period
  99. _.each($scope.panel.queries.ids, function(id) {
  100. var q = $scope.ejs.FilteredQuery(
  101. querySrv.getEjsObj(id),
  102. filterSrv.getBoolFilter(_ids_without_time).must(
  103. $scope.ejs.RangeFilter(timeField)
  104. .from($scope.old_time.from)
  105. .to($scope.old_time.to)
  106. ));
  107. request = request
  108. .facet($scope.ejs.QueryFacet("old_"+id)
  109. .query(q)
  110. ).size(0);
  111. });
  112. // Populate the inspector panel
  113. $scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
  114. // If we're on the first segment we need to get our indices
  115. if (_segment === 0) {
  116. kbnIndex.indices(
  117. $scope.old_time.from,
  118. $scope.old_time.to,
  119. dashboard.current.index.pattern,
  120. dashboard.current.index.interval
  121. ).then(function (p) {
  122. $scope.index = _.union(p,$scope.index);
  123. request = request.indices($scope.index[_segment]);
  124. process_results(request.doSearch(),_segment,query_id);
  125. });
  126. } else {
  127. process_results(request.indices($scope.index[_segment]).doSearch(),_segment,query_id);
  128. }
  129. };
  130. // Populate scope when we have results
  131. var process_results = function(results,_segment,query_id) {
  132. results.then(function(results) {
  133. $scope.panelMeta.loading = false;
  134. if(_segment === 0) {
  135. $scope.hits = {};
  136. $scope.data = [];
  137. query_id = $scope.query_id = new Date().getTime();
  138. }
  139. // Check for error and abort if found
  140. if(!(_.isUndefined(results.error))) {
  141. $scope.panel.error = $scope.parse_error(results.error);
  142. return;
  143. }
  144. // Convert facet ids to numbers
  145. var facetIds = _.map(_.keys(results.facets),function(k){if(!isNaN(k)){return parseInt(k, 10);}});
  146. // Make sure we're still on the same query/queries
  147. if($scope.query_id === query_id &&
  148. _.intersection(facetIds,$scope.panel.queries.ids).length === $scope.panel.queries.ids.length
  149. ) {
  150. var i = 0;
  151. _.each($scope.panel.queries.ids, function(id) {
  152. var n = results.facets[id].count;
  153. var o = results.facets['old_'+id].count;
  154. var hits = {
  155. new : _.isUndefined($scope.data[i]) || _segment === 0 ? n : $scope.data[i].hits.new+n,
  156. old : _.isUndefined($scope.data[i]) || _segment === 0 ? o : $scope.data[i].hits.old+o
  157. };
  158. $scope.hits.new += n;
  159. $scope.hits.old += o;
  160. var percent = percentage(hits.old,hits.new) == null ?
  161. '?' : Math.round(percentage(hits.old,hits.new)*100)/100;
  162. // Create series
  163. $scope.data[i] = {
  164. info: querySrv.list[id],
  165. hits: {
  166. new : hits.new,
  167. old : hits.old
  168. },
  169. percent: percent
  170. };
  171. i++;
  172. });
  173. $scope.$emit('render');
  174. if(_segment < $scope.index.length-1) {
  175. $scope.get_data(_segment+1,query_id);
  176. } else {
  177. $scope.trends = $scope.data;
  178. }
  179. }
  180. });
  181. };
  182. function percentage(x,y) {
  183. return x === 0 ? null : 100*(y-x)/x;
  184. }
  185. $scope.set_refresh = function (state) {
  186. $scope.refresh = state;
  187. };
  188. $scope.close_edit = function() {
  189. if($scope.refresh) {
  190. $scope.get_data();
  191. }
  192. $scope.refresh = false;
  193. $scope.$emit('render');
  194. };
  195. });
  196. });