You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/04/12 15:51:43 UTC
[incubator-druid] branch master updated: Wrap query with limit
within the web console (#7449)
This is an automated email from the ASF dual-hosted git repository.
fjy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git
The following commit(s) were added to refs/heads/master by this push:
new 60dd75d Wrap query with limit within the web console (#7449)
60dd75d is described below
commit 60dd75d3d957a99c7eb28f59336f596310cf48e7
Author: Vadim Ogievetsky <va...@gmail.com>
AuthorDate: Fri Apr 12 08:51:37 2019 -0700
Wrap query with limit within the web console (#7449)
* wrap with limit
* make actual menu checkbox component
---
.../{sql-control.scss => menu-checkbox.scss} | 62 +-----------
.../{sql-control.scss => menu-checkbox.tsx} | 63 ++----------
web-console/src/components/sql-control.scss | 19 +---
web-console/src/components/sql-control.tsx | 109 +++++++++++++++------
.../src/components/table-column-selection.tsx | 20 ++--
web-console/src/views/sql-view.tsx | 52 +++++++---
6 files changed, 145 insertions(+), 180 deletions(-)
diff --git a/web-console/src/components/sql-control.scss b/web-console/src/components/menu-checkbox.scss
similarity index 52%
copy from web-console/src/components/sql-control.scss
copy to web-console/src/components/menu-checkbox.scss
index d5e894d..8d4963a 100644
--- a/web-console/src/components/sql-control.scss
+++ b/web-console/src/components/menu-checkbox.scss
@@ -16,63 +16,11 @@
* limitations under the License.
*/
+.menu-checkbox {
+ height: 30px;
+ padding: 7px 4px;
-.sql-control {
-
- .ace_scroller {
- background-color: #232C35;
- }
-
- .ace_gutter-layer {
- background-color: #27313c;
- }
-
- .buttons {
- position: relative;
-
- button{
- margin-right: 15px;
- }
-
- .query-elapsed {
- position: absolute;
- right: 10px;
- }
- }
-
-}
-
-.sql-control-popover {
- padding:10px;
- min-width: 120px;
-
- .bp3-form-group {
- margin-bottom: 0;
- }
-
- button {
- span {
- position: relative;
- left: -7px;
- }
- padding-right: 35px;
- }
-}
-
-.ace_tooltip {
- padding: 10px;
- background-color: #333D47;
- color: #C1CCD5;
- width: 500px;
- display: block;
- height: auto;
- white-space: initial;
-
- hr {
- margin: 10px 0;
- }
-
- .function-doc-name {
- font-size: 18px;
+ label {
+ margin: 0;
}
}
diff --git a/web-console/src/components/sql-control.scss b/web-console/src/components/menu-checkbox.tsx
similarity index 52%
copy from web-console/src/components/sql-control.scss
copy to web-console/src/components/menu-checkbox.tsx
index d5e894d..21b438b 100644
--- a/web-console/src/components/sql-control.scss
+++ b/web-console/src/components/menu-checkbox.tsx
@@ -16,63 +16,16 @@
* limitations under the License.
*/
+import { Checkbox, ICheckboxProps } from '@blueprintjs/core';
+import * as React from 'react';
-.sql-control {
+import './menu-checkbox.scss';
- .ace_scroller {
- background-color: #232C35;
- }
-
- .ace_gutter-layer {
- background-color: #27313c;
- }
-
- .buttons {
- position: relative;
-
- button{
- margin-right: 15px;
- }
-
- .query-elapsed {
- position: absolute;
- right: 10px;
- }
- }
-
-}
+export class MenuCheckbox extends React.Component<ICheckboxProps, {}> {
-.sql-control-popover {
- padding:10px;
- min-width: 120px;
-
- .bp3-form-group {
- margin-bottom: 0;
- }
-
- button {
- span {
- position: relative;
- left: -7px;
- }
- padding-right: 35px;
- }
-}
-
-.ace_tooltip {
- padding: 10px;
- background-color: #333D47;
- color: #C1CCD5;
- width: 500px;
- display: block;
- height: auto;
- white-space: initial;
-
- hr {
- margin: 10px 0;
- }
-
- .function-doc-name {
- font-size: 18px;
+ render() {
+ return <li className="menu-checkbox">
+ <Checkbox {...this.props}/>
+ </li>;
}
}
diff --git a/web-console/src/components/sql-control.scss b/web-console/src/components/sql-control.scss
index d5e894d..2fe4036 100644
--- a/web-console/src/components/sql-control.scss
+++ b/web-console/src/components/sql-control.scss
@@ -42,23 +42,6 @@
}
-.sql-control-popover {
- padding:10px;
- min-width: 120px;
-
- .bp3-form-group {
- margin-bottom: 0;
- }
-
- button {
- span {
- position: relative;
- left: -7px;
- }
- padding-right: 35px;
- }
-}
-
.ace_tooltip {
padding: 10px;
background-color: #333D47;
@@ -71,7 +54,7 @@
hr {
margin: 10px 0;
}
-
+
.function-doc-name {
font-size: 18px;
}
diff --git a/web-console/src/components/sql-control.tsx b/web-console/src/components/sql-control.tsx
index 6b4de1d..1aa9089 100644
--- a/web-console/src/components/sql-control.tsx
+++ b/web-console/src/components/sql-control.tsx
@@ -16,7 +16,15 @@
* limitations under the License.
*/
-import { Button, Checkbox, Classes, FormGroup, Intent, Menu, Popover, Position } from '@blueprintjs/core';
+import {
+ Button,
+ ButtonGroup,
+ Intent,
+ Menu,
+ MenuItem,
+ Popover,
+ Position
+} from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
import * as ace from 'brace';
@@ -25,6 +33,7 @@ import 'brace/mode/hjson';
import 'brace/mode/sql';
import 'brace/theme/solarized_dark';
import * as classNames from 'classnames';
+import * as Hjson from 'hjson';
import * as React from 'react';
import AceEditor from 'react-ace';
import * as ReactDOMServer from 'react-dom/server';
@@ -32,21 +41,34 @@ import * as ReactDOMServer from 'react-dom/server';
import { SQLFunctionDoc } from '../../lib/sql-function-doc';
import { AppToaster } from '../singletons/toaster';
+import { MenuCheckbox } from './menu-checkbox';
+
import './sql-control.scss';
+function validHjson(query: string) {
+ try {
+ Hjson.parse(query);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
const langTools = ace.acequire('ace/ext/language_tools');
export interface SqlControlProps extends React.Props<any> {
initSql: string | null;
- onRun: (query: string) => void;
- onExplain: (query: string) => void;
+ onRun: (query: string, bypassCache: boolean, wrapQuery: boolean) => void;
+ onExplain: (sqlQuery: string) => void;
queryElapsed: number | null;
}
export interface SqlControlState {
query: string;
- autoCompleteOn: boolean;
+ autoComplete: boolean;
autoCompleteLoading: boolean;
+ bypassCache: boolean;
+ wrapQuery: boolean;
}
export class SqlControl extends React.Component<SqlControlProps, SqlControlState> {
@@ -54,8 +76,10 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
super(props, context);
this.state = {
query: props.initSql || '',
- autoCompleteOn: true,
- autoCompleteLoading: false
+ autoComplete: true,
+ autoCompleteLoading: false,
+ bypassCache: false,
+ wrapQuery: true
};
}
@@ -166,29 +190,49 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
});
}
- render() {
- const { onRun, onExplain, queryElapsed } = this.props;
- const { query, autoCompleteOn } = this.state;
+ private onRunClick = () => {
+ const { onRun } = this.props;
+ const { query, bypassCache, wrapQuery } = this.state;
+ onRun(query, bypassCache, wrapQuery);
+ }
- const isRune = query.trim().startsWith('{');
+ renderExtraMenu(isRune: boolean) {
+ const { onExplain } = this.props;
+ const { query, autoComplete, bypassCache, wrapQuery } = this.state;
- const SqlControlPopover = <Popover position={Position.BOTTOM_LEFT}>
- <Button minimal icon={IconNames.MORE}/>
- <div className="sql-control-popover">
- <Checkbox
- checked={isRune ? false : autoCompleteOn}
- label="Auto complete"
- onChange={() => this.setState({autoCompleteOn: !autoCompleteOn})}
- />
- <Button
+ return <Menu>
+ {
+ !isRune &&
+ <>
+ <MenuItem
icon={IconNames.CLEAN}
- className={Classes.POPOVER_DISMISS}
text="Explain"
onClick={() => onExplain(query)}
- minimal
/>
- </div>
- </Popover>;
+ <MenuCheckbox
+ checked={autoComplete}
+ label="Auto complete"
+ onChange={() => this.setState({autoComplete: !autoComplete})}
+ />
+ <MenuCheckbox
+ checked={wrapQuery}
+ label="Wrap query with limit"
+ onChange={() => this.setState({wrapQuery: !wrapQuery})}
+ />
+ </>
+ }
+ <MenuCheckbox
+ checked={bypassCache}
+ label="Bypass cache"
+ onChange={() => this.setState({bypassCache: !bypassCache})}
+ />
+ </Menu>;
+ }
+
+ render() {
+ const { queryElapsed } = this.props;
+ const { query, autoComplete, wrapQuery } = this.state;
+ const isRune = query.trim().startsWith('{');
// Set the key in the AceEditor to force a rebind and prevent an error that happens otherwise
return <div className="sql-control">
@@ -208,17 +252,24 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
$blockScrolling: Infinity
}}
setOptions={{
- enableBasicAutocompletion: isRune ? false : autoCompleteOn,
- enableLiveAutocompletion: isRune ? false : autoCompleteOn,
+ enableBasicAutocompletion: isRune ? false : autoComplete,
+ enableLiveAutocompletion: isRune ? false : autoComplete,
showLineNumbers: true,
tabSize: 2
}}
/>
<div className="buttons">
- <Button rightIcon={IconNames.CARET_RIGHT} onClick={() => onRun(query)}>
- {isRune ? 'Rune' : 'Run'}
- </Button>
- {!isRune && SqlControlPopover}
+ <ButtonGroup>
+ <Button
+ icon={IconNames.CARET_RIGHT}
+ onClick={this.onRunClick}
+ text={isRune ? 'Rune' : (wrapQuery ? 'Run with limit' : 'Run as is')}
+ disabled={isRune && !validHjson(query)}
+ />
+ <Popover position={Position.BOTTOM_LEFT} content={this.renderExtraMenu(isRune)}>
+ <Button icon={IconNames.MORE}/>
+ </Popover>
+ </ButtonGroup>
{
queryElapsed &&
<span className={'query-elapsed'}> Last query took {(queryElapsed / 1000).toFixed(2)} seconds</span>
diff --git a/web-console/src/components/table-column-selection.tsx b/web-console/src/components/table-column-selection.tsx
index 641f3bb..3635d51 100644
--- a/web-console/src/components/table-column-selection.tsx
+++ b/web-console/src/components/table-column-selection.tsx
@@ -20,6 +20,8 @@ import { Button, Checkbox, FormGroup, Menu, Popover, Position } from '@blueprint
import { IconNames } from '@blueprintjs/icons';
import * as React from 'react';
+import { MenuCheckbox } from './menu-checkbox';
+
import './table-column-selection.scss';
interface TableColumnSelectionProps extends React.Props<any> {
@@ -44,16 +46,14 @@ export class TableColumnSelection extends React.Component<TableColumnSelectionPr
render() {
const { columns, onChange, tableColumnsHidden } = this.props;
const checkboxes = <Menu className="table-column-selection-menu">
- <FormGroup>
- {columns.map(column => (
- <Checkbox
- label={column}
- key={column}
- checked={!tableColumnsHidden.includes(column)}
- onChange={() => onChange(column)}
- />
- ))}
- </FormGroup>
+ {columns.map(column => (
+ <MenuCheckbox
+ label={column}
+ key={column}
+ checked={!tableColumnsHidden.includes(column)}
+ onChange={() => onChange(column)}
+ />
+ ))}
</Menu>;
return <Popover
diff --git a/web-console/src/views/sql-view.tsx b/web-console/src/views/sql-view.tsx
index 53712d9..1713066 100644
--- a/web-console/src/views/sql-view.tsx
+++ b/web-console/src/views/sql-view.tsx
@@ -35,6 +35,12 @@ import {
import './sql-view.scss';
+interface QueryWithFlags {
+ queryString: string;
+ bypassCache?: boolean;
+ wrapQuery?: boolean;
+}
+
export interface SqlViewProps extends React.Props<any> {
initSql: string | null;
}
@@ -57,7 +63,7 @@ interface SqlQueryResult {
export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
- private sqlQueryManager: QueryManager<string, SqlQueryResult>;
+ private sqlQueryManager: QueryManager<QueryWithFlags, SqlQueryResult>;
private explainQueryManager: QueryManager<string, any>;
constructor(props: SqlViewProps, context: any) {
@@ -76,22 +82,46 @@ export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
componentDidMount(): void {
this.sqlQueryManager = new QueryManager({
- processQuery: async (query: string) => {
+ processQuery: async (queryWithFlags: QueryWithFlags) => {
+ const { queryString, bypassCache, wrapQuery } = queryWithFlags;
const startTime = new Date();
- if (query.trim().startsWith('{')) {
+
+ if (queryString.trim().startsWith('{')) {
// Secret way to issue a native JSON "rune" query
- const runeQuery = Hjson.parse(query);
+ const runeQuery = Hjson.parse(queryString);
+
+ if (bypassCache) {
+ runeQuery.context = runeQuery.context || {};
+ runeQuery.context.useCache = false;
+ runeQuery.context.populateCache = false;
+ }
+
const result = await queryDruidRune(runeQuery);
return {
queryResult: decodeRune(runeQuery, result),
queryElapsed: new Date().valueOf() - startTime.valueOf()
};
+
} else {
- const result = await queryDruidSql({
- query,
+ const actualQuery = wrapQuery ?
+ `SELECT * FROM (${queryString.trim().replace(/;+$/, '')}) LIMIT 5000` :
+ queryString;
+
+ const queryPayload: Record<string, any> = {
+ query: actualQuery,
resultFormat: 'array',
header: true
- });
+ };
+
+ if (wrapQuery) {
+ queryPayload.context = {
+ useCache: false,
+ populateCache: false
+ };
+ }
+
+ const result = await queryDruidSql(queryPayload);
+
return {
queryResult: {
header: (result && result.length) ? result[0] : [],
@@ -178,11 +208,11 @@ export class SqlView extends React.Component<SqlViewProps, SqlViewState> {
return <div className="sql-view app-view">
<SqlControl
initSql={initSql || localStorageGet(LocalStorageKeys.QUERY_KEY)}
- onRun={q => {
- localStorageSet(LocalStorageKeys.QUERY_KEY, q);
- this.sqlQueryManager.runQuery(q);
+ onRun={(queryString, bypassCache, wrapQuery) => {
+ localStorageSet(LocalStorageKeys.QUERY_KEY, queryString);
+ this.sqlQueryManager.runQuery({ queryString, bypassCache, wrapQuery });
}}
- onExplain={(q: string) => this.getExplain(q)}
+ onExplain={this.getExplain}
queryElapsed={queryElapsed}
/>
{this.renderResultTable()}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org