module.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. angular.module('kibana.histogram', [])
  2. .controller('histogram', function($scope, eventBus) {
  3. // Set and populate defaults
  4. var _d = {
  5. group : "default",
  6. query : [ {query: "*", label:"Query"} ],
  7. interval : secondsToHms(calculate_interval($scope.from,$scope.to,40,0)/1000),
  8. fill : 3,
  9. linewidth : 3,
  10. timezone : 'browser', // browser, utc or a standard timezone
  11. spyable : true,
  12. zoomlinks : true,
  13. bars : true,
  14. stack : true,
  15. points : false,
  16. lines : false,
  17. legend : true,
  18. 'x-axis' : true,
  19. 'y-axis' : true,
  20. }
  21. _.defaults($scope.panel,_d)
  22. $scope.init = function() {
  23. eventBus.register($scope,'time', function(event,time){$scope.set_time(time)});
  24. // Consider eliminating the check for array, this should always be an array
  25. eventBus.register($scope,'query', function(event, query) {
  26. if(_.isArray(query)) {
  27. $scope.panel.query = _.map(query,function(q) {
  28. return {query: q, label: q};
  29. })
  30. } else {
  31. $scope.panel.query[0] = {query: query, label: query}
  32. }
  33. $scope.get_data();
  34. });
  35. // Now that we're all setup, request the time from our group if we don't
  36. // have it yet
  37. if(_.isUndefined($scope.time))
  38. eventBus.broadcast($scope.$id,$scope.panel.group,'get_time')
  39. }
  40. $scope.remove_query = function(q) {
  41. $scope.panel.query = _.without($scope.panel.query,q);
  42. $scope.get_data();
  43. }
  44. $scope.add_query = function(label,query) {
  45. if(!(_.isArray($scope.panel.query)))
  46. $scope.panel.query = new Array();
  47. $scope.panel.query.unshift({
  48. query: query,
  49. label: label,
  50. });
  51. $scope.get_data();
  52. }
  53. $scope.get_data = function(segment,query_id) {
  54. delete $scope.panel.error
  55. // Make sure we have everything for the request to complete
  56. if(_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
  57. return
  58. $scope.panel.loading = true;
  59. var _segment = _.isUndefined(segment) ? 0 : segment
  60. var request = $scope.ejs.Request().indices($scope.panel.index[_segment]);
  61. // Build the question part of the query
  62. var queries = [];
  63. _.each($scope.panel.query, function(v) {
  64. queries.push($scope.ejs.FilteredQuery(
  65. ejs.QueryStringQuery(v.query || '*'),
  66. ejs.RangeFilter($scope.time.field)
  67. .from($scope.time.from)
  68. .to($scope.time.to))
  69. )
  70. });
  71. // Build the facet part, injecting the query in as a facet filter
  72. _.each(queries, function(v) {
  73. request = request
  74. .facet($scope.ejs.DateHistogramFacet("chart"+_.indexOf(queries,v))
  75. .field($scope.time.field)
  76. .interval($scope.panel.interval)
  77. .facetFilter($scope.ejs.QueryFilter(v))
  78. ).size(0)
  79. })
  80. // Populate the inspector panel
  81. $scope.populate_modal(request);
  82. // Then run it
  83. var results = request.doSearch();
  84. // Populate scope when we have results
  85. results.then(function(results) {
  86. $scope.panel.loading = false;
  87. if(_segment == 0) {
  88. $scope.hits = 0;
  89. $scope.data = [];
  90. query_id = $scope.query_id = new Date().getTime();
  91. }
  92. // Check for error and abort if found
  93. if(!(_.isUndefined(results.error))) {
  94. $scope.panel.error = $scope.parse_error(results.error);
  95. return;
  96. }
  97. // Make sure we're still on the same query
  98. if($scope.query_id === query_id) {
  99. var i = 0;
  100. _.each(results.facets, function(v, k) {
  101. // Null values at each end of the time range ensure we see entire range
  102. if(_.isUndefined($scope.data[i]) || _segment == 0) {
  103. var data = [[$scope.time.from.getTime(), null],[$scope.time.to.getTime(), null]];
  104. var hits = 0;
  105. } else {
  106. var data = $scope.data[i].data
  107. var hits = $scope.data[i].hits
  108. }
  109. // Assemble segments
  110. var segment_data = [];
  111. _.each(v.entries, function(v, k) {
  112. segment_data.push([v['time'],v['count']])
  113. hits += v['count']; // The series level hits counter
  114. $scope.hits += v['count']; // Entire dataset level hits counter
  115. });
  116. data.splice.apply(data,[1,0].concat(segment_data)) // Join histogram data
  117. // Create the flot series object
  118. var series = {
  119. data: {
  120. label: $scope.panel.query[i].label || "query"+(parseInt(i)+1),
  121. data: data,
  122. hits: hits
  123. },
  124. };
  125. if (!(_.isUndefined($scope.panel.query[i].color)))
  126. series.data.color = $scope.panel.query[i].color;
  127. $scope.data[i] = series.data
  128. i++;
  129. });
  130. // Tell the histogram directive to render.
  131. $scope.$emit('render')
  132. // If we still have segments left, get them
  133. if(_segment < $scope.panel.index.length-1) {
  134. $scope.get_data(_segment+1,query_id)
  135. }
  136. }
  137. });
  138. }
  139. // function $scope.zoom
  140. // factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
  141. $scope.zoom = function(factor) {
  142. eventBus.broadcast($scope.$id,$scope.panel.group,'zoom',factor)
  143. }
  144. // I really don't like this function, too much dom manip. Break out into directive?
  145. $scope.populate_modal = function(request) {
  146. $scope.modal = {
  147. title: "Inspector",
  148. body : "<h5>Last Elasticsearch Query</h5><pre>"+
  149. 'curl -XGET '+config.elasticsearch+'/'+$scope.panel.index+"/_search?pretty -d'\n"+
  150. angular.toJson(JSON.parse(request.toString()),true)+
  151. "'</pre>",
  152. }
  153. }
  154. $scope.set_time = function(time) {
  155. $scope.time = time;
  156. $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index
  157. $scope.panel.interval = secondsToHms(
  158. calculate_interval(time.from,time.to,50,0)/1000);
  159. $scope.get_data();
  160. }
  161. })
  162. .directive('histogramChart', function(eventBus) {
  163. return {
  164. restrict: 'A',
  165. link: function(scope, elem, attrs, ctrl) {
  166. var height = scope.panel.height || scope.row.height;
  167. // Receive render events
  168. scope.$on('render',function(){
  169. render_panel();
  170. });
  171. // Re-render if the window is resized
  172. angular.element(window).bind('resize', function(){
  173. render_panel();
  174. });
  175. // Function for rendering panel
  176. function render_panel() {
  177. // Set barwidth based on specified interval
  178. var barwidth = interval_to_seconds(scope.panel.interval)*1000
  179. var scripts = $LAB.script("common/lib/panels/jquery.flot.js")
  180. .script("common/lib/panels/jquery.flot.time.js")
  181. .script("common/lib/panels/jquery.flot.stack.js")
  182. .script("common/lib/panels/jquery.flot.selection.js")
  183. .script("common/lib/panels/timezone.js")
  184. // Populate element. Note that jvectormap appends, does not replace.
  185. scripts.wait(function(){
  186. var stack = scope.panel.stack ? true : null;
  187. // Populate element
  188. try {
  189. scope.plot = $.plot(elem, scope.data, {
  190. legend: { show: false },
  191. series: {
  192. stack: stack,
  193. lines: {
  194. show: scope.panel.lines,
  195. fill: scope.panel.fill/10,
  196. lineWidth: scope.panel.linewidth,
  197. steps: false
  198. },
  199. bars: { show: scope.panel.bars, fill: 1, barWidth: barwidth/1.8 },
  200. points: { show: scope.panel.points, fill: 1, fillColor: false},
  201. shadowSize: 1
  202. },
  203. yaxis: { show: scope.panel['y-axis'], min: 0, color: "#000" },
  204. xaxis: {
  205. timezone: scope.panel.timezone,
  206. show: scope.panel['x-axis'],
  207. mode: "time",
  208. timeformat: time_format(scope.panel.interval),
  209. label: "Datetime",
  210. color: "#000",
  211. },
  212. selection: {
  213. mode: "x",
  214. color: '#666'
  215. },
  216. grid: {
  217. backgroundColor: '#fff',
  218. borderWidth: 0,
  219. borderColor: '#eee',
  220. color: "#eee",
  221. hoverable: true,
  222. },
  223. colors: ['#EB6841','#00A0B0','#6A4A3C','#EDC951','#CC333F']
  224. })
  225. // Work around for missing legend at initialization
  226. if(!scope.$$phase)
  227. scope.$apply()
  228. } catch(e) {
  229. elem.text(e)
  230. }
  231. })
  232. }
  233. function time_format(interval) {
  234. var _int = interval_to_seconds(interval)
  235. if(_int >= 2628000)
  236. return "%m/%y"
  237. if(_int >= 86400)
  238. return "%m/%d/%y"
  239. if(_int >= 60)
  240. return "%H:%M<br>%m/%d"
  241. else
  242. return "%H:%M:%S"
  243. }
  244. function tt(x, y, contents) {
  245. var tooltip = $('#pie-tooltip').length ?
  246. $('#pie-tooltip') : $('<div id="pie-tooltip"></div>');
  247. //var tooltip = $('#pie-tooltip')
  248. tooltip.html(contents).css({
  249. position: 'absolute',
  250. top : y + 5,
  251. left : x + 5,
  252. color : "#000",
  253. border : '2px solid #000',
  254. padding : '10px',
  255. 'font-size': '11pt',
  256. 'font-weight' : 200,
  257. 'background-color': '#FFF',
  258. 'border-radius': '10px',
  259. }).appendTo("body");
  260. }
  261. elem.bind("plothover", function (event, pos, item) {
  262. if (item) {
  263. tt(pos.pageX, pos.pageY,
  264. "<div style='vertical-align:middle;display:inline-block;background:"+item.series.color+";height:15px;width:15px;border-radius:10px;'></div> "+
  265. item.datapoint[1].toFixed(0) + " @ " +
  266. new Date(item.datapoint[0]).format('mm/dd HH:MM:ss'));
  267. } else {
  268. $("#pie-tooltip").remove();
  269. }
  270. });
  271. elem.bind("plotselected", function (event, ranges) {
  272. scope.time.from = new Date(ranges.xaxis.from);
  273. scope.time.to = new Date(ranges.xaxis.to)
  274. eventBus.broadcast(scope.$id,scope.panel.group,'set_time',scope.time)
  275. });
  276. }
  277. };
  278. })