You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2016/06/27 03:22:57 UTC
[13/22] ignite git commit: Ignite Web Console beta2.
http://git-wip-us.apache.org/repos/asf/ignite/blob/541e17d0/modules/web-console/src/main/js/controllers/sql-controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/controllers/sql-controller.js b/modules/web-console/src/main/js/controllers/sql-controller.js
index 4bc39e2..a8058ff 100644
--- a/modules/web-console/src/main/js/controllers/sql-controller.js
+++ b/modules/web-console/src/main/js/controllers/sql-controller.js
@@ -19,18 +19,26 @@
import consoleModule from 'controllers/common-module';
consoleModule.controller('sqlController', [
- '$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', '$loading', '$common', '$confirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'QueryNotebooks', 'uiGridExporterConstants',
- function ($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, $loading, $common, $confirm, agentMonitor, IgniteChartColors, QueryNotebooks, uiGridExporterConstants) {
- var stopTopology = null;
+ '$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', '$loading', '$common', '$confirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'QueryNotebooks', 'uiGridConstants', 'uiGridExporterConstants',
+ function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, $loading, $common, $confirm, agentMonitor, IgniteChartColors, QueryNotebooks, uiGridConstants, uiGridExporterConstants) {
+ let stopTopology = null;
- $scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
+ const _tryStopRefresh = function(paragraph) {
+ if (paragraph.rate && paragraph.rate.stopTime) {
+ $interval.cancel(paragraph.rate.stopTime);
+
+ delete paragraph.rate.stopTime;
+ }
+ };
+
+ const _stopTopologyRefresh = () => {
$interval.cancel(stopTopology);
if ($scope.notebook && $scope.notebook.paragraphs)
- $scope.notebook.paragraphs.forEach(function (paragraph) {
- _tryStopRefresh(paragraph);
- });
- });
+ $scope.notebook.paragraphs.forEach((paragraph) => _tryStopRefresh(paragraph));
+ };
+
+ $scope.$on('$stateChangeStart', _stopTopologyRefresh);
$scope.caches = [];
@@ -51,14 +59,14 @@ consoleModule.controller('sqlController', [
];
$scope.exportDropdown = [
- { 'text': 'Export all', 'click': 'exportCsvAll(paragraph)' }
- //{ 'text': 'Export all to CSV', 'click': 'exportCsvAll(paragraph)' },
- //{ 'text': 'Export all to PDF', 'click': 'exportPdfAll(paragraph)' }
+ { text: 'Export all', click: 'exportCsvAll(paragraph)' }
+ // { 'text': 'Export all to CSV', 'click': 'exportCsvAll(paragraph)' },
+ // { 'text': 'Export all to PDF', 'click': 'exportPdfAll(paragraph)' }
];
$scope.metadata = [];
- $scope.metaFilter = "";
+ $scope.metaFilter = '';
$scope.metaOptions = {
nodeChildren: 'children',
@@ -69,1429 +77,1451 @@ consoleModule.controller('sqlController', [
}
};
- $scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? "<default>" : cacheName;
+ $scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? '<default>' : cacheName;
- var _handleException = function(err) {
+ const _handleException = function(err) {
$common.showError(err);
};
// Time line X axis descriptor.
- var TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
+ const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
// Row index X axis descriptor.
- var ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
+ const ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
// We need max 1800 items to hold history for 30 mins in case of refresh every second.
- var HISTORY_LENGTH = 1800;
+ const HISTORY_LENGTH = 1800;
- var MAX_VAL_COLS = IgniteChartColors.length;
+ const MAX_VAL_COLS = IgniteChartColors.length;
$anchorScroll.yOffset = 55;
$scope.chartColor = function(index) {
- return {"color": "white", "background-color": IgniteChartColors[index]};
+ return {color: 'white', 'background-color': IgniteChartColors[index]};
};
- $scope.chartRemoveKeyColumn = function (paragraph, index) {
- paragraph.chartKeyCols.splice(index, 1);
+ function _chartNumber(arr, idx, dflt) {
+ if (idx >= 0 && arr && arr.length > idx && _.isNumber(arr[idx]))
+ return arr[idx];
- _chartApplySettings(paragraph, true);
- };
+ return dflt;
+ }
- $scope.chartRemoveValColumn = function (paragraph, index) {
- paragraph.chartValCols.splice(index, 1);
+ function _min(rows, idx, dflt) {
+ let min = _chartNumber(rows[0], idx, dflt);
- _chartApplySettings(paragraph, true);
- };
+ _.forEach(rows, (row) => {
+ const v = _chartNumber(row, idx, dflt);
- $scope.chartAcceptKeyColumn = function(paragraph, item) {
- var accepted = _.findIndex(paragraph.chartKeyCols, item) < 0;
+ if (v < min)
+ min = v;
+ });
- if (accepted) {
- paragraph.chartKeyCols = [item];
+ return min;
+ }
- _chartApplySettings(paragraph, true);
- }
+ function _max(rows, idx, dflt) {
+ let max = _chartNumber(rows[0], idx, dflt);
- return false;
- };
+ _.forEach(rows, (row) => {
+ const v = _chartNumber(row, idx, dflt);
- $scope.chartAcceptValColumn = function(paragraph, item) {
- var valCols = paragraph.chartValCols;
+ if (v > max)
+ max = v;
+ });
- var accepted = _.findIndex(valCols, item) < 0 && item.value >= 0 && _numberType(item.type);
+ return max;
+ }
- if (accepted) {
- if (valCols.length == MAX_VAL_COLS - 1)
- valCols.shift();
+ function _sum(rows, idx) {
+ let sum = 0;
- valCols.push(item);
+ _.forEach(rows, (row) => sum += _chartNumber(row, idx, 0));
- _chartApplySettings(paragraph, true);
- }
+ return sum;
+ }
- return false;
- };
+ function _aggregate(rows, aggFx, idx, dflt) {
+ const len = rows.length;
- $scope.scrollParagraphs = [];
+ switch (aggFx) {
+ case 'FIRST':
+ return _chartNumber(rows[0], idx, dflt);
- $scope.rebuildScrollParagraphs = function () {
- $scope.scrollParagraphs = $scope.notebook.paragraphs.map(function (paragraph) {
- return {
- "text": paragraph.name,
- "click": 'scrollToParagraph("' + paragraph.id + '")'
- };
- });
- };
+ case 'LAST':
+ return _chartNumber(rows[len - 1], idx, dflt);
- $scope.scrollToParagraph = function (paragraphId) {
- var idx = _.findIndex($scope.notebook.paragraphs, {id: paragraphId});
+ case 'MIN':
+ return _min(rows, idx, dflt);
- if (idx >= 0) {
- if (!_.includes($scope.notebook.expandedParagraphs, idx))
- $scope.notebook.expandedParagraphs.push(idx);
+ case 'MAX':
+ return _max(rows, idx, dflt);
- setTimeout(function () {
- $scope.notebook.paragraphs[idx].ace.focus();
- });
- }
+ case 'SUM':
+ return _sum(rows, idx);
- $location.hash(paragraphId);
+ case 'AVG':
+ return len > 0 ? _sum(rows, idx) / len : 0;
- $anchorScroll();
- };
+ case 'COUNT':
+ return len;
- const _hideColumn = (col) => col.fieldName !== '_KEY' && col.fieldName !== '_VAL';
+ default:
+ }
- const _allColumn = () => true;
+ return 0;
+ }
- var paragraphId = 0;
+ function _chartLabel(arr, idx, dflt) {
+ if (arr && arr.length > idx && _.isString(arr[idx]))
+ return arr[idx];
- function enhanceParagraph(paragraph) {
- paragraph.nonEmpty = function () {
- return this.rows && this.rows.length > 0;
- };
+ return dflt;
+ }
- paragraph.chart = function () {
- return this.result != 'table' && this.result != 'none';
- };
+ function _chartDatum(paragraph) {
+ let datum = [];
- paragraph.queryExecuted = () =>
- paragraph.queryArgs && paragraph.queryArgs.query && !paragraph.queryArgs.query.startsWith('EXPLAIN ');
+ if (paragraph.chartColumnsConfigured()) {
+ paragraph.chartValCols.forEach(function(valCol) {
+ let index = 0;
+ let values = [];
+ const colIdx = valCol.value;
- paragraph.table = function () {
- return this.result == 'table';
- };
+ if (paragraph.chartTimeLineEnabled()) {
+ const aggFx = valCol.aggFx;
+ const colLbl = valCol.label + ' [' + aggFx + ']';
- paragraph.chartColumnsConfigured = function () {
- return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
- };
+ if (paragraph.charts && paragraph.charts.length === 1)
+ datum = paragraph.charts[0].data;
- paragraph.chartTimeLineEnabled = function () {
- return !_.isEmpty(this.chartKeyCols) && angular.equals(this.chartKeyCols[0], TIME_LINE);
- };
+ const chartData = _.find(datum, {series: valCol.label});
- paragraph.timeLineSupported = function () {
- return this.result != 'pie';
- };
+ const leftBound = new Date();
+ leftBound.setMinutes(leftBound.getMinutes() - parseInt(paragraph.timeLineSpan, 10));
- paragraph.refreshExecuting = function () {
- return paragraph.rate && paragraph.rate.stopTime
- };
+ if (chartData) {
+ const lastItem = _.last(paragraph.chartHistory);
- Object.defineProperty(paragraph, 'gridOptions', { value: {
- onRegisterApi: function(api) {
- $animate.enabled(api.grid.element, false);
+ values = chartData.values;
- this.api = api;
- },
- enableGridMenu: false,
- enableColumnMenus: false,
- setRows: function(rows) {
- this.height = Math.min(rows.length, 15) * 30 + 42 + 'px';
+ values.push({
+ x: lastItem.tm,
+ y: _aggregate(lastItem.rows, aggFx, colIdx, index++)
+ });
- this.data = rows;
- }
- }});
+ while (values.length > 0 && values[0].x < leftBound)
+ values.shift();
+ }
+ else {
+ _.forEach(paragraph.chartHistory, (history) => {
+ if (history.tm >= leftBound) {
+ values.push({
+ x: history.tm,
+ y: _aggregate(history.rows, aggFx, colIdx, index++)
+ });
+ }
+ });
- Object.defineProperty(paragraph, 'chartHistory', {value: []});
- }
+ datum.push({series: valCol.label, key: colLbl, values});
+ }
+ }
+ else {
+ index = paragraph.total;
- $scope.aceInit = function (paragraph) {
- return function (editor) {
- editor.setAutoScrollEditorIntoView(true);
- editor.$blockScrolling = Infinity;
+ values = _.map(paragraph.rows, function(row) {
+ const xCol = paragraph.chartKeyCols[0].value;
- var renderer = editor.renderer;
+ const v = {
+ x: _chartNumber(row, xCol, index),
+ xLbl: _chartLabel(row, xCol, null),
+ y: _chartNumber(row, colIdx, index)
+ };
- renderer.setHighlightGutterLine(false);
- renderer.setShowPrintMargin(false);
- renderer.setOption('fontFamily', 'monospace');
- renderer.setOption('fontSize', '14px');
- renderer.setOption('minLines', '5');
- renderer.setOption('maxLines', '15');
+ index++;
- editor.setTheme('ace/theme/chrome');
+ return v;
+ });
- Object.defineProperty(paragraph, 'ace', { value: editor });
+ datum.push({series: valCol.label, key: valCol.label, values});
+ }
+ });
}
- };
- var _setActiveCache = function () {
- if ($scope.caches.length > 0)
- _.forEach($scope.notebook.paragraphs, function (paragraph) {
- if (!_.find($scope.caches, {name: paragraph.cacheName}))
- paragraph.cacheName = $scope.caches[0].name;
- });
- };
+ return datum;
+ }
- const _refreshFn = () =>
- agentMonitor.topology()
- .then((clusters) => {
- agentMonitor.checkModal();
+ function _xX(d) {
+ return d.x;
+ }
- const caches = _.flattenDeep(clusters.map((cluster) => cluster.caches));
+ function _yY(d) {
+ return d.y;
+ }
- $scope.caches = _.sortBy(_.uniqBy(_.reject(caches, { mode: 'LOCAL' }), 'name'), 'name');
+ function _xAxisTimeFormat(d) {
+ return d3.time.format('%X')(new Date(d));
+ }
- _setActiveCache();
- })
- .catch((err) => {
- if (err.code === 2)
- return agentMonitor.showNodeError('Agent is failed to authenticate in grid. Please check agent\'s login and password.');
+ const _intClasses = ['java.lang.Byte', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
- agentMonitor.showNodeError(err.message)}
- );
+ function _intType(cls) {
+ return _.includes(_intClasses, cls);
+ }
- var loadNotebook = function (notebook) {
- $scope.notebook = notebook;
+ const _xAxisWithLabelFormat = function(paragraph) {
+ return function(d) {
+ const values = paragraph.charts[0].data[0].values;
- $scope.notebook_name = notebook.name;
+ const fmt = _intType(paragraph.chartKeyCols[0].type) ? 'd' : ',.2f';
- if (!$scope.notebook.expandedParagraphs)
- $scope.notebook.expandedParagraphs = [];
+ const dx = values[d];
- if (!$scope.notebook.paragraphs)
- $scope.notebook.paragraphs = [];
+ if (!dx)
+ return d3.format(fmt)(d);
- _.forEach(notebook.paragraphs, function (paragraph) {
- paragraph.id = 'paragraph-' + paragraphId++;
+ const lbl = dx.xLbl;
- enhanceParagraph(paragraph);
- });
+ return lbl ? lbl : d3.format(fmt)(d);
+ };
+ };
- if (!notebook.paragraphs || notebook.paragraphs.length == 0)
- $scope.addParagraph();
- else
- $scope.rebuildScrollParagraphs();
+ function _xAxisLabel(paragraph) {
+ return _.isEmpty(paragraph.chartKeyCols) ? 'X' : paragraph.chartKeyCols[0].label;
+ }
- agentMonitor.startWatch({
- state: 'base.configuration.clusters',
- text: 'Back to Configuration',
- goal: 'execute sql statements'
- })
- .then(() => {
- $loading.start('sqlLoading');
+ const _yAxisFormat = function(d) {
+ const fmt = d < 1000 ? ',.2f' : '.3s';
- _refreshFn()
- .finally(() => {
- if ($root.IgniteDemoMode)
- _.forEach($scope.notebook.paragraphs, $scope.execute);
+ return d3.format(fmt)(d);
+ };
- $loading.finish('sqlLoading');
+ function _updateCharts(paragraph) {
+ $timeout(() => _.forEach(paragraph.charts, (chart) => chart.api.update()), 100);
+ }
- stopTopology = $interval(_refreshFn, 5000, 0, false);
- });
- });
- };
+ function _updateChartsWithData(paragraph, newDatum) {
+ $timeout(() => {
+ if (!paragraph.chartTimeLineEnabled()) {
+ const chartDatum = paragraph.charts[0].data;
- QueryNotebooks.read($state.params.noteId)
- .then(loadNotebook)
- .catch(function() {
- $scope.notebookLoadFailed = true;
+ chartDatum.length = 0;
- $loading.finish('sqlLoading');
+ _.forEach(newDatum, (series) => chartDatum.push(series));
+ }
+
+ paragraph.charts[0].api.update();
});
+ }
- $scope.renameNotebook = function (name) {
- if (!name)
- return;
+ function _yAxisLabel(paragraph) {
+ const cols = paragraph.chartValCols;
- if ($scope.notebook.name != name) {
- $scope.notebook.name = name;
+ const tml = paragraph.chartTimeLineEnabled();
- QueryNotebooks.save($scope.notebook)
- .then(function() {
- var idx = _.findIndex($root.notebooks, function (item) {
- return item._id == $scope.notebook._id;
- });
+ return _.isEmpty(cols) ? 'Y' : _.map(cols, function(col) {
+ let lbl = col.label;
- if (idx >= 0) {
- $root.notebooks[idx].name = name;
+ if (tml)
+ lbl += ' [' + col.aggFx + ']';
- $root.rebuildDropdown();
- }
+ return lbl;
+ }).join(', ');
+ }
- $scope.notebook.edit = false;
- })
- .catch(_handleException);
- }
- else
- $scope.notebook.edit = false
- };
+ function _barChart(paragraph) {
+ const datum = _chartDatum(paragraph);
- $scope.removeNotebook = function () {
- $confirm.confirm('Are you sure you want to remove: "' + $scope.notebook.name + '"?')
- .then(function () {
- return QueryNotebooks.remove($scope.notebook);
- })
- .then(function (notebook) {
- if (notebook)
- $state.go('base.sql.notebook', {noteId: notebook._id});
- else
- $state.go('base.configuration.clusters');
- })
- .catch(_handleException);
- };
-
- $scope.renameParagraph = function (paragraph, newName) {
- if (!newName)
- return;
+ if (_.isEmpty(paragraph.charts)) {
+ const stacked = paragraph.chartsOptions && paragraph.chartsOptions.barChart
+ ? paragraph.chartsOptions.barChart.stacked
+ : true;
- if (paragraph.name != newName) {
- paragraph.name = newName;
+ const options = {
+ chart: {
+ type: 'multiBarChart',
+ height: 400,
+ margin: {left: 70},
+ duration: 0,
+ x: _xX,
+ y: _yY,
+ xAxis: {
+ axisLabel: _xAxisLabel(paragraph),
+ tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+ showMaxMin: false
+ },
+ yAxis: {
+ axisLabel: _yAxisLabel(paragraph),
+ tickFormat: _yAxisFormat
+ },
+ color: IgniteChartColors,
+ stacked,
+ showControls: true,
+ legend: {
+ vers: 'furious',
+ margin: {right: -25}
+ }
+ }
+ };
- $scope.rebuildScrollParagraphs();
+ paragraph.charts = [{options, data: datum}];
- QueryNotebooks.save($scope.notebook)
- .then(function () { paragraph.edit = false; })
- .catch(_handleException);
+ _updateCharts(paragraph);
}
else
- paragraph.edit = false
- };
+ _updateChartsWithData(paragraph, datum);
+ }
- $scope.addParagraph = function () {
- var sz = $scope.notebook.paragraphs.length;
+ function _pieChartDatum(paragraph) {
+ const datum = [];
- var paragraph = {
- id: 'paragraph-' + paragraphId++,
- name: 'Query' + (sz ==0 ? '' : sz),
- query: '',
- pageSize: $scope.pageSizes[0],
- timeLineSpan: $scope.timeLineSpans[0],
- result: 'none',
- rate: {
- value: 1,
- unit: 60000,
- installed: false
- }
- };
+ if (paragraph.chartColumnsConfigured() && !paragraph.chartTimeLineEnabled()) {
+ paragraph.chartValCols.forEach(function(valCol) {
+ let index = paragraph.total;
- enhanceParagraph(paragraph);
+ const values = _.map(paragraph.rows, (row) => {
+ const xCol = paragraph.chartKeyCols[0].value;
- if ($scope.caches && $scope.caches.length > 0)
- paragraph.cacheName = $scope.caches[0].name;
+ const v = {
+ x: xCol < 0 ? index : row[xCol],
+ y: _chartNumber(row, valCol.value, index)
+ };
- $scope.notebook.paragraphs.push(paragraph);
+ // Workaround for known problem with zero values on Pie chart.
+ if (v.y === 0)
+ v.y = 0.0001;
- $scope.notebook.expandedParagraphs.push(sz);
+ index++;
- $scope.rebuildScrollParagraphs();
+ return v;
+ });
- $location.hash(paragraph.id);
+ datum.push({series: paragraph.chartKeyCols[0].label, key: valCol.label, values});
+ });
+ }
- $anchorScroll();
+ return datum;
+ }
- setTimeout(function () {
- paragraph.ace.focus();
+ function _pieChart(paragraph) {
+ let datum = _pieChartDatum(paragraph);
+
+ if (datum.length === 0)
+ datum = [{values: []}];
+
+ paragraph.charts = _.map(datum, function(data) {
+ return {
+ options: {
+ chart: {
+ type: 'pieChart',
+ height: 400,
+ duration: 0,
+ x: _xX,
+ y: _yY,
+ showLabels: true,
+ labelThreshold: 0.05,
+ labelType: 'percent',
+ donut: true,
+ donutRatio: 0.35,
+ legend: {
+ vers: 'furious',
+ margin: {
+ right: -25
+ }
+ }
+ },
+ title: {
+ enable: true,
+ text: data.key
+ }
+ },
+ data: data.values
+ };
});
- };
- $scope.setResult = function (paragraph, new_result) {
- if (paragraph.result === new_result)
- return;
+ _updateCharts(paragraph);
+ }
- _saveChartSettings(paragraph);
+ function _lineChart(paragraph) {
+ const datum = _chartDatum(paragraph);
- paragraph.result = new_result;
+ if (_.isEmpty(paragraph.charts)) {
+ const options = {
+ chart: {
+ type: 'lineChart',
+ height: 400,
+ margin: { left: 70 },
+ duration: 0,
+ x: _xX,
+ y: _yY,
+ xAxis: {
+ axisLabel: _xAxisLabel(paragraph),
+ tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+ showMaxMin: false
+ },
+ yAxis: {
+ axisLabel: _yAxisLabel(paragraph),
+ tickFormat: _yAxisFormat
+ },
+ color: IgniteChartColors,
+ useInteractiveGuideline: true,
+ legend: {
+ vers: 'furious',
+ margin: {
+ right: -25
+ }
+ }
+ }
+ };
- if (paragraph.chart())
- _chartApplySettings(paragraph, true);
- };
+ paragraph.charts = [{options, data: datum}];
- $scope.resultEq = function(paragraph, result) {
- return (paragraph.result === result);
- };
+ _updateCharts(paragraph);
+ }
+ else
+ _updateChartsWithData(paragraph, datum);
+ }
- $scope.removeParagraph = function(paragraph) {
- $confirm.confirm('Are you sure you want to remove: "' + paragraph.name + '"?')
- .then(function () {
- $scope.stopRefresh(paragraph);
+ function _areaChart(paragraph) {
+ const datum = _chartDatum(paragraph);
- var paragraph_idx = _.findIndex($scope.notebook.paragraphs, function (item) {
- return paragraph == item;
- });
+ if (_.isEmpty(paragraph.charts)) {
+ const style = paragraph.chartsOptions && paragraph.chartsOptions.areaChart
+ ? paragraph.chartsOptions.areaChart.style
+ : 'stack';
- var panel_idx = _.findIndex($scope.expandedParagraphs, function (item) {
- return paragraph_idx == item;
- });
+ const options = {
+ chart: {
+ type: 'stackedAreaChart',
+ height: 400,
+ margin: {left: 70},
+ duration: 0,
+ x: _xX,
+ y: _yY,
+ xAxis: {
+ axisLabel: _xAxisLabel(paragraph),
+ tickFormat: paragraph.chartTimeLineEnabled() ? _xAxisTimeFormat : _xAxisWithLabelFormat(paragraph),
+ showMaxMin: false
+ },
+ yAxis: {
+ axisLabel: _yAxisLabel(paragraph),
+ tickFormat: _yAxisFormat
+ },
+ color: IgniteChartColors,
+ style,
+ legend: {
+ vers: 'furious',
+ margin: {right: -25}
+ }
+ }
+ };
- if (panel_idx >= 0)
- $scope.expandedParagraphs.splice(panel_idx, 1);
+ paragraph.charts = [{options, data: datum}];
- $scope.notebook.paragraphs.splice(paragraph_idx, 1);
+ _updateCharts(paragraph);
+ }
+ else
+ _updateChartsWithData(paragraph, datum);
+ }
- $scope.rebuildScrollParagraphs();
+ function _chartApplySettings(paragraph, resetCharts) {
+ if (resetCharts)
+ paragraph.charts = [];
- QueryNotebooks.save($scope.notebook)
- .catch(_handleException);
- });
- };
+ if (paragraph.chart() && paragraph.nonEmpty()) {
+ switch (paragraph.result) {
+ case 'bar':
+ _barChart(paragraph);
+ break;
- $scope.paragraphExpanded = function(paragraph) {
- var paragraph_idx = _.findIndex($scope.notebook.paragraphs, function (item) {
- return paragraph == item;
- });
+ case 'pie':
+ _pieChart(paragraph);
+ break;
- var panel_idx = _.findIndex($scope.notebook.expandedParagraphs, function (item) {
- return paragraph_idx == item;
- });
+ case 'line':
+ _lineChart(paragraph);
+ break;
- return panel_idx >= 0;
+ case 'area':
+ _areaChart(paragraph);
+ break;
+
+ default:
+ }
+ }
+ }
+
+ $scope.chartRemoveKeyColumn = function(paragraph, index) {
+ paragraph.chartKeyCols.splice(index, 1);
+
+ _chartApplySettings(paragraph, true);
};
- var _columnFilter = function(paragraph) {
- return paragraph.disabledSystemColumns || paragraph.systemColumns ? _allColumn : _hideColumn;
+ $scope.chartRemoveValColumn = function(paragraph, index) {
+ paragraph.chartValCols.splice(index, 1);
+
+ _chartApplySettings(paragraph, true);
};
- var _notObjectType = function(cls) {
- return $common.isJavaBuiltInClass(cls);
+ $scope.chartAcceptKeyColumn = function(paragraph, item) {
+ const accepted = _.findIndex(paragraph.chartKeyCols, item) < 0;
+
+ if (accepted) {
+ paragraph.chartKeyCols = [item];
+
+ _chartApplySettings(paragraph, true);
+ }
+
+ return false;
};
- var _numberClasses = ['java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Double',
+ const _numberClasses = ['java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Double',
'java.lang.Float', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
- var _numberType = function(cls) {
+ const _numberType = function(cls) {
return _.includes(_numberClasses, cls);
};
- var _intClasses = ['java.lang.Byte', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Short'];
+ $scope.chartAcceptValColumn = function(paragraph, item) {
+ const valCols = paragraph.chartValCols;
- function _intType(cls) {
- return _.includes(_intClasses, cls);
- }
+ const accepted = _.findIndex(valCols, item) < 0 && item.value >= 0 && _numberType(item.type);
- var _rebuildColumns = function (paragraph) {
- var columnDefs = [];
+ if (accepted) {
+ if (valCols.length === MAX_VAL_COLS - 1)
+ valCols.shift();
- _.forEach(_.groupBy(paragraph.meta, 'fieldName'), function (colsByName, fieldName) {
- var colsByTypes = _.groupBy(colsByName, 'typeName');
+ valCols.push(item);
- var needType = _.keys(colsByTypes).length > 1;
+ _chartApplySettings(paragraph, true);
+ }
- _.forEach(colsByTypes, function(colsByType, typeName) {
- _.forEach(colsByType, function (col, ix) {
- col.fieldName = (needType && !$common.isEmptyString(typeName) ? typeName + '.' : '') + fieldName + (ix > 0 ? ix : '');
- })
- });
- });
+ return false;
+ };
- _.forEach(paragraph.meta, function (col, idx) {
- if (paragraph.columnFilter(col)) {
- if (_notObjectType(col.fieldTypeName))
- paragraph.chartColumns.push({value: idx, type: col.fieldTypeName, label: col.fieldName, aggFx: $scope.aggregateFxs[0]});
-
- columnDefs.push({
- displayName: col.fieldName,
- headerTooltip: _fullColName(col),
- field: paragraph.queryArgs.query ? '' + idx : col.fieldName,
- minWidth: 50
- });
- }
+ $scope.scrollParagraphs = [];
+
+ $scope.rebuildScrollParagraphs = function() {
+ $scope.scrollParagraphs = $scope.notebook.paragraphs.map(function(paragraph) {
+ return {
+ text: paragraph.name,
+ click: 'scrollToParagraph("' + paragraph.id + '")'
+ };
});
+ };
- paragraph.gridOptions.columnDefs = columnDefs;
+ $scope.scrollToParagraph = function(paragraphId) {
+ const idx = _.findIndex($scope.notebook.paragraphs, {id: paragraphId});
- if (paragraph.chartColumns.length > 0) {
- paragraph.chartColumns.push(TIME_LINE);
- paragraph.chartColumns.push(ROW_IDX);
+ if (idx >= 0) {
+ if (!_.includes($scope.notebook.expandedParagraphs, idx))
+ $scope.notebook.expandedParagraphs.push(idx);
+
+ setTimeout(function() {
+ $scope.notebook.paragraphs[idx].ace.focus();
+ });
}
- // We could accept onl not object columns for X axis.
- paragraph.chartKeyCols = _retainColumns(paragraph.chartColumns, paragraph.chartKeyCols, _notObjectType, true);
+ $location.hash(paragraphId);
- // We could accept only numeric columns for Y axis.
- paragraph.chartValCols = _retainColumns(paragraph.chartColumns, paragraph.chartValCols, _numberType, false, paragraph.chartKeyCols);
+ $anchorScroll();
};
- $scope.toggleSystemColumns = function (paragraph) {
- if (paragraph.disabledSystemColumns)
- return;
-
- paragraph.systemColumns = !paragraph.systemColumns;
-
- paragraph.columnFilter = _columnFilter(paragraph);
+ const _hideColumn = (col) => col.fieldName !== '_KEY' && col.fieldName !== '_VAL';
- paragraph.chartColumns = [];
+ const _allColumn = () => true;
- _rebuildColumns(paragraph);
- };
+ let paragraphId = 0;
- function _retainColumns(allCols, curCols, acceptableType, xAxis, unwantedCols) {
- var retainedCols = [];
+ const _fullColName = function(col) {
+ const res = [];
- var availableCols = xAxis ? allCols : _.filter(allCols, function (col) {
- return col.value >= 0;
- });
+ if (col.schemaName)
+ res.push(col.schemaName);
- if (availableCols.length > 0) {
- curCols.forEach(function (curCol) {
- var col = _.find(availableCols, {label: curCol.label});
+ if (col.typeName)
+ res.push(col.typeName);
- if (col && acceptableType(col.type)) {
- col.aggFx = curCol.aggFx;
+ res.push(col.fieldName);
- retainedCols.push(col);
- }
- });
+ return res.join('.');
+ };
- // If nothing was restored, add first acceptable column.
- if (_.isEmpty(retainedCols)) {
- var col;
+ function enhanceParagraph(paragraph) {
+ paragraph.nonEmpty = function() {
+ return this.rows && this.rows.length > 0;
+ };
- if (unwantedCols)
- col = _.find(availableCols, function (col) {
- return !_.find(unwantedCols, {label: col.label}) && acceptableType(col.type);
- });
+ paragraph.chart = function() {
+ return this.result !== 'table' && this.result !== 'none';
+ };
- if (!col)
- col = _.find(availableCols, function (col) {
- return acceptableType(col.type);
- });
+ paragraph.queryExecuted = () =>
+ paragraph.queryArgs && paragraph.queryArgs.query && !paragraph.queryArgs.query.startsWith('EXPLAIN ');
- if (col)
- retainedCols.push(col);
- }
- }
+ paragraph.table = function() {
+ return this.result === 'table';
+ };
- return retainedCols;
- }
+ paragraph.chartColumnsConfigured = function() {
+ return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
+ };
- /**
- * @param {Object} paragraph Query
- * @param {{fieldsMetadata: Array, items: Array, queryId: int, last: Boolean}} res Query results.
- * @private
- */
- var _processQueryResult = function (paragraph, res) {
- var prevKeyCols = paragraph.chartKeyCols;
- var prevValCols = paragraph.chartValCols;
+ paragraph.chartTimeLineEnabled = function() {
+ return !_.isEmpty(this.chartKeyCols) && angular.equals(this.chartKeyCols[0], TIME_LINE);
+ };
- if (!_.eq(paragraph.meta, res.fieldsMetadata)) {
- paragraph.meta = [];
+ paragraph.timeLineSupported = function() {
+ return this.result !== 'pie';
+ };
- paragraph.chartColumns = [];
+ paragraph.refreshExecuting = function() {
+ return paragraph.rate && paragraph.rate.stopTime;
+ };
- if (!$common.isDefined(paragraph.chartKeyCols))
- paragraph.chartKeyCols = [];
+ Object.defineProperty(paragraph, 'gridOptions', { value: {
+ enableGridMenu: false,
+ enableColumnMenus: false,
+ flatEntityAccess: true,
+ fastWatch: true,
+ updateColumns(cols) {
+ this.columnDefs = _.map(cols, (col) => {
+ return {
+ displayName: col.fieldName,
+ headerTooltip: _fullColName(col),
+ field: col.field,
+ minWidth: 50
+ };
+ });
- if (!$common.isDefined(paragraph.chartValCols))
- paragraph.chartValCols = [];
+ $timeout(() => this.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN));
+ },
+ updateRows(rows) {
+ const sizeChanged = this.data.length !== rows.length;
- if (res.fieldsMetadata.length <= 2) {
- var _key = _.find(res.fieldsMetadata, {fieldName: '_KEY'});
- var _val = _.find(res.fieldsMetadata, {fieldName: '_VAL'});
+ this.data = rows;
- paragraph.disabledSystemColumns = (res.fieldsMetadata.length == 2 && _key && _val) ||
- (res.fieldsMetadata.length == 1 && (_key || _val));
- }
+ if (sizeChanged) {
+ const height = Math.min(rows.length, 15) * 30 + 47;
- paragraph.columnFilter = _columnFilter(paragraph);
+ // Remove header height.
+ this.api.grid.element.css('height', height + 'px');
- paragraph.meta = res.fieldsMetadata;
+ $timeout(() => this.api.core.handleWindowResize());
+ }
+ },
+ onRegisterApi(api) {
+ $animate.enabled(api.grid.element, false);
- _rebuildColumns(paragraph);
- }
+ this.api = api;
+ }
+ }});
- paragraph.page = 1;
+ Object.defineProperty(paragraph, 'chartHistory', {value: []});
+ }
- paragraph.total = 0;
+ $scope.aceInit = function(paragraph) {
+ return function(editor) {
+ editor.setAutoScrollEditorIntoView(true);
+ editor.$blockScrolling = Infinity;
- paragraph.queryId = res.last ? null : res.queryId;
+ const renderer = editor.renderer;
- delete paragraph.errMsg;
+ renderer.setHighlightGutterLine(false);
+ renderer.setShowPrintMargin(false);
+ renderer.setOption('fontFamily', 'monospace');
+ renderer.setOption('fontSize', '14px');
+ renderer.setOption('minLines', '5');
+ renderer.setOption('maxLines', '15');
- // Prepare explain results for display in table.
- if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.items) {
- paragraph.rows = [];
+ editor.setTheme('ace/theme/chrome');
- res.items.forEach(function (row, i) {
- var line = res.items.length - 1 == i ? row[0] : row[0] + '\n';
+ Object.defineProperty(paragraph, 'ace', { value: editor });
+ };
+ };
- line.replace(/\"/g, '').split('\n').forEach(function (line) {
- paragraph.rows.push([line]);
- });
+ const _setActiveCache = function() {
+ if ($scope.caches.length > 0) {
+ _.forEach($scope.notebook.paragraphs, (paragraph) => {
+ if (!_.find($scope.caches, {name: paragraph.cacheName}))
+ paragraph.cacheName = $scope.caches[0].name;
});
}
- else
- paragraph.rows = res.items;
-
- paragraph.gridOptions.setRows(paragraph.rows);
+ };
- var chartHistory = paragraph.chartHistory;
+ const _updateTopology = () =>
+ agentMonitor.topology()
+ .then((clusters) => {
+ agentMonitor.checkModal();
- // Clear history on query change.
- var queryChanged = paragraph.prevQuery != paragraph.query;
+ const caches = _.flattenDeep(clusters.map((cluster) => cluster.caches));
- if (queryChanged) {
- paragraph.prevQuery = paragraph.query;
+ $scope.caches = _.sortBy(_.map(_.uniqBy(_.reject(caches, {mode: 'LOCAL'}), 'name'), (cache) => {
+ cache.label = $scope.maskCacheName(cache.name);
- chartHistory.length = 0;
+ return cache;
+ }), 'label');
- _.forEach(paragraph.charts, function (chart) {
- chart.data.length = 0;
+ _setActiveCache();
})
- }
-
- // Add results to history.
- chartHistory.push({tm: new Date(), rows: paragraph.rows});
-
- // Keep history size no more than max length.
- while (chartHistory.length > HISTORY_LENGTH)
- chartHistory.shift();
+ .catch((err) => {
+ if (err.code === 2)
+ return agentMonitor.showNodeError('Agent is failed to authenticate in grid. Please check agent\'s login and password.');
- _showLoading(paragraph, false);
+ agentMonitor.showNodeError(err);
+ });
- if (paragraph.result === 'none' || !paragraph.queryExecuted())
- paragraph.result = 'table';
- else if (paragraph.chart()) {
- var resetCharts = queryChanged;
+ const _startTopologyRefresh = () => {
+ $loading.start('sqlLoading');
- if (!resetCharts) {
- var curKeyCols = paragraph.chartKeyCols;
- var curValCols = paragraph.chartValCols;
+ agentMonitor.awaitAgent()
+ .then(_updateTopology)
+ .finally(() => {
+ if ($root.IgniteDemoMode)
+ _.forEach($scope.notebook.paragraphs, $scope.execute);
- resetCharts = !prevKeyCols || !prevValCols ||
- prevKeyCols.length != curKeyCols.length ||
- prevValCols.length != curValCols.length;
- }
+ $loading.finish('sqlLoading');
- _chartApplySettings(paragraph, resetCharts);
- }
+ stopTopology = $interval(_updateTopology, 5000, 0, false);
+ });
};
- const _closeOldQuery = (paragraph) => {
- const queryId = paragraph.queryArgs && paragraph.queryArgs.queryId;
+ const loadNotebook = function(notebook) {
+ $scope.notebook = notebook;
- return queryId ? agentMonitor.queryClose(queryId) : $q.when();
- };
+ $scope.notebook_name = notebook.name;
- const _executeRefresh = (paragraph) => {
- const args = paragraph.queryArgs;
+ if (!$scope.notebook.expandedParagraphs)
+ $scope.notebook.expandedParagraphs = [];
- agentMonitor.awaitAgent()
- .then(() => _closeOldQuery(paragraph))
- .then(() => agentMonitor.query(args.cacheName, args.pageSize, args.query))
- .then(_processQueryResult.bind(this, paragraph))
- .catch((err) => {
- paragraph.errMsg = err.message;
- });
- };
+ if (!$scope.notebook.paragraphs)
+ $scope.notebook.paragraphs = [];
- const _showLoading = (paragraph, enable) => paragraph.loading = enable;
+ _.forEach(notebook.paragraphs, (paragraph) => {
+ paragraph.id = 'paragraph-' + paragraphId++;
- $scope.execute = function (paragraph) {
- QueryNotebooks.save($scope.notebook)
- .catch(_handleException);
+ enhanceParagraph(paragraph);
+ });
- paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
+ if (!notebook.paragraphs || notebook.paragraphs.length === 0)
+ $scope.addParagraph();
+ else
+ $scope.rebuildScrollParagraphs();
- _showLoading(paragraph, true);
+ agentMonitor.startWatch({
+ state: 'base.configuration.clusters',
+ text: 'Back to Configuration',
+ goal: 'execute sql statements',
+ onDisconnect: () => {
+ _stopTopologyRefresh();
- _closeOldQuery(paragraph)
- .then(function () {
- const args = paragraph.queryArgs = {
- cacheName: paragraph.cacheName,
- pageSize: paragraph.pageSize,
- query: paragraph.query
- };
+ _startTopologyRefresh();
+ }
+ })
+ .then(_startTopologyRefresh);
+ };
- return agentMonitor.query(args.cacheName, args.pageSize, args.query);
- })
- .then(function (res) {
- _processQueryResult(paragraph, res);
+ QueryNotebooks.read($state.params.noteId)
+ .then(loadNotebook)
+ .catch(() => {
+ $scope.notebookLoadFailed = true;
- _tryStartRefresh(paragraph);
- })
- .catch((err) => {
- paragraph.errMsg = err.message;
+ $loading.finish('sqlLoading');
+ });
- _showLoading(paragraph, false);
+ $scope.renameNotebook = function(name) {
+ if (!name)
+ return;
- $scope.stopRefresh(paragraph);
- })
- .finally(function () {
- paragraph.ace.focus();
- });
- };
+ if ($scope.notebook.name !== name) {
+ const prevName = $scope.notebook.name;
- $scope.queryExecuted = function(paragraph) {
- return $common.isDefined(paragraph.queryArgs);
- };
+ $scope.notebook.name = name;
- $scope.explain = function (paragraph) {
- QueryNotebooks.save($scope.notebook)
- .catch(_handleException);
+ QueryNotebooks.save($scope.notebook)
+ .then(function() {
+ const idx = _.findIndex($root.notebooks, function(item) {
+ return item._id === $scope.notebook._id;
+ });
- _cancelRefresh(paragraph);
+ if (idx >= 0) {
+ $root.notebooks[idx].name = name;
- _showLoading(paragraph, true);
+ $root.rebuildDropdown();
+ }
- _closeOldQuery(paragraph)
- .then(function () {
- const args = paragraph.queryArgs = {
- cacheName: paragraph.cacheName,
- pageSize: paragraph.pageSize,
- query: 'EXPLAIN ' + paragraph.query
- };
+ $scope.notebook.edit = false;
+ })
+ .catch((err) => {
+ $scope.notebook.name = prevName;
- return agentMonitor.query(args.cacheName, args.pageSize, args.query);
- })
- .then(_processQueryResult.bind(this, paragraph))
- .catch((err) => {
- paragraph.errMsg = err.message;
+ _handleException(err);
+ });
+ }
+ else
+ $scope.notebook.edit = false;
+ };
- _showLoading(paragraph, false);
+ $scope.removeNotebook = function() {
+ $confirm.confirm('Are you sure you want to remove: "' + $scope.notebook.name + '"?')
+ .then(function() {
+ return QueryNotebooks.remove($scope.notebook);
+ })
+ .then(function(notebook) {
+ if (notebook)
+ $state.go('base.sql.notebook', {noteId: notebook._id});
+ else
+ $state.go('base.configuration.clusters');
})
- .finally(function () {
- paragraph.ace.focus();
- });
- };
-
- $scope.scan = function (paragraph) {
- QueryNotebooks.save($scope.notebook)
.catch(_handleException);
+ };
- _cancelRefresh(paragraph);
-
- _showLoading(paragraph, true);
+ $scope.renameParagraph = function(paragraph, newName) {
+ if (!newName)
+ return;
- _closeOldQuery(paragraph)
- .then(() => {
- const args = paragraph.queryArgs = {
- cacheName: paragraph.cacheName,
- pageSize: paragraph.pageSize
- };
+ if (paragraph.name !== newName) {
+ paragraph.name = newName;
- return agentMonitor.query(args.cacheName, args.pageSize);
- })
- .then(_processQueryResult.bind(this, paragraph))
- .catch((err) => {
- paragraph.errMsg = err.message;
+ $scope.rebuildScrollParagraphs();
- _showLoading(paragraph, false);
- })
- .finally(function () {
- paragraph.ace.focus();
- });
+ QueryNotebooks.save($scope.notebook)
+ .then(function() { paragraph.edit = false; })
+ .catch(_handleException);
+ }
+ else
+ paragraph.edit = false;
};
- $scope.nextPage = function(paragraph) {
- _showLoading(paragraph, true);
+ $scope.addParagraph = function() {
+ const sz = $scope.notebook.paragraphs.length;
- paragraph.queryArgs.pageSize = paragraph.pageSize;
+ const paragraph = {
+ id: 'paragraph-' + paragraphId++,
+ name: 'Query' + (sz === 0 ? '' : sz),
+ query: '',
+ pageSize: $scope.pageSizes[0],
+ timeLineSpan: $scope.timeLineSpans[0],
+ result: 'none',
+ rate: {
+ value: 1,
+ unit: 60000,
+ installed: false
+ }
+ };
- agentMonitor.next(paragraph.queryId, paragraph.pageSize)
- .then(function (res) {
- paragraph.page++;
+ enhanceParagraph(paragraph);
- paragraph.total += paragraph.rows.length;
+ if ($scope.caches && $scope.caches.length > 0)
+ paragraph.cacheName = $scope.caches[0].name;
- paragraph.rows = res.items;
+ $scope.notebook.paragraphs.push(paragraph);
- if (paragraph.chart()) {
- if (paragraph.result == 'pie')
- _updatePieChartsWithData(paragraph, _pieChartDatum(paragraph));
- else
- _updateChartsWithData(paragraph, _chartDatum(paragraph));
- }
+ $scope.notebook.expandedParagraphs.push(sz);
- paragraph.gridOptions.setRows(paragraph.rows);
+ $scope.rebuildScrollParagraphs();
- _showLoading(paragraph, false);
+ $location.hash(paragraph.id);
- if (res.last)
- delete paragraph.queryId;
- })
- .catch((err) => {
- paragraph.errMsg = err.message;
+ $anchorScroll();
- _showLoading(paragraph, false);
- })
- .finally(function () {
- paragraph.ace.focus();
- });
+ setTimeout(function() {
+ paragraph.ace.focus();
+ });
};
- var _fullColName = function(col) {
- var res = [];
-
- if (col.schemaName)
- res.push(col.schemaName);
- if (col.typeName)
- res.push(col.typeName);
+ function _saveChartSettings(paragraph) {
+ if (!_.isEmpty(paragraph.charts)) {
+ const chart = paragraph.charts[0].api.getScope().chart;
- res.push(col.fieldName);
+ if (!$common.isDefined(paragraph.chartsOptions))
+ paragraph.chartsOptions = {barChart: {stacked: true}, areaChart: {style: 'stack'}};
- return res.join('.');
- };
+ switch (paragraph.result) {
+ case 'bar':
+ paragraph.chartsOptions.barChart.stacked = chart.stacked();
- const _export = (fileName, columnFilter, meta, rows) => {
- let csvContent = '';
+ break;
- const cols = [];
- const excludedCols = [];
+ case 'area':
+ paragraph.chartsOptions.areaChart.style = chart.style();
- if (meta) {
- _.forEach(meta, (col, idx) => {
- if (columnFilter(col))
- cols.push(_fullColName(col));
- else
- excludedCols.push(idx);
- });
+ break;
- csvContent += cols.join(';') + '\n';
+ default:
+ }
}
+ }
- _.forEach(rows, (row) => {
- cols.length = 0;
+ $scope.setResult = function(paragraph, new_result) {
+ if (paragraph.result === new_result)
+ return;
- if (Array.isArray(row)) {
- _.forEach(row, (elem, idx) => {
- if (_.includes(excludedCols, idx))
- return;
+ _saveChartSettings(paragraph);
- cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
+ paragraph.result = new_result;
+
+ if (paragraph.chart())
+ _chartApplySettings(paragraph, true);
+ else
+ $timeout(() => paragraph.gridOptions.api.core.handleWindowResize());
+ };
+
+ $scope.resultEq = function(paragraph, result) {
+ return (paragraph.result === result);
+ };
+
+ $scope.removeParagraph = function(paragraph) {
+ $confirm.confirm('Are you sure you want to remove: "' + paragraph.name + '"?')
+ .then(function() {
+ $scope.stopRefresh(paragraph);
+
+ const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
+ return paragraph === item;
});
- }
- else {
- _.forEach(meta, (col) => {
- if (columnFilter(col)) {
- const elem = row[col.fieldName];
- cols.push(_.isUndefined(elem) ? '' : JSON.stringify(elem));
- }
+ const panel_idx = _.findIndex($scope.expandedParagraphs, function(item) {
+ return paragraph_idx === item;
});
- }
- csvContent += cols.join(';') + '\n';
- });
+ if (panel_idx >= 0)
+ $scope.expandedParagraphs.splice(panel_idx, 1);
- $common.download('application/octet-stream;charset=utf-8', fileName, escape(csvContent));
- };
+ $scope.notebook.paragraphs.splice(paragraph_idx, 1);
- $scope.exportCsv = function(paragraph) {
- _export(paragraph.name + '.csv', paragraph.columnFilter, paragraph.meta, paragraph.rows);
+ $scope.rebuildScrollParagraphs();
- //paragraph.gridOptions.api.exporter.csvExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
+ QueryNotebooks.save($scope.notebook)
+ .catch(_handleException);
+ });
};
- $scope.exportPdf = function(paragraph) {
- paragraph.gridOptions.api.exporter.pdfExport(uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE);
- };
+ $scope.paragraphExpanded = function(paragraph) {
+ const paragraph_idx = _.findIndex($scope.notebook.paragraphs, function(item) {
+ return paragraph === item;
+ });
- $scope.exportCsvAll = function (paragraph) {
- const args = paragraph.queryArgs;
+ const panel_idx = _.findIndex($scope.notebook.expandedParagraphs, function(item) {
+ return paragraph_idx === item;
+ });
- agentMonitor.queryGetAll(args.cacheName, args.query)
- .then((res) => _export(paragraph.name + '-all.csv', paragraph.columnFilter, res.fieldsMetadata, res.items))
- .finally(() => paragraph.ace.focus());
+ return panel_idx >= 0;
};
- $scope.exportPdfAll = function(paragraph) {
- //$http.post('/api/v1/agent/query/getAll', {query: paragraph.query, cacheName: paragraph.cacheName})
- // .success(function (item) {
- // _export(paragraph.name + '-all.csv', item.meta, item.rows);
- // })
- // .error(function (errMsg) {
- // $common.showError(errMsg);
- // });
+ const _columnFilter = function(paragraph) {
+ return paragraph.disabledSystemColumns || paragraph.systemColumns ? _allColumn : _hideColumn;
};
- $scope.rateAsString = function (paragraph) {
- if (paragraph.rate && paragraph.rate.installed) {
- var idx = _.findIndex($scope.timeUnit, function (unit) {
- return unit.value == paragraph.rate.unit;
- });
+ const _notObjectType = function(cls) {
+ return $common.isJavaBuiltInClass(cls);
+ };
- if (idx >= 0)
- return ' ' + paragraph.rate.value + $scope.timeUnit[idx].short;
+ function _retainColumns(allCols, curCols, acceptableType, xAxis, unwantedCols) {
+ const retainedCols = [];
- paragraph.rate.installed = false;
- }
+ const availableCols = xAxis ? allCols : _.filter(allCols, function(col) {
+ return col.value >= 0;
+ });
- return '';
- };
+ if (availableCols.length > 0) {
+ curCols.forEach(function(curCol) {
+ const col = _.find(availableCols, {label: curCol.label});
- var _cancelRefresh = function (paragraph) {
- if (paragraph.rate && paragraph.rate.stopTime) {
- delete paragraph.queryArgs;
+ if (col && acceptableType(col.type)) {
+ col.aggFx = curCol.aggFx;
- paragraph.rate.installed = false;
+ retainedCols.push(col);
+ }
+ });
- $interval.cancel(paragraph.rate.stopTime);
+ // If nothing was restored, add first acceptable column.
+ if (_.isEmpty(retainedCols)) {
+ let col;
- delete paragraph.rate.stopTime;
- }
- };
+ if (unwantedCols)
+ col = _.find(availableCols, (avCol) => !_.find(unwantedCols, {label: avCol.label}) && acceptableType(avCol.type));
- var _tryStopRefresh = function (paragraph) {
- if (paragraph.rate && paragraph.rate.stopTime) {
- $interval.cancel(paragraph.rate.stopTime);
+ if (!col)
+ col = _.find(availableCols, (avCol) => acceptableType(avCol.type));
- delete paragraph.rate.stopTime;
+ if (col)
+ retainedCols.push(col);
+ }
}
- };
- var _tryStartRefresh = function (paragraph) {
- _tryStopRefresh(paragraph);
+ return retainedCols;
+ }
- if (paragraph.rate && paragraph.rate.installed && paragraph.queryArgs) {
- $scope.chartAcceptKeyColumn(paragraph, TIME_LINE);
+ const _rebuildColumns = function(paragraph) {
+ _.forEach(_.groupBy(paragraph.meta, 'fieldName'), function(colsByName, fieldName) {
+ const colsByTypes = _.groupBy(colsByName, 'typeName');
- _executeRefresh(paragraph);
+ const needType = _.keys(colsByTypes).length > 1;
- var delay = paragraph.rate.value * paragraph.rate.unit;
+ _.forEach(colsByTypes, function(colsByType, typeName) {
+ _.forEach(colsByType, function(col, ix) {
+ col.fieldName = (needType && !$common.isEmptyString(typeName) ? typeName + '.' : '') + fieldName + (ix > 0 ? ix : '');
+ });
+ });
+ });
- paragraph.rate.stopTime = $interval(_executeRefresh, delay, 0, false, paragraph);
- }
- };
+ const cols = [];
- $scope.startRefresh = function (paragraph, value, unit) {
- paragraph.rate.value = value;
- paragraph.rate.unit = unit;
- paragraph.rate.installed = true;
+ _.forEach(paragraph.meta, (col, idx) => {
+ if (paragraph.columnFilter(col)) {
+ col.field = paragraph.queryArgs.query ? idx.toString() : col.fieldName;
- if (paragraph.queryExecuted())
- _tryStartRefresh(paragraph);
- };
+ cols.push(col);
+ }
+ });
- $scope.stopRefresh = function (paragraph) {
- paragraph.rate.installed = false;
+ paragraph.gridOptions.updateColumns(cols);
- _tryStopRefresh(paragraph);
- };
+ paragraph.chartColumns = _.reduce(cols, (acc, col) => {
+ if (_notObjectType(col.fieldTypeName)) {
+ acc.push({
+ label: col.fieldName,
+ type: col.fieldTypeName,
+ aggFx: $scope.aggregateFxs[0],
+ value: col.field
+ });
+ }
- function _chartNumber(arr, idx, dflt) {
- if (idx >= 0 && arr && arr.length > idx && _.isNumber(arr[idx]))
- return arr[idx];
+ return acc;
+ }, []);
- return dflt;
- }
+ if (paragraph.chartColumns.length > 0) {
+ paragraph.chartColumns.push(TIME_LINE);
+ paragraph.chartColumns.push(ROW_IDX);
+ }
- function _chartLabel(arr, idx, dflt) {
- if (arr && arr.length > idx && _.isString(arr[idx]))
- return arr[idx];
+ // We could accept onl not object columns for X axis.
+ paragraph.chartKeyCols = _retainColumns(paragraph.chartColumns, paragraph.chartKeyCols, _notObjectType, true);
+
+ // We could accept only numeric columns for Y axis.
+ paragraph.chartValCols = _retainColumns(paragraph.chartColumns, paragraph.chartValCols, _numberType, false, paragraph.chartKeyCols);
+ };
+
+ $scope.toggleSystemColumns = function(paragraph) {
+ if (paragraph.disabledSystemColumns)
+ return;
+
+ paragraph.systemColumns = !paragraph.systemColumns;
- return dflt;
- }
+ paragraph.columnFilter = _columnFilter(paragraph);
- function _min(rows, idx, dflt) {
- var min = _chartNumber(rows[0], idx, dflt);
+ paragraph.chartColumns = [];
- _.forEach(rows, function (row) {
- var v = _chartNumber(row, idx, dflt);
+ _rebuildColumns(paragraph);
+ };
- if (v < min)
- min = v;
- });
+ const _showLoading = (paragraph, enable) => paragraph.loading = enable;
- return min;
- }
+ /**
+ * @param {Object} paragraph Query
+ * @param {{fieldsMetadata: Array, items: Array, queryId: int, last: Boolean}} res Query results.
+ * @private
+ */
+ const _processQueryResult = function(paragraph, res) {
+ const prevKeyCols = paragraph.chartKeyCols;
+ const prevValCols = paragraph.chartValCols;
- function _max(rows, idx, dflt) {
- var max = _chartNumber(rows[0], idx, dflt);
+ if (!_.eq(paragraph.meta, res.fieldsMetadata)) {
+ paragraph.meta = [];
- _.forEach(rows, function (row) {
- var v = _chartNumber(row, idx, dflt);
+ paragraph.chartColumns = [];
- if (v > max)
- max = v;
- });
+ if (!$common.isDefined(paragraph.chartKeyCols))
+ paragraph.chartKeyCols = [];
- return max;
- }
+ if (!$common.isDefined(paragraph.chartValCols))
+ paragraph.chartValCols = [];
- function _sum(rows, idx) {
- var sum = 0;
+ if (res.fieldsMetadata.length <= 2) {
+ const _key = _.find(res.fieldsMetadata, {fieldName: '_KEY'});
+ const _val = _.find(res.fieldsMetadata, {fieldName: '_VAL'});
- _.forEach(rows, function (row) {
- sum += _chartNumber(row, idx, 0);
- });
+ paragraph.disabledSystemColumns = (res.fieldsMetadata.length === 2 && _key && _val) ||
+ (res.fieldsMetadata.length === 1 && (_key || _val));
+ }
- return sum;
- }
+ paragraph.columnFilter = _columnFilter(paragraph);
- function _aggregate(rows, aggFx, idx, dflt) {
- var len = rows.length;
+ paragraph.meta = res.fieldsMetadata;
- switch (aggFx) {
- case 'FIRST':
- return _chartNumber(rows[0], idx, dflt);
+ _rebuildColumns(paragraph);
+ }
- case 'LAST':
- return _chartNumber(rows[len - 1], idx, dflt);
+ paragraph.page = 1;
- case 'MIN':
- return _min(rows, idx, dflt);
+ paragraph.total = 0;
- case 'MAX':
- return _max(rows, idx, dflt);
+ paragraph.queryId = res.last ? null : res.queryId;
- case 'SUM':
- return _sum(rows, idx);
+ delete paragraph.errMsg;
- case 'AVG':
- return len > 0 ? _sum(rows, idx) / len : 0;
+ // Prepare explain results for display in table.
+ if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.items) {
+ paragraph.rows = [];
- case 'COUNT':
- return len;
+ res.items.forEach(function(row, i) {
+ const line = res.items.length - 1 === i ? row[0] : row[0] + '\n';
+
+ line.replace(/\"/g, '').split('\n').forEach((ln) => paragraph.rows.push([ln]));
+ });
}
+ else
+ paragraph.rows = res.items;
- return 0;
- }
+ paragraph.gridOptions.updateRows(paragraph.rows);
- function _chartDatum(paragraph) {
- var datum = [];
+ const chartHistory = paragraph.chartHistory;
- if (paragraph.chartColumnsConfigured()) {
- paragraph.chartValCols.forEach(function (valCol) {
- var index = 0;
- var values = [];
- var colIdx = valCol.value;
+ // Clear history on query change.
+ const queryChanged = paragraph.prevQuery !== paragraph.query;
- if (paragraph.chartTimeLineEnabled()) {
- var aggFx = valCol.aggFx;
- var colLbl = valCol.label + ' [' + aggFx + ']';
+ if (queryChanged) {
+ paragraph.prevQuery = paragraph.query;
- if (paragraph.charts && paragraph.charts.length == 1)
- datum = paragraph.charts[0].data;
+ chartHistory.length = 0;
- var chartData = _.find(datum, {series: valCol.label});
+ _.forEach(paragraph.charts, (chart) => chart.data.length = 0);
+ }
- var leftBound = new Date();
- leftBound.setMinutes(leftBound.getMinutes() - parseInt(paragraph.timeLineSpan));
+ // Add results to history.
+ chartHistory.push({tm: new Date(), rows: paragraph.rows});
- if (chartData) {
- var lastItem = _.last(paragraph.chartHistory);
+ // Keep history size no more than max length.
+ while (chartHistory.length > HISTORY_LENGTH)
+ chartHistory.shift();
- values = chartData.values;
+ _showLoading(paragraph, false);
- values.push({
- x: lastItem.tm,
- y: _aggregate(lastItem.rows, aggFx, colIdx, index++)
- });
+ if (paragraph.result === 'none' || !paragraph.queryExecuted())
+ paragraph.result = 'table';
+ else if (paragraph.chart()) {
+ let resetCharts = queryChanged;
- while (values.length > 0 && values[0].x < leftBound)
- values.shift();
- }
- else {
- _.forEach(paragraph.chartHistory, function (history) {
- if (history.tm >= leftBound)
- values.push({
- x: history.tm,
- y: _aggregate(history.rows, aggFx, colIdx, index++)
- });
- });
+ if (!resetCharts) {
+ const curKeyCols = paragraph.chartKeyCols;
+ const curValCols = paragraph.chartValCols;
- datum.push({series: valCol.label, key: colLbl, values: values});
- }
- }
- else {
- index = paragraph.total;
+ resetCharts = !prevKeyCols || !prevValCols ||
+ prevKeyCols.length !== curKeyCols.length ||
+ prevValCols.length !== curValCols.length;
+ }
- values = _.map(paragraph.rows, function (row) {
- var xCol = paragraph.chartKeyCols[0].value;
+ _chartApplySettings(paragraph, resetCharts);
+ }
+ };
- var v = {
- x: _chartNumber(row, xCol, index),
- xLbl: _chartLabel(row, xCol, undefined),
- y: _chartNumber(row, colIdx, index)
- };
+ const _closeOldQuery = (paragraph) => {
+ const queryId = paragraph.queryArgs && paragraph.queryArgs.queryId;
- index++;
+ return queryId ? agentMonitor.queryClose(queryId) : $q.when();
+ };
- return v;
- });
+ const _executeRefresh = (paragraph) => {
+ const args = paragraph.queryArgs;
- datum.push({series: valCol.label, key: valCol.label, values: values});
- }
- });
- }
+ agentMonitor.awaitAgent()
+ .then(() => _closeOldQuery(paragraph))
+ .then(() => agentMonitor.query(args.cacheName, args.pageSize, args.query))
+ .then(_processQueryResult.bind(this, paragraph))
+ .catch((err) => paragraph.errMsg = err.message);
+ };
- return datum;
- }
+ const _tryStartRefresh = function(paragraph) {
+ _tryStopRefresh(paragraph);
- function _pieChartDatum(paragraph) {
- var datum = [];
+ if (paragraph.rate && paragraph.rate.installed && paragraph.queryArgs) {
+ $scope.chartAcceptKeyColumn(paragraph, TIME_LINE);
- if (paragraph.chartColumnsConfigured() && !paragraph.chartTimeLineEnabled()) {
- paragraph.chartValCols.forEach(function (valCol) {
- var index = paragraph.total;
+ _executeRefresh(paragraph);
- var values = _.map(paragraph.rows, function (row) {
- var xCol = paragraph.chartKeyCols[0].value;
+ const delay = paragraph.rate.value * paragraph.rate.unit;
- var v = {
- x: xCol < 0 ? index : row[xCol],
- y: _chartNumber(row, valCol.value, index)
- };
+ paragraph.rate.stopTime = $interval(_executeRefresh, delay, 0, false, paragraph);
+ }
+ };
- index++;
+ $scope.execute = function(paragraph) {
+ QueryNotebooks.save($scope.notebook)
+ .catch(_handleException);
- return v;
- });
+ paragraph.prevQuery = paragraph.queryArgs ? paragraph.queryArgs.query : paragraph.query;
- datum.push({series: paragraph.chartKeyCols[0].label, key: valCol.label, values: values});
- });
- }
+ _showLoading(paragraph, true);
- return datum;
- }
+ _closeOldQuery(paragraph)
+ .then(function() {
+ const args = paragraph.queryArgs = {
+ cacheName: paragraph.cacheName,
+ pageSize: paragraph.pageSize,
+ query: paragraph.query
+ };
- $scope.paragraphTimeSpanVisible = function (paragraph) {
- return paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled();
- };
+ return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+ })
+ .then(function(res) {
+ _processQueryResult(paragraph, res);
- $scope.paragraphTimeLineSpan = function (paragraph) {
- if (paragraph && paragraph.timeLineSpan)
- return paragraph.timeLineSpan.toString();
+ _tryStartRefresh(paragraph);
+ })
+ .catch((err) => {
+ paragraph.errMsg = err.message;
- return '1';
- };
+ _showLoading(paragraph, false);
- function _saveChartSettings(paragraph) {
- if (!_.isEmpty(paragraph.charts)) {
- var chart = paragraph.charts[0].api.getScope().chart;
+ $scope.stopRefresh(paragraph);
+ })
+ .finally(() => paragraph.ace.focus());
+ };
- if (!$common.isDefined(paragraph.chartsOptions))
- paragraph.chartsOptions = {barChart: {stacked: true}, areaChart: {style: 'stack'}};
+ $scope.queryExecuted = function(paragraph) {
+ return $common.isDefined(paragraph.queryArgs);
+ };
- switch (paragraph.result) {
- case 'bar':
- paragraph.chartsOptions.barChart.stacked = chart.stacked();
+ const _cancelRefresh = function(paragraph) {
+ if (paragraph.rate && paragraph.rate.stopTime) {
+ delete paragraph.queryArgs;
- break;
+ paragraph.rate.installed = false;
- case 'area':
- paragraph.chartsOptions.areaChart.style = chart.style();
+ $interval.cancel(paragraph.rate.stopTime);
- break;
- }
+ delete paragraph.rate.stopTime;
}
- }
+ };
- function _chartApplySettings(paragraph, resetCharts) {
- if (resetCharts)
- paragraph.charts = [];
+ $scope.explain = function(paragraph) {
+ QueryNotebooks.save($scope.notebook)
+ .catch(_handleException);
- if (paragraph.chart() && paragraph.nonEmpty()) {
- switch (paragraph.result) {
- case 'bar':
- _barChart(paragraph);
- break;
+ _cancelRefresh(paragraph);
- case 'pie':
- _pieChart(paragraph);
- break;
+ _showLoading(paragraph, true);
- case 'line':
- _lineChart(paragraph);
- break;
+ _closeOldQuery(paragraph)
+ .then(function() {
+ const args = paragraph.queryArgs = {
+ cacheName: paragraph.cacheName,
+ pageSize: paragraph.pageSize,
+ query: 'EXPLAIN ' + paragraph.query
+ };
- case 'area':
- _areaChart(paragraph);
- break;
- }
- }
- }
+ return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+ })
+ .then(_processQueryResult.bind(this, paragraph))
+ .catch((err) => {
+ paragraph.errMsg = err.message;
- $scope.applyChartSettings = function (paragraph) {
- _chartApplySettings(paragraph, true);
+ _showLoading(paragraph, false);
+ })
+ .finally(() => paragraph.ace.focus());
};
- function _xAxisLabel(paragraph) {
- return _.isEmpty(paragraph.chartKeyCols) ? 'X' : paragraph.chartKeyCols[0].label;
- }
+ $scope.scan = function(paragraph) {
+ QueryNotebooks.save($scope.notebook)
+ .catch(_handleException);
+
+ _cancelRefresh(paragraph);
+
+ _showLoading(paragraph, true);
+
+ _closeOldQuery(paragraph)
+ .then(() => {
+ const args = paragraph.queryArgs = {
+ cacheName: paragraph.cacheName,
+ pageSize: paragraph.pageSize
+ };
- function _yAxisLabel(paragraph) {
- var cols = paragraph.chartValCols;
+ return agentMonitor.query(args.cacheName, args.pageSize);
+ })
+ .then(_processQueryResult.bind(this, paragraph))
+ .catch((err) => {
+ paragraph.errMsg = err.message;
- var tml = paragraph.chartTimeLineEnabled();
+ _showLoading(paragraph, false);
+ })
+ .finally(() => paragraph.ace.focus());
+ };
- return _.isEmpty(cols) ? 'Y' : _.map(cols, function (col) {
- var lbl = col.label;
+ function _updatePieChartsWithData(paragraph, newDatum) {
+ $timeout(() => {
+ _.forEach(paragraph.charts, function(chart) {
+ const chartDatum = chart.data;
- if (tml)
- lbl += ' [' + col.aggFx + ']';
+ chartDatum.length = 0;
- return lbl;
- }).join(', ');
- }
+ _.forEach(newDatum, function(series) {
+ if (chart.options.title.text === series.key)
+ _.forEach(series.values, (v) => chartDatum.push(v));
+ });
+ });
- function _xX(d) {
- return d.x;
+ _.forEach(paragraph.charts, (chart) => chart.api.update());
+ });
}
- function _yY(d) {
- return d.y;
- }
+ $scope.nextPage = function(paragraph) {
+ _showLoading(paragraph, true);
- function _xAxisTimeFormat(d) {
- return d3.time.format('%X')(new Date(d));
- }
+ paragraph.queryArgs.pageSize = paragraph.pageSize;
- var _xAxisWithLabelFormat = function(paragraph) {
- return function (d) {
- var values = paragraph.charts[0].data[0].values;
+ agentMonitor.next(paragraph.queryId, paragraph.pageSize)
+ .then(function(res) {
+ paragraph.page++;
- var fmt = _intType(paragraph.chartKeyCols[0].type) ? 'd' : ',.2f';
+ paragraph.total += paragraph.rows.length;
- var dx = values[d];
+ paragraph.rows = res.items;
- if (!dx)
- return d3.format(fmt)(d);
+ if (paragraph.chart()) {
+ if (paragraph.result === 'pie')
+ _updatePieChartsWithData(paragraph, _pieChartDatum(paragraph));
+ else
+ _updateChartsWithData(paragraph, _chartDatum(paragraph));
+ }
- var lbl = dx.xLbl;
+ paragraph.gridOptions.updateRows(paragraph.rows);
- return lbl ? lbl : d3.format(fmt)(d);
- }
- };
+ _showLoading(paragraph, false);
- var _yAxisFormat = function(d) {
- var fmt = d < 1000 ? ',.2f' : '.3s';
+ if (res.last)
+ delete paragraph.queryId;
+ })
+ .catch((err) => {
+ paragraph.errMsg = err.message;
- return d3.format(fmt)(d);
+ _showLoading(paragraph, false);
+ })
+ .finally(() => paragraph.ace.focus());
};
- function _updateCharts(paragraph) {
- $timeout(function () {
- _.forEach(paragraph.charts, function (chart) {
- chart.api.update();
- });
- }, 100);
- }
+ const _export = (fileName, columnFilter, meta, rows) =>
<TRUNCATED>