You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2016/09/08 04:46:23 UTC
[06/50] [abbrv] ignite git commit: IGNITE-3706 Added query duration,
scan with filter on SQL screen.
IGNITE-3706 Added query duration, scan with filter on SQL screen.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/0a1fa244
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/0a1fa244
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/0a1fa244
Branch: refs/heads/ignite-3629
Commit: 0a1fa244e2f3a17b2962b1e6e8a7e6a01c0a090e
Parents: 9e45dee
Author: Andrey Novikov <an...@apache.org>
Authored: Wed Aug 24 14:54:23 2016 +0700
Committer: Andrey Novikov <an...@apache.org>
Committed: Wed Aug 24 14:54:23 2016 +0700
----------------------------------------------------------------------
modules/web-console/DEVNOTES.txt | 4 +-
modules/web-console/backend/app/agent.js | 61 +--
modules/web-console/backend/app/browser.js | 36 +-
modules/web-console/frontend/app/app.js | 2 +
.../frontend/app/filters/duration.filter.js | 38 ++
.../frontend/app/modules/agent/agent.module.js | 43 +-
.../app/modules/sql/scan-filter-input.jade | 39 ++
.../modules/sql/scan-filter-input.service.js | 52 ++
.../frontend/app/modules/sql/sql.controller.js | 482 +++++++++++--------
.../frontend/app/modules/sql/sql.module.js | 2 +
.../frontend/gulpfile.babel.js/paths.js | 3 +-
.../frontend/public/stylesheets/style.scss | 74 ++-
modules/web-console/frontend/views/sql/sql.jade | 64 ++-
13 files changed, 546 insertions(+), 354 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/DEVNOTES.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/DEVNOTES.txt b/modules/web-console/DEVNOTES.txt
index 0b35ab0..150041e 100644
--- a/modules/web-console/DEVNOTES.txt
+++ b/modules/web-console/DEVNOTES.txt
@@ -16,7 +16,7 @@ How to deploy locally:
Check npm version: "npm --version".
5. Run "npm install --no-optional" in terminal for download dependencies.
6. Build ignite-web-agent module follow instructions from 'modules/web-agent/README.txt'.
-7. Copy ignite-web-agent-<version>.zip from target of ignite-web-agent module to 'modules/web-console/src/main/js/serve/agent_dists' folder.
+7. Copy ignite-web-agent-<version>.zip from target of ignite-web-agent module to 'modules/web-console/backend/agent_dists' folder.
Steps 1 - 7 should be executed once.
@@ -29,6 +29,6 @@ How to run console in development mode:
If needed run "npm install --no-optional" (if dependencies changed) and run "npm start" to start backend.
3. In new terminal change directory to '$IGNITE_HOME/modules/web-console/frontend'
- and start webpack in development mode "npm run dev" .
+ and start webpack in development mode "npm run dev".
4. In browser open: http://localhost:9000
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/backend/app/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/app/agent.js b/modules/web-console/backend/app/agent.js
index 0f4eb9f..a1858fd 100644
--- a/modules/web-console/backend/app/agent.js
+++ b/modules/web-console/backend/app/agent.js
@@ -222,59 +222,64 @@ module.exports.factory = function(_, ws, fs, path, JSZip, socketio, settings, mo
}
/**
- *
* @param {Boolean} demo Is need run command on demo node.
+ * @param {String} nid Node id.
* @param {String} cacheName Cache name.
* @param {String} query Query.
+ * @param {Boolean} local Flag whether to execute query locally.
* @param {int} pageSize Page size.
* @returns {Promise}
*/
- fieldsQuery(demo, cacheName, query, pageSize) {
- const cmd = new Command(demo, 'qryfldexe')
- .addParam('cacheName', cacheName)
- .addParam('qry', query)
- .addParam('pageSize', pageSize);
-
- return this.executeRest(cmd);
- }
-
- /**
- *
- * @param {Boolean} demo Is need run command on demo node.
- * @param {String} cacheName Cache name.
- * @param {int} pageSize Page size.
- * @returns {Promise}
- */
- scan(demo, cacheName, pageSize) {
- const cmd = new Command(demo, 'qryscanexe')
- .addParam('cacheName', cacheName)
- .addParam('pageSize', pageSize);
+ fieldsQuery(demo, nid, cacheName, query, local, pageSize) {
+ const cmd = new Command(demo, 'exe')
+ .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+ .addParam('p1', nid)
+ .addParam('p2', 'org.apache.ignite.internal.visor.query.VisorQueryTask')
+ .addParam('p3', 'org.apache.ignite.internal.visor.query.VisorQueryArg')
+ .addParam('p4', cacheName)
+ .addParam('p5', query)
+ .addParam('p6', local)
+ .addParam('p7', pageSize);
return this.executeRest(cmd);
}
/**
* @param {Boolean} demo Is need run command on demo node.
+ * @param {String} nid Node id.
* @param {int} queryId Query Id.
* @param {int} pageSize Page size.
* @returns {Promise}
*/
- queryFetch(demo, queryId, pageSize) {
- const cmd = new Command(demo, 'qryfetch')
- .addParam('qryId', queryId)
- .addParam('pageSize', pageSize);
+ queryFetch(demo, nid, queryId, pageSize) {
+ const cmd = new Command(demo, 'exe')
+ .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+ .addParam('p1', nid)
+ .addParam('p2', 'org.apache.ignite.internal.visor.query.VisorQueryNextPageTask')
+ .addParam('p3', 'org.apache.ignite.lang.IgniteBiTuple')
+ .addParam('p4', 'java.lang.String')
+ .addParam('p5', 'java.lang.Integer')
+ .addParam('p6', queryId)
+ .addParam('p7', pageSize);
return this.executeRest(cmd);
}
/**
* @param {Boolean} demo Is need run command on demo node.
+ * @param {String} nid Node id.
* @param {int} queryId Query Id.
* @returns {Promise}
*/
- queryClose(demo, queryId) {
- const cmd = new Command(demo, 'qrycls')
- .addParam('qryId', queryId);
+ queryClose(demo, nid, queryId) {
+ const cmd = new Command(demo, 'exe')
+ .addParam('name', 'org.apache.ignite.internal.visor.compute.VisorGatewayTask')
+ .addParam('p1', '')
+ .addParam('p2', 'org.apache.ignite.internal.visor.query.VisorQueryCleanupTask')
+ .addParam('p3', 'java.util.Map')
+ .addParam('p4', 'java.util.UUID')
+ .addParam('p5', 'java.util.Set')
+ .addParam('p6', `${nid}=${queryId}`);
return this.executeRest(cmd);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/backend/app/browser.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/app/browser.js b/modules/web-console/backend/app/browser.js
index 1c5058d..3256b6a 100644
--- a/modules/web-console/backend/app/browser.js
+++ b/modules/web-console/backend/app/browser.js
@@ -90,53 +90,53 @@ module.exports.factory = (_, socketio, agentMgr, configure) => {
});
// Close query on node.
- socket.on('node:query:close', (queryId, cb) => {
+ socket.on('node:query:close', (nid, queryId, cb) => {
agentMgr.findAgent(accountId())
- .then((agent) => agent.queryClose(demo, queryId))
+ .then((agent) => agent.queryClose(demo, nid, queryId))
.then(() => cb())
.catch((err) => cb(_errorToJson(err)));
});
// Execute query on node and return first page to browser.
- socket.on('node:query', (cacheName, pageSize, query, cb) => {
+ socket.on('node:query', (nid, cacheName, query, local, pageSize, cb) => {
agentMgr.findAgent(accountId())
- .then((agent) => {
- if (query === null)
- return agent.scan(demo, cacheName, pageSize);
-
- return agent.fieldsQuery(demo, cacheName, query, pageSize);
- })
+ .then((agent) => agent.fieldsQuery(demo, nid, cacheName, query, local, pageSize))
.then((res) => cb(null, res))
.catch((err) => cb(_errorToJson(err)));
});
// Fetch next page for query and return result to browser.
- socket.on('node:query:fetch', (queryId, pageSize, cb) => {
+ socket.on('node:query:fetch', (nid, queryId, pageSize, cb) => {
agentMgr.findAgent(accountId())
- .then((agent) => agent.queryFetch(demo, queryId, pageSize))
+ .then((agent) => agent.queryFetch(demo, nid, queryId, pageSize))
.then((res) => cb(null, res))
.catch((err) => cb(_errorToJson(err)));
});
// Execute query on node and return full result to browser.
- socket.on('node:query:getAll', (cacheName, query, cb) => {
+ socket.on('node:query:getAll', (nid, cacheName, query, local, cb) => {
// Set page size for query.
const pageSize = 1024;
agentMgr.findAgent(accountId())
.then((agent) => {
- const firstPage = query === null ? agent.scan(demo, cacheName, pageSize)
- : agent.fieldsQuery(demo, cacheName, query, pageSize);
+ const firstPage = agent.fieldsQuery(demo, nid, cacheName, query, local, pageSize)
+ .then(({result}) => {
+ if (result.key)
+ return Promise.reject(result.key);
+
+ return result.value;
+ });
const fetchResult = (acc) => {
- if (acc.last)
+ if (!acc.hasMore)
return acc;
- return agent.queryFetch(demo, acc.queryId, pageSize)
+ return agent.queryFetch(demo, acc.responseNodeId, acc.queryId, pageSize)
.then((res) => {
- acc.items = acc.items.concat(res.items);
+ acc.rows = acc.rows.concat(res.rows);
- acc.last = res.last;
+ acc.hasMore = res.hasMore;
return fetchResult(acc);
});
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/app.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/app.js b/modules/web-console/frontend/app/app.js
index 89381aa..7340d7b 100644
--- a/modules/web-console/frontend/app/app.js
+++ b/modules/web-console/frontend/app/app.js
@@ -89,6 +89,7 @@ import UnsavedChangesGuard from './services/UnsavedChangesGuard.service';
import byName from './filters/byName.filter';
import domainsValidation from './filters/domainsValidation.filter';
import hasPojo from './filters/hasPojo.filter';
+import duration from './filters/duration.filter';
// Generators
import $generatorCommon from 'generator/generator-common';
@@ -215,6 +216,7 @@ angular
.filter(...hasPojo)
.filter(...domainsValidation)
.filter(...byName)
+.filter(...duration)
.config(['$stateProvider', '$locationProvider', '$urlRouterProvider', ($stateProvider, $locationProvider, $urlRouterProvider) => {
// Set up the states.
$stateProvider
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/filters/duration.filter.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/filters/duration.filter.js b/modules/web-console/frontend/app/filters/duration.filter.js
new file mode 100644
index 0000000..deeedd7
--- /dev/null
+++ b/modules/web-console/frontend/app/filters/duration.filter.js
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export default ['duration', [() => {
+ /**
+ * @param {Number} t Time in ms.
+ */
+ return (t) => {
+ const a = (i, suffix) => i && i !== '00' ? i + suffix + ' ' : '';
+
+ const cd = 24 * 60 * 60 * 1000;
+ const ch = 60 * 60 * 1000;
+ const cm = 60 * 1000;
+ const cs = 1000;
+
+ const d = Math.floor(t / cd);
+ const h = Math.floor((t - d * cd) / ch);
+ const m = Math.floor((t - d * cd - h * ch) / cm);
+ const s = Math.floor((t - d * cd - h * ch - m * cm) / cs);
+ const ms = t % 1000;
+
+ return a(d, 'd') + a(h, 'h') + a(m, 'm') + a(s, 's') + (t < cm ? ms + 'ms' : '');
+ };
+}]];
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/modules/agent/agent.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/agent/agent.module.js b/modules/web-console/frontend/app/modules/agent/agent.module.js
index ca95166..f65781e 100644
--- a/modules/web-console/frontend/app/modules/agent/agent.module.js
+++ b/modules/web-console/frontend/app/modules/agent/agent.module.js
@@ -18,6 +18,8 @@
import angular from 'angular';
import io from 'socket.io-client'; // eslint-disable-line no-unused-vars
+const maskNull = (val) => _.isEmpty(val) ? 'null' : val;
+
class IgniteAgentMonitor {
constructor(socketFactory, $root, $q, $state, $modal, Messages) {
this._scope = $root.$new();
@@ -260,47 +262,60 @@ class IgniteAgentMonitor {
}
/**
+ * @param {String} nid Node id.
* @param {int} [queryId]
* @returns {Promise}
*/
- queryClose(queryId) {
- return this._rest('node:query:close', queryId);
+ queryClose(nid, queryId) {
+ return this._rest('node:query:close', nid, queryId);
}
/**
+ * @param {String} nid Node id.
* @param {String} cacheName Cache name.
- * @param {int} pageSize
* @param {String} [query] Query if null then scan query.
+ * @param {Boolean} local Flag whether to execute query locally.
+ * @param {int} pageSize
* @returns {Promise}
*/
- query(cacheName, pageSize, query) {
- return this._rest('node:query', _.isEmpty(cacheName) ? null : cacheName, pageSize, query);
+ query(nid, cacheName, query, local, pageSize) {
+ return this._rest('node:query', nid, maskNull(cacheName), maskNull(query), local, pageSize)
+ .then(({result}) => {
+ if (_.isEmpty(result.key))
+ return result.value;
+
+ return Promise.reject(result.key);
+ });
}
/**
+ * @param {String} nid Node id.
* @param {String} cacheName Cache name.
* @param {String} [query] Query if null then scan query.
+ * @param {Boolean} local Flag whether to execute query locally.
* @returns {Promise}
*/
- queryGetAll(cacheName, query) {
- return this._rest('node:query:getAll', _.isEmpty(cacheName) ? null : cacheName, query);
+ queryGetAll(nid, cacheName, query, local) {
+ return this._rest('node:query:getAll', nid, maskNull(cacheName), maskNull(query), local);
}
/**
- * @param {String} [cacheName] Cache name.
+ * @param {String} nid Node id.
+ * @param {int} queryId
+ * @param {int} pageSize
* @returns {Promise}
*/
- metadata(cacheName) {
- return this._rest('node:cache:metadata', _.isEmpty(cacheName) ? null : cacheName);
+ next(nid, queryId, pageSize) {
+ return this._rest('node:query:fetch', nid, queryId, pageSize)
+ .then(({result}) => result);
}
/**
- * @param {int} queryId
- * @param {int} pageSize
+ * @param {String} [cacheName] Cache name.
* @returns {Promise}
*/
- next(queryId, pageSize) {
- return this._rest('node:query:fetch', queryId, pageSize);
+ metadata(cacheName) {
+ return this._rest('node:cache:metadata', maskNull(cacheName));
}
stopWatch() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/modules/sql/scan-filter-input.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/sql/scan-filter-input.jade b/modules/web-console/frontend/app/modules/sql/scan-filter-input.jade
new file mode 100644
index 0000000..addc5f3
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/sql/scan-filter-input.jade
@@ -0,0 +1,39 @@
+//-
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+include ../../helpers/jade/mixins.jade
+
+.modal(tabindex='-1' role='dialog')
+ .modal-dialog
+ .modal-content
+ .modal-header
+ button.close(ng-click='$hide()') ×
+ h4.modal-title Scan filter
+ form.form-horizontal.modal-body.row(name='ui.inputForm' novalidate)
+ .settings-row
+ .col-sm-2
+ label.required.labelFormField Filter:
+ .col-sm-10
+ .input-tip
+ +ignite-form-field-input('"filter"', 'ui.filter', false, 'true', 'Enter filter')(
+ data-ignite-form-field-input-autofocus='true'
+ ignite-on-enter='form.$valid && ok()'
+ )
+ .settings-row
+ +checkbox('Case sensitive search', 'ui.caseSensitive', '"caseSensitive"', 'Select this checkbox for case sensitive search')
+ .modal-footer
+ button.btn.btn-default(id='btn-cancel' ng-click='$hide()') Cancel
+ button.btn.btn-primary(id='btn-scan' ng-disabled='ui.inputForm.$invalid' ng-click='ok()') Scan
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/modules/sql/scan-filter-input.service.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/sql/scan-filter-input.service.js b/modules/web-console/frontend/app/modules/sql/scan-filter-input.service.js
new file mode 100644
index 0000000..d460f04
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/sql/scan-filter-input.service.js
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class ScanFilter {
+ static $inject = ['$rootScope', '$q', '$modal'];
+
+ constructor($root, $q, $modal) {
+ this.deferred = null;
+ this.$q = $q;
+
+ const scope = $root.$new();
+
+ scope.ui = {};
+
+ scope.ok = () => {
+ this.deferred.resolve({filter: scope.ui.filter, caseSensitive: !!scope.ui.caseSensitive});
+
+ this.modal.hide();
+ };
+
+ scope.$hide = () => {
+ this.modal.hide();
+
+ this.deferred.reject();
+ };
+
+ this.modal = $modal({templateUrl: '/scan-filter-input.html', scope, placement: 'center', show: false});
+ }
+
+ open() {
+ this.deferred = this.$q.defer();
+
+ this.modal.$promise.then(this.modal.show);
+
+ return this.deferred.promise;
+ }
+}
+export default ScanFilter;
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/modules/sql/sql.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/sql/sql.controller.js b/modules/web-console/frontend/app/modules/sql/sql.controller.js
index 76db15f..3162224 100644
--- a/modules/web-console/frontend/app/modules/sql/sql.controller.js
+++ b/modules/web-console/frontend/app/modules/sql/sql.controller.js
@@ -15,9 +15,92 @@
* limitations under the License.
*/
+// Time line X axis descriptor.
+const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
+
+// Row index X axis descriptor.
+const ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
+
+/** Prefix for node local key for SCAN near queries. */
+const SCAN_NEAR_CACHE = 'VISOR_SCAN_NEAR_CACHE';
+
+/** Prefix for node local key for SCAN near queries. */
+const SCAN_CACHE_WITH_FILTER = 'VISOR_SCAN_CACHE_WITH_FILTER';
+
+/** Prefix for node local key for SCAN near queries. */
+const SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE = 'VISOR_SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE';
+
+const _fullColName = (col) => {
+ const res = [];
+
+ if (col.schemaName)
+ res.push(col.schemaName);
+
+ if (col.typeName)
+ res.push(col.typeName);
+
+ res.push(col.fieldName);
+
+ return res.join('.');
+};
+
+class Paragraph {
+ constructor($timeout, Notebook, paragraph) {
+ this.$timeout = $timeout;
+ this.Notebook = Notebook;
+
+ _.assign(this, paragraph);
+ }
+
+ resultType() {
+ if (_.isNil(this.queryArgs))
+ return null;
+
+ if (!_.isEmpty(this.errMsg))
+ return 'error';
+
+ if (_.isEmpty(this.rows))
+ return 'empty';
+
+ return this.result === 'table' ? 'table' : 'chart';
+ }
+
+ nonRefresh() {
+ return _.isNil(this.rate) || _.isNil(this.rate.stopTime);
+ }
+
+ table() {
+ return this.result === 'table';
+ }
+
+ chart() {
+ return this.result !== 'table' && this.result !== 'none';
+ }
+
+ nonEmpty() {
+ return this.rows && this.rows.length > 0;
+ }
+
+ queryExecuted() {
+ return this.queryArgs && this.queryArgs.query && !this.queryArgs.query.startsWith('EXPLAIN ');
+ }
+
+ timeLineSupported() {
+ return this.result !== 'pie';
+ }
+
+ chartColumnsConfigured() {
+ return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
+ }
+
+ chartTimeLineEnabled() {
+ return !_.isEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE);
+ }
+}
+
// Controller for SQL notebook screen.
-export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'uiGridConstants', 'uiGridExporterConstants',
- function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, uiGridConstants, uiGridExporterConstants) {
+export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval', '$animate', '$location', '$anchorScroll', '$state', '$modal', '$popover', 'IgniteLoading', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteAgentMonitor', 'IgniteChartColors', 'IgniteNotebook', 'IgniteScanFilterInput', 'uiGridConstants', 'uiGridExporterConstants',
+ function($root, $scope, $http, $q, $timeout, $interval, $animate, $location, $anchorScroll, $state, $modal, $popover, Loading, LegacyUtils, Messages, Confirm, agentMonitor, IgniteChartColors, Notebook, ScanFilterInput, uiGridConstants, uiGridExporterConstants) {
let stopTopology = null;
const _tryStopRefresh = function(paragraph) {
@@ -76,39 +159,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
$scope.maskCacheName = (cacheName) => _.isEmpty(cacheName) ? '<default>' : cacheName;
- // Time line X axis descriptor.
- const TIME_LINE = {value: -1, type: 'java.sql.Date', label: 'TIME_LINE'};
-
- // Row index X axis descriptor.
- const ROW_IDX = {value: -2, type: 'java.lang.Integer', label: 'ROW_IDX'};
-
- const EXTENDED_PARAGRAPH = {
- table() {
- return this.result === 'table';
- },
- chart() {
- return this.result !== 'table' && this.result !== 'none';
- },
- nonEmpty() {
- return this.rows && this.rows.length > 0;
- },
- queryExecuted() {
- return this.queryArgs && this.queryArgs.query && !this.queryArgs.query.startsWith('EXPLAIN ');
- },
- refreshExecuting() {
- return this.rate && this.rate.stopTime;
- },
- timeLineSupported() {
- return this.result !== 'pie';
- },
- chartColumnsConfigured() {
- return !_.isEmpty(this.chartKeyCols) && !_.isEmpty(this.chartValCols);
- },
- chartTimeLineEnabled() {
- return !_.isEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE);
- }
- };
-
// We need max 1800 items to hold history for 30 mins in case of refresh every second.
const HISTORY_LENGTH = 1800;
@@ -653,65 +703,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
let paragraphId = 0;
- const _fullColName = function(col) {
- const res = [];
-
- if (col.schemaName)
- res.push(col.schemaName);
-
- if (col.typeName)
- res.push(col.typeName);
-
- res.push(col.fieldName);
-
- return res.join('.');
- };
-
- function enhanceParagraph(paragraph) {
- Object.assign(paragraph, EXTENDED_PARAGRAPH);
-
- 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,
- cellClass: 'cell-left'
- };
- });
-
- $timeout(() => this.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN));
- },
- updateRows(rows) {
- const sizeChanged = this.data.length !== rows.length;
-
- this.data = rows;
-
- if (sizeChanged) {
- const height = Math.min(rows.length, 15) * 30 + 47;
-
- // Remove header height.
- this.api.grid.element.css('height', height + 'px');
-
- $timeout(() => this.api.core.handleWindowResize());
- }
- },
- onRegisterApi(api) {
- $animate.enabled(api.grid.element, false);
-
- this.api = api;
- }
- }});
-
- Object.defineProperty(paragraph, 'chartHistory', {value: []});
- }
-
$scope.aceInit = function(paragraph) {
return function(editor) {
editor.setAutoScrollEditorIntoView(true);
@@ -732,89 +723,142 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
};
};
- 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;
- });
- }
- };
-
- const _updateTopology = () =>
+ /**
+ * Update caches list.
+ * @private
+ */
+ const _refreshFn = () =>
agentMonitor.topology()
.then((clusters) => {
- agentMonitor.checkModal();
+ $scope.caches = _.sortBy(_.reduce(clusters, (items, cluster) => {
+ _.forEach(cluster.caches, (cache) => {
+ let item = _.find(items, {name: cache.name});
- const caches = _.flattenDeep(clusters.map((cluster) => cluster.caches));
+ if (_.isNil(item)) {
+ cache.label = $scope.maskCacheName(cache.name);
- $scope.caches = _.sortBy(_.map(_.uniqBy(_.reject(caches, {mode: 'LOCAL'}), 'name'), (cache) => {
- cache.label = $scope.maskCacheName(cache.name);
+ cache.nodeIds = [];
- return cache;
- }), 'label');
+ items.push(item = cache);
+ }
+
+ item.nodeIds.push(cluster.nodeId);
+ });
+
+ return items;
+ }, []), 'label');
+
+ if (_.isEmpty($scope.caches))
+ return;
+
+ // Reset to first cache in case of stopped selected.
+ const cacheNames = _.map($scope.caches, (cache) => cache.name);
- _setActiveCache();
+ _.forEach($scope.notebook.paragraphs, (paragraph) => {
+ if (!_.includes(cacheNames, paragraph.cacheName))
+ paragraph.cacheName = _.head(cacheNames);
+ });
})
- .catch((err) => {
- if (err.code === 2)
- return agentMonitor.showNodeError('Agent is failed to authenticate in grid. Please check agent\'s login and password.');
+ .then(() => agentMonitor.checkModal())
+ .catch((err) => agentMonitor.showNodeError(err));
+
+ const _startWatch = () =>
+ agentMonitor.startWatch({
+ state: 'base.configuration.clusters',
+ text: 'Back to Configuration',
+ goal: 'execute sql statements',
+ onDisconnect: () => {
+ _stopTopologyRefresh();
- agentMonitor.showNodeError(err);
+ _startWatch();
+ }
+ })
+ .then(() => Loading.start('sqlLoading'))
+ .then(_refreshFn)
+ .then(() => Loading.finish('sqlLoading'))
+ .then(() => {
+ $root.IgniteDemoMode && _.forEach($scope.notebook.paragraphs, $scope.execute);
+
+ stopTopology = $interval(_refreshFn, 5000, 0, false);
});
- const _startTopologyRefresh = () => {
- Loading.start('sqlLoading');
+ Notebook.find($state.params.noteId)
+ .then((notebook) => {
+ $scope.notebook = _.cloneDeep(notebook);
+
+ $scope.notebook_name = $scope.notebook.name;
+
+ if (!$scope.notebook.expandedParagraphs)
+ $scope.notebook.expandedParagraphs = [];
+
+ if (!$scope.notebook.paragraphs)
+ $scope.notebook.paragraphs = [];
+
+ $scope.notebook.paragraphs = _.map($scope.notebook.paragraphs, (paragraph) => {
+ paragraph.id = 'paragraph-' + paragraphId++;
+
+ const par = new Paragraph($timeout, Notebook, paragraph);
+
+ Object.defineProperty(par, 'gridOptions', {value: {
+ enableGridMenu: false,
+ enableColumnMenus: false,
+ flatEntityAccess: true,
+ fastWatch: true,
+ rebuildColumns() {
+ if (_.isNil(this.api))
+ return;
+
+ this.columnDefs = _.reduce(par.meta, (cols, col, idx) => {
+ if (par.columnFilter(col)) {
+ cols.push({
+ displayName: col.fieldName,
+ headerTooltip: _fullColName(col),
+ field: idx.toString(),
+ minWidth: 50,
+ cellClass: 'cell-left'
+ });
+ }
- agentMonitor.awaitAgent()
- .then(_updateTopology)
- .then(() => {
- if ($root.IgniteDemoMode)
- _.forEach($scope.notebook.paragraphs, $scope.execute);
+ return cols;
+ }, []);
- Loading.finish('sqlLoading');
+ $timeout(() => this.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN));
+ },
+ adjustHeight() {
+ if (_.isNil(this.api))
+ return;
- stopTopology = $interval(_updateTopology, 5000, 0, false);
- });
- };
+ this.data = par.rows;
- const loadNotebook = function(notebook) {
- $scope.notebook = _.cloneDeep(notebook);
+ const height = Math.min(this.data.length, 15) * 30 + 47;
- $scope.notebook_name = $scope.notebook.name;
+ // Remove header height.
+ this.api.grid.element.css('height', height + 'px');
- if (!$scope.notebook.expandedParagraphs)
- $scope.notebook.expandedParagraphs = [];
+ $timeout(() => this.api.core.handleWindowResize());
+ },
+ onRegisterApi(api) {
+ $animate.enabled(api.grid.element, false);
- if (!$scope.notebook.paragraphs)
- $scope.notebook.paragraphs = [];
+ this.api = api;
- _.forEach($scope.notebook.paragraphs, (paragraph) => {
- paragraph.id = 'paragraph-' + paragraphId++;
+ this.rebuildColumns();
- enhanceParagraph(paragraph);
- });
+ this.adjustHeight();
+ }
+ }});
- if (_.isEmpty($scope.notebook.paragraphs))
- $scope.addParagraph();
- else
- $scope.rebuildScrollParagraphs();
+ Object.defineProperty(par, 'chartHistory', {value: []});
- agentMonitor.startWatch({
- state: 'base.configuration.clusters',
- text: 'Back to Configuration',
- goal: 'execute sql statements',
- onDisconnect: () => {
- _stopTopologyRefresh();
+ return par;
+ });
- _startTopologyRefresh();
- }
+ if (_.isEmpty($scope.notebook.paragraphs))
+ $scope.addParagraph();
+ else
+ $scope.rebuildScrollParagraphs();
})
- .then(_startTopologyRefresh);
- };
-
- Notebook.find($state.params.noteId)
- .then(loadNotebook)
+ .then(_startWatch)
.catch(() => {
$scope.notebookLoadFailed = true;
@@ -878,8 +922,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
}
};
- enhanceParagraph(paragraph);
-
if ($scope.caches && $scope.caches.length > 0)
paragraph.cacheName = $scope.caches[0].name;
@@ -893,9 +935,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
$anchorScroll();
- setTimeout(function() {
- paragraph.ace.focus();
- });
+ setTimeout(paragraph.ace.focus);
};
function _saveChartSettings(paragraph) {
@@ -931,8 +971,6 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
if (paragraph.chart())
_chartApplySettings(paragraph, true);
- else
- $timeout(() => paragraph.gridOptions.api.core.handleWindowResize());
};
$scope.resultEq = function(paragraph, result) {
@@ -1033,25 +1071,15 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
});
});
- const cols = [];
-
- _.forEach(paragraph.meta, (col, idx) => {
- if (paragraph.columnFilter(col)) {
- col.field = paragraph.queryArgs.query ? idx.toString() : col.fieldName;
-
- cols.push(col);
- }
- });
-
- paragraph.gridOptions.updateColumns(cols);
+ paragraph.gridOptions.rebuildColumns();
- paragraph.chartColumns = _.reduce(cols, (acc, col) => {
- if (_notObjectType(col.fieldTypeName)) {
+ paragraph.chartColumns = _.reduce(paragraph.meta, (acc, col, idx) => {
+ if (paragraph.columnFilter(col) && _notObjectType(col.fieldTypeName)) {
acc.push({
label: col.fieldName,
type: col.fieldTypeName,
aggFx: $scope.aggregateFxs[0],
- value: col.field
+ value: idx.toString()
});
}
@@ -1087,14 +1115,14 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
/**
* @param {Object} paragraph Query
- * @param {{fieldsMetadata: Array, items: Array, queryId: int, last: Boolean}} res Query results.
+ * @param {{columns: Array, rows: Array, responseNodeId: String, queryId: int, hasMore: Boolean}} res Query results.
* @private
*/
- const _processQueryResult = function(paragraph, res) {
+ const _processQueryResult = (paragraph, res) => {
const prevKeyCols = paragraph.chartKeyCols;
const prevValCols = paragraph.chartValCols;
- if (!_.eq(paragraph.meta, res.fieldsMetadata)) {
+ if (!_.eq(paragraph.meta, res.columns)) {
paragraph.meta = [];
paragraph.chartColumns = [];
@@ -1105,17 +1133,17 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
if (!LegacyUtils.isDefined(paragraph.chartValCols))
paragraph.chartValCols = [];
- if (res.fieldsMetadata.length <= 2) {
- const _key = _.find(res.fieldsMetadata, {fieldName: '_KEY'});
- const _val = _.find(res.fieldsMetadata, {fieldName: '_VAL'});
+ if (res.columns.length <= 2) {
+ const _key = _.find(res.columns, {fieldName: '_KEY'});
+ const _val = _.find(res.columns, {fieldName: '_VAL'});
- paragraph.disabledSystemColumns = (res.fieldsMetadata.length === 2 && _key && _val) ||
- (res.fieldsMetadata.length === 1 && (_key || _val));
+ paragraph.disabledSystemColumns = (res.columns.length === 2 && _key && _val) ||
+ (res.columns.length === 1 && (_key || _val));
}
paragraph.columnFilter = _columnFilter(paragraph);
- paragraph.meta = res.fieldsMetadata;
+ paragraph.meta = res.columns;
_rebuildColumns(paragraph);
}
@@ -1124,24 +1152,28 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
paragraph.total = 0;
- paragraph.queryId = res.last ? null : res.queryId;
+ paragraph.duration = res.duration;
+
+ paragraph.queryId = res.hasMore ? res.queryId : null;
+
+ paragraph.resNodeId = res.responseNodeId;
delete paragraph.errMsg;
// Prepare explain results for display in table.
- if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.items) {
+ if (paragraph.queryArgs.query && paragraph.queryArgs.query.startsWith('EXPLAIN') && res.rows) {
paragraph.rows = [];
- res.items.forEach(function(row, i) {
- const line = res.items.length - 1 === i ? row[0] : row[0] + '\n';
+ res.rows.forEach((row, i) => {
+ const line = res.rows.length - 1 === i ? row[0] : row[0] + '\n';
line.replace(/\"/g, '').split('\n').forEach((ln) => paragraph.rows.push([ln]));
});
}
else
- paragraph.rows = res.items;
+ paragraph.rows = res.rows;
- paragraph.gridOptions.updateRows(paragraph.rows);
+ paragraph.gridOptions.adjustHeight(paragraph.rows.length);
const chartHistory = paragraph.chartHistory;
@@ -1189,12 +1221,18 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
return queryId ? agentMonitor.queryClose(queryId) : $q.when();
};
+ const cacheNode = (name) => {
+ const cache = _.find($scope.caches, {name});
+
+ return cache.nodeIds[_.random(0, cache.nodeIds.length - 1)];
+ };
+
const _executeRefresh = (paragraph) => {
const args = paragraph.queryArgs;
agentMonitor.awaitAgent()
.then(() => _closeOldQuery(paragraph))
- .then(() => agentMonitor.query(args.cacheName, args.pageSize, args.query))
+ .then(() => agentMonitor.query(cacheNode(args.cacheName), args.cacheName, args.query, false, args.pageSize))
.then(_processQueryResult.bind(this, paragraph))
.catch((err) => paragraph.errMsg = err.message);
};
@@ -1213,7 +1251,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
}
};
- $scope.execute = function(paragraph) {
+ $scope.execute = (paragraph) => {
Notebook.save($scope.notebook)
.catch(Messages.showError);
@@ -1222,16 +1260,16 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
_showLoading(paragraph, true);
_closeOldQuery(paragraph)
- .then(function() {
+ .then(() => {
const args = paragraph.queryArgs = {
cacheName: paragraph.cacheName,
pageSize: paragraph.pageSize,
query: paragraph.query
};
- return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+ return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, args.query, false, args.pageSize);
})
- .then(function(res) {
+ .then((res) => {
_processQueryResult(paragraph, res);
_tryStartRefresh(paragraph);
@@ -1250,7 +1288,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
return LegacyUtils.isDefined(paragraph.queryArgs);
};
- const _cancelRefresh = function(paragraph) {
+ const _cancelRefresh = (paragraph) => {
if (paragraph.rate && paragraph.rate.stopTime) {
delete paragraph.queryArgs;
@@ -1262,7 +1300,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
}
};
- $scope.explain = function(paragraph) {
+ $scope.explain = (paragraph) => {
Notebook.save($scope.notebook)
.catch(Messages.showError);
@@ -1271,14 +1309,14 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
_showLoading(paragraph, true);
_closeOldQuery(paragraph)
- .then(function() {
+ .then(() => {
const args = paragraph.queryArgs = {
cacheName: paragraph.cacheName,
pageSize: paragraph.pageSize,
query: 'EXPLAIN ' + paragraph.query
};
- return agentMonitor.query(args.cacheName, args.pageSize, args.query);
+ return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, args.query, false, args.pageSize);
})
.then(_processQueryResult.bind(this, paragraph))
.catch((err) => {
@@ -1289,7 +1327,7 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
.then(() => paragraph.ace.focus());
};
- $scope.scan = function(paragraph) {
+ $scope.scan = (paragraph, query = null) => {
Notebook.save($scope.notebook)
.catch(Messages.showError);
@@ -1301,10 +1339,11 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
.then(() => {
const args = paragraph.queryArgs = {
cacheName: paragraph.cacheName,
- pageSize: paragraph.pageSize
+ pageSize: paragraph.pageSize,
+ query
};
- return agentMonitor.query(args.cacheName, args.pageSize);
+ return agentMonitor.query(cacheNode(paragraph.cacheName), args.cacheName, query, false, args.pageSize);
})
.then(_processQueryResult.bind(this, paragraph))
.catch((err) => {
@@ -1315,6 +1354,15 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
.then(() => paragraph.ace.focus());
};
+ $scope.scanWithFilter = (paragraph) => {
+ ScanFilterInput.open()
+ .then(({filter, caseSensitive}) => {
+ const prefix = caseSensitive ? SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE : SCAN_CACHE_WITH_FILTER;
+
+ $scope.scan(paragraph, `${prefix}${filter}`);
+ });
+ };
+
function _updatePieChartsWithData(paragraph, newDatum) {
$timeout(() => {
_.forEach(paragraph.charts, function(chart) {
@@ -1332,18 +1380,20 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
});
}
- $scope.nextPage = function(paragraph) {
+ $scope.nextPage = (paragraph) => {
_showLoading(paragraph, true);
paragraph.queryArgs.pageSize = paragraph.pageSize;
- agentMonitor.next(paragraph.queryId, paragraph.pageSize)
- .then(function(res) {
+ agentMonitor.next(paragraph.resNodeId, paragraph.queryId, paragraph.pageSize)
+ .then((res) => {
paragraph.page++;
paragraph.total += paragraph.rows.length;
- paragraph.rows = res.items;
+ paragraph.duration = res.duration;
+
+ paragraph.rows = res.rows;
if (paragraph.chart()) {
if (paragraph.result === 'pie')
@@ -1352,11 +1402,11 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
_updateChartsWithData(paragraph, _chartDatum(paragraph));
}
- paragraph.gridOptions.updateRows(paragraph.rows);
+ paragraph.gridOptions.adjustHeight(paragraph.rows.length);
_showLoading(paragraph, false);
- if (res.last)
+ if (!res.hasMore)
delete paragraph.queryId;
})
.catch((err) => {
@@ -1424,8 +1474,8 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
$scope.exportCsvAll = function(paragraph) {
const args = paragraph.queryArgs;
- agentMonitor.queryGetAll(args.cacheName, args.query)
- .then((res) => _export(paragraph.name + '-all.csv', paragraph.columnFilter, res.fieldsMetadata, res.items))
+ agentMonitor.queryGetAll(cacheNode(args.cacheName), args.cacheName, args.query, false)
+ .then((res) => _export(paragraph.name + '-all.csv', paragraph.columnFilter, res.columns, res.rows))
.catch(Messages.showError)
.then(() => paragraph.ace.focus());
};
@@ -1544,6 +1594,18 @@ export default ['$rootScope', '$scope', '$http', '$q', '$timeout', '$interval',
scope.title = 'SCAN query';
scope.content = [`SCAN query for cache: <b>${$scope.maskCacheName(paragraph.queryArgs.cacheName)}</b>`];
}
+ if (paragraph.queryArgs.query.startsWith(SCAN_CACHE_WITH_FILTER)) {
+ scope.title = 'SCAN query';
+
+ let filter = '';
+
+ if (paragraph.queryArgs.query.startsWith(SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE))
+ filter = paragraph.queryArgs.query.substr(SCAN_CACHE_WITH_FILTER_CASE_SENSITIVE.length);
+ else
+ filter = paragraph.queryArgs.query.substr(SCAN_CACHE_WITH_FILTER.length);
+
+ scope.content = [`SCAN query for cache: <b>${$scope.maskCacheName(paragraph.queryArgs.cacheName)}</b> with filter: <b>${filter}</b>`];
+ }
else if (paragraph.queryArgs.query .startsWith('EXPLAIN ')) {
scope.title = 'Explain query';
scope.content = [paragraph.queryArgs.query];
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/app/modules/sql/sql.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/sql/sql.module.js b/modules/web-console/frontend/app/modules/sql/sql.module.js
index 703ecc8..daf5cce 100644
--- a/modules/web-console/frontend/app/modules/sql/sql.module.js
+++ b/modules/web-console/frontend/app/modules/sql/sql.module.js
@@ -19,6 +19,7 @@ import angular from 'angular';
import NotebookData from './Notebook.data';
import Notebook from './Notebook.service';
+import ScanFilterInput from './scan-filter-input.service';
import notebook from './notebook.controller';
import sql from './sql.controller';
@@ -52,5 +53,6 @@ angular.module('ignite-console.sql', [
)
.service('IgniteNotebookData', NotebookData)
.service('IgniteNotebook', Notebook)
+ .service('IgniteScanFilterInput', ScanFilterInput)
.controller('notebookController', notebook)
.controller('sqlController', sql);
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/gulpfile.babel.js/paths.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/gulpfile.babel.js/paths.js b/modules/web-console/frontend/gulpfile.babel.js/paths.js
index 4441cfa..9134e44 100644
--- a/modules/web-console/frontend/gulpfile.babel.js/paths.js
+++ b/modules/web-console/frontend/gulpfile.babel.js/paths.js
@@ -28,7 +28,8 @@ const jadePaths = [
'./views/*.jade',
'./views/**/*.jade',
'./app/helpers/**/*.jade',
- './app/modules/states/configuration/**/*.jade'
+ './app/modules/states/configuration/**/*.jade',
+ './app/modules/sql/*.jade'
];
const resourcePaths = [
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/public/stylesheets/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/stylesheets/style.scss b/modules/web-console/frontend/public/stylesheets/style.scss
index 3704643..994595a 100644
--- a/modules/web-console/frontend/public/stylesheets/style.scss
+++ b/modules/web-console/frontend/public/stylesheets/style.scss
@@ -641,29 +641,6 @@ button.form-control {
}
}
- .sql-controls {
- margin: 10px 0;
- padding: 0 10px;
- }
-
- .sql-table-total {
- padding: 0 10px;
-
- label, b {
- display: inline-block;
-
- padding-top: 5px;
-
- height: 27px;
- }
-
- margin-bottom: 10px;
- }
-
- .sql-table {
- height: 400px;
- }
-
table thead {
background-color: white;
}
@@ -682,38 +659,45 @@ button.form-control {
line-height: 55px;
}
- .sql-error-result {
- padding: 10px 0;
+ .sql-controls {
+ border-top: 1px solid $ignite-border-color;
- text-align: center;
- color: $brand-primary;
+ padding: 10px 10px;
+ }
+ .sql-result {
border-top: 1px solid $ignite-border-color;
- }
- .sql-empty-result {
- margin-top: 10px;
- margin-bottom: 10px;
- text-align: center;
- color: $ignite-placeholder-color;
- }
+ .error {
+ padding: 10px 10px;
- .sql-next {
- float: right;
+ text-align: center;
+ color: $brand-primary;
+ }
- .disabled {
- cursor: default;
- text-decoration: none;
+ .empty {
+ padding: 10px 10px;
+
+ text-align: center;
+ color: $ignite-placeholder-color;
}
- a {
- margin-right: 5px;
- margin-bottom: 5px;
+ .total {
+ padding: 10px 10px;
}
- i {
- margin-top: 3px;
- margin-right: 10px;
+ .table {
+ margin: 0
+ }
+
+ .chart {
+ margin: 0
+ }
+
+ .footer {
+ border-top: 1px solid $ignite-border-color;
+
+ padding: 5px 10px;
}
}
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/0a1fa244/modules/web-console/frontend/views/sql/sql.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/views/sql/sql.jade b/modules/web-console/frontend/views/sql/sql.jade
index 582c8ca..570517d 100644
--- a/modules/web-console/frontend/views/sql/sql.jade
+++ b/modules/web-console/frontend/views/sql/sql.jade
@@ -28,8 +28,8 @@ mixin result-toolbar
+btn-toolbar-data('fa-line-chart', 'line', 'Show line chart<br/>By default first column - X values, second column - Y values<br/>In case of one column it will be treated as Y values')
+btn-toolbar-data('fa-area-chart', 'area', 'Show area chart<br/>By default first column - X values, second column - Y values<br/>In case of one column it will be treated as Y values')
-mixin chart-settings(mdl)
- .row
+mixin chart-settings
+ .total.row
.col-xs-4
.chart-settings-link(ng-show='paragraph.chart && paragraph.chartColumns.length > 0')
a(title='Click to show chart settings dialog' ng-click='$event.stopPropagation()' bs-popover data-template-url='/sql/chart-settings.html' data-placement='bottom' data-auto-close='1' data-trigger='click')
@@ -86,9 +86,13 @@ mixin query-controls
.sql-controls
a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && execute(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute", true)}}') Execute
a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, true)' ng-click='actionAvailable(paragraph, true) && explain(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "explain", true)}}') Explain
- a.btn.btn-primary(ng-disabled='!actionAvailable(paragraph, false)' ng-click='actionAvailable(paragraph, false) && scan(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute scan", false)}}') Scan
+ .btn-group(ng-disabled='!actionAvailable(paragraph, false)')
+ a.btn.btn-primary.fieldButton(ng-click='actionAvailable(paragraph, false) && scan(paragraph)' data-placement='bottom' bs-tooltip data-title='{{actionTooltip(paragraph, "execute scan", false)}}') Scan
+ a.btn.btn-primary(data-toggle='dropdown' data-container='body' bs-dropdown='[{ text: "Scan with filter", click: "actionAvailable(paragraph, false) && scanWithFilter(paragraph)" }]' data-placement='bottom-right')
+ span.caret
+
.pull-right
- labelHide System columns:
+ label.tipLabel System columns:
a.btn.btn-default.fa.fa-bars.tipLabel(ng-class='{"btn-info": paragraph.systemColumns}' ng-click='toggleSystemColumns(paragraph)' ng-disabled='paragraph.disabledSystemColumns' bs-tooltip data-title='Show "_KEY", "_VAL" columns')
label.tipLabel Refresh rate:
button.btn.btn-default.fa.fa-clock-o.tipLabel(title='Click to show refresh rate dialog' ng-class='{"btn-info": paragraph.rate && paragraph.rate.installed}' bs-popover data-template-url='/sql/paragraph-rate.html' data-placement='left' data-auto-close='1' data-trigger='click') {{rateAsString(paragraph)}}
@@ -96,10 +100,11 @@ mixin query-controls
button.select-toggle.fieldButton.btn.btn-default(ng-model='paragraph.pageSize' bs-options='item for item in pageSizes' bs-select bs-tooltip data-placement='bottom-right' data-title='Max number of rows to show in query result as one page')
mixin table-result
- .sql-table-total.row
+ .total.row
.col-xs-4
- label(style='margin-right: 10px;') Page: #[b {{paragraph.page}}]
- label Results so far: #[b {{paragraph.rows.length + paragraph.total}}]
+ label Page: #[b {{paragraph.page}}]
+ label.margin-left-dflt Results so far: #[b {{paragraph.rows.length + paragraph.total}}]
+ label.margin-left-dflt Duration: #[b {{paragraph.duration | duration}}]
.col-xs-4
+result-toolbar
.col-xs-4
@@ -112,31 +117,19 @@ mixin table-result
mixin chart-result
div(ng-show='paragraph.queryExecuted()')
+chart-settings
- div(ng-show='paragraph.chartColumns.length > 0 && !paragraph.chartColumnsConfigured()')
- .sql-empty-result Cannot display chart. Please configure axis using #[b Chart settings]
- div(ng-show='paragraph.chartColumns.length == 0')
- .sql-empty-result Cannot display chart. Result set must contain Java build-in type columns. Please change query and execute it again.
+ .empty(ng-show='paragraph.chartColumns.length > 0 && !paragraph.chartColumnsConfigured()') Cannot display chart. Please configure axis using #[b Chart settings]
+ .empty(ng-show='paragraph.chartColumns.length == 0') Cannot display chart. Result set must contain Java build-in type columns. Please change query and execute it again.
div(ng-show='paragraph.chartColumnsConfigured()')
div(ng-show='paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()')
div(ng-repeat='chart in paragraph.charts')
nvd3(options='chart.options' data='chart.data' api='chart.api')
- .sql-empty-result(ng-show='!paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' column for X-axis. Please use another column for X-axis or switch to another chart.
- .sql-empty-result(ng-hide='paragraph.queryExecuted()')
+ .empty(ng-show='!paragraph.timeLineSupported() && paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' column for X-axis. Please use another column for X-axis or switch to another chart.
+ .empty(ng-hide='paragraph.queryExecuted()')
.row
.col-xs-4.col-xs-offset-4
+result-toolbar
label.margin-top-dflt Charts do not support #[b Explain] and #[b Scan] query
-mixin footer-controls
- hr(style='margin-top: 0; margin-bottom: 5px')
- a(style='float: left; margin-left: 10px; margin-bottom: 5px' ng-click='showResultQuery(paragraph)') Show query
-
- -var nextVisibleCondition = 'paragraph.queryId && (paragraph.table() || paragraph.chart() && (paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()))'
-
- .sql-next(ng-show=nextVisibleCondition)
- i.fa.fa-chevron-circle-right(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)')
- a(ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)') Next
-
.row(ng-controller='sqlController')
.docs-content
.row(ng-if='notebook' bs-affix style='margin-bottom: 20px;')
@@ -182,20 +175,19 @@ mixin footer-controls
.empty-caches(ng-show='caches.length == 0')
label No caches
.col-sm-12
- hr(style='margin: 0')
- .col-sm-12
+query-controls
- .col-sm-12.sql-error-result(ng-show='paragraph.errMsg') Error: {{paragraph.errMsg}}
- .col-sm-12(ng-show='!paragraph.errMsg && paragraph.queryArgs')
- hr(style='margin-top: 0; margin-bottom: 10px')
-
- .sql-empty-result(ng-show='!paragraph.nonEmpty()') Result set is empty
-
- div(ng-show='paragraph.table() && paragraph.nonEmpty()')
+ .col-sm-12.sql-result(ng-switch='paragraph.resultType()')
+ .error(ng-switch-when='error') Error: {{paragraph.errMsg}}
+ .empty(ng-switch-when='empty') Result set is empty
+ .table(ng-switch-when='table')
+table-result
-
- div(ng-show='paragraph.chart() && paragraph.nonEmpty()')
+ .chart(ng-switch-when='chart')
+chart-result
+ .footer.clearfix(ng-show='paragraph.nonRefresh()')
+ a.pull-left(ng-click='showResultQuery(paragraph)') Show query
+
+ -var nextVisibleCondition = 'paragraph.queryId && (paragraph.table() || paragraph.chart() && (paragraph.timeLineSupported() || !paragraph.chartTimeLineEnabled()))'
- div(ng-show='!paragraph.refreshExecuting()')
- +footer-controls
+ .pull-right(ng-show=nextVisibleCondition ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && nextPage(paragraph)')
+ i.fa.fa-chevron-circle-right
+ a Next