You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by cw...@apache.org on 2019/03/16 08:46:00 UTC
[incubator-druid] branch master updated: Add SQL auto complete in
druid console (#7244)
This is an automated email from the ASF dual-hosted git repository.
cwylie 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 5406aaa Add SQL auto complete in druid console (#7244)
5406aaa is described below
commit 5406aaa49d5a7a7071bb013e5be1d265405cf940
Author: Qi Shu <sh...@gmail.com>
AuthorDate: Sat Mar 16 01:45:53 2019 -0700
Add SQL auto complete in druid console (#7244)
* Add SQL auto complete in druid console
* Add comment in sql.md to alert user to change create-sql-function-doc if sql.md format gets changed
---
docs/content/querying/sql.md | 7 ++
web-console/.gitignore | 2 +
web-console/script/build | 3 +
web-console/script/create-sql-function-doc | 50 +++++++++++
web-console/src/components/sql-control.scss | 61 +++++++++++++
web-console/src/components/sql-control.tsx | 133 +++++++++++++++++++++++++++-
web-console/tsconfig.json | 5 +-
7 files changed, 255 insertions(+), 6 deletions(-)
diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md
index 66ecc43..78b3768 100644
--- a/docs/content/querying/sql.md
+++ b/docs/content/querying/sql.md
@@ -22,6 +22,13 @@ title: "SQL"
~ under the License.
-->
+ <!--
+ The format of the tables that describe the functions and operators
+ should not be changed without updating the script create-sql-function-doc
+ in web-console/script/create-sql-function-doc, because the script detects
+ patterns in this markdown file and parse it to TypeScript file for web console
+ -->
+
# SQL
<div class="note caution">
diff --git a/web-console/.gitignore b/web-console/.gitignore
index defba19..be63fe3 100644
--- a/web-console/.gitignore
+++ b/web-console/.gitignore
@@ -9,6 +9,8 @@ coordinator-console/
pages/
index.html
+lib/sql-function-doc.ts
+
.tscache
tscommand-*.tmp.txt
diff --git a/web-console/script/build b/web-console/script/build
index 7537122..cbc9092 100755
--- a/web-console/script/build
+++ b/web-console/script/build
@@ -27,6 +27,9 @@ echo "Copying blueprint assets in..."
sed 's|url("assets|url("/assets|g' "./node_modules/@blueprintjs/core/dist/blueprint.css" > lib/blueprint.css
cp -r "./node_modules/@blueprintjs/core/dist/assets" .
+echo "Adding SQL function doc..."
+PATH="./target/node:$PATH" ./script/create-sql-function-doc
+
echo "Transpiling ReactTable CSS..."
PATH="./target/node:$PATH" ./node_modules/.bin/stylus lib/react-table.styl -o lib/react-table.css
diff --git a/web-console/script/create-sql-function-doc b/web-console/script/create-sql-function-doc
new file mode 100755
index 0000000..146a39e
--- /dev/null
+++ b/web-console/script/create-sql-function-doc
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# 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.
+
+readfile='../docs/content/querying/sql.md'
+writefile='lib/sql-function-doc.ts'
+
+> "$writefile"
+
+echo -e "// This file is auto generated and should not be modified\n" > "$writefile"
+echo -e 'export const SQLFunctionDoc: any[] = [' >> "$writefile"
+
+isFunction=false
+
+while read -r line; do
+ if [[ $line =~ ^###.*functions$ ]]; then
+ isFunction=true
+ elif [[ $line =~ ^## ]] ; then
+ isFunction=false
+ elif [[ $isFunction == true ]]; then
+ if [[ $line =~ \|\`.*\`\|.*\| ]]; then
+ syntax=$(echo $line | grep -o '|`.*`|')
+ syntax=${syntax:2:${#syntax}-4}
+ syntax=${syntax//\\/}
+ description=$(echo $line | grep -o '`|.*.|')
+ description=${description//\"/\'}
+ description=${description:2:${#description}-4}
+ echo -e " {" >> "$writefile"
+ echo -e " syntax: \"$syntax\"," >> "$writefile"
+ echo -e " description: \"$description\"" >> "$writefile"
+ echo -e " }," >> "$writefile"
+ fi
+ fi
+done < "$readfile"
+
+echo -e ']' >> "$writefile"
\ No newline at end of file
diff --git a/web-console/src/components/sql-control.scss b/web-console/src/components/sql-control.scss
new file mode 100644
index 0000000..9e55c5a
--- /dev/null
+++ b/web-console/src/components/sql-control.scss
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+
+.sql-control {
+
+ .ace_scroller {
+ background-color: #232C35;
+ }
+
+ .ace_gutter-layer {
+ background-color: #27313c;
+ }
+
+ .buttons {
+
+ button{
+ margin-right: 15px;
+ }
+
+ }
+
+}
+
+.auto-complete-checkbox {
+ margin:10px;
+ min-width: 120px;
+}
+
+.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;
+ }
+}
\ No newline at end of file
diff --git a/web-console/src/components/sql-control.tsx b/web-console/src/components/sql-control.tsx
index 96dd780..a2d21b5 100644
--- a/web-console/src/components/sql-control.tsx
+++ b/web-console/src/components/sql-control.tsx
@@ -17,6 +17,8 @@
*/
import * as React from 'react';
+import * as ReactDOMServer from 'react-dom/server';
+import axios from "axios";
import * as classNames from 'classnames';
import * as ace from 'brace'
import AceEditor from "react-ace";
@@ -24,8 +26,13 @@ import 'brace/mode/sql';
import 'brace/mode/hjson';
import 'brace/theme/solarized_dark';
import 'brace/ext/language_tools';
-import { Intent, Button } from "@blueprintjs/core";
+import {Intent, Button, Popover, Checkbox, Classes, Position} from "@blueprintjs/core";
+import { SQLFunctionDoc } from "../../lib/sql-function-doc";
import { IconNames } from './filler';
+import './sql-control.scss'
+import {AppToaster} from "../singletons/toaster";
+
+const langTools = ace.acequire('ace/ext/language_tools');
export interface SqlControlProps extends React.Props<any> {
initSql: string | null;
@@ -35,6 +42,7 @@ export interface SqlControlProps extends React.Props<any> {
export interface SqlControlState {
query: string;
autoCompleteOn: boolean;
+ autoCompleteLoading: boolean;
}
export class SqlControl extends React.Component<SqlControlProps, SqlControlState> {
@@ -42,8 +50,111 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
super(props, context);
this.state = {
query: props.initSql || '',
- autoCompleteOn: true
+ autoCompleteOn: true,
+ autoCompleteLoading: false
+ };
+ }
+
+
+ private addDatasourceAutoCompleter = async (): Promise<any> =>{
+ const datasourceResp = await axios.post("/druid/v2/sql", { query: `SELECT datasource FROM sys.segments GROUP BY 1`})
+ const datasourceList: any[] = datasourceResp.data.map((d: any) => {
+ const datasourceName: string = d.datasource;
+ return {
+ value: datasourceName,
+ score: 50,
+ meta: "datasource"
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, datasourceList);
+ }
+ };
+
+ langTools.addCompleter(completer);
+ }
+
+ private addColumnNameAutoCompleter = async (): Promise<any> => {
+ const columnNameResp = await axios.post("/druid/v2/sql", {query: `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'druid'`})
+ const columnNameList: any[] = columnNameResp.data.map((d: any) => {
+ const columnName: string = d.COLUMN_NAME;
+ return {
+ value: columnName,
+ score: 50,
+ meta: "column"
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, columnNameList);
+ }
};
+
+ langTools.addCompleter(completer);
+ }
+
+ private addFunctionAutoCompleter = (): void => {
+ const functionList: any[]= SQLFunctionDoc.map((entry: any) => {
+ let funcName: string = entry.syntax.replace(/\(.*\)/,"()");
+ if (!funcName.includes("(")) funcName = funcName.substr(0,10);
+ return {
+ value: funcName,
+ score: 80,
+ meta: "function",
+ syntax: entry.syntax,
+ description: entry.description,
+ completer: {
+ insertMatch: (editor:any, data:any) => {
+ editor.completer.insertMatch({value: data.caption});
+ const pos = editor.getCursorPosition();
+ editor.gotoLine(pos.row+1, pos.column-1);
+ }
+ }
+ };
+ });
+
+ const completer = {
+ getCompletions: (editor:any , session: any, pos: any, prefix: any, callback: any) => {
+ callback(null, functionList);
+ },
+ getDocTooltip: (item: any) => {
+ if (item.meta === "function") {
+ const functionName = item.caption.slice(0,-2);
+ item.docHTML = ReactDOMServer.renderToStaticMarkup((
+ <div className={"function-doc"}>
+ <div className={"function-doc-name"}><b>{functionName}</b></div>
+ <hr/>
+ <div><b>Syntax:</b></div>
+ <div>{item.syntax}</div>
+ <br/>
+ <div><b>Description:</b></div>
+ <div>{item.description}</div>
+ </div>
+ ))
+ }
+ }
+ };
+ langTools.addCompleter(completer);
+ }
+
+ private addCompleters = async () => {
+ try {
+ this.addFunctionAutoCompleter();
+ await this.addDatasourceAutoCompleter();
+ await this.addColumnNameAutoCompleter();
+ } catch (e) {
+ AppToaster.show({
+ message: "Failed to load SQL auto completer",
+ intent: Intent.DANGER
+ });
+ }
+ }
+
+ componentDidMount(): void {
+ this.addCompleters();
}
private handleChange = (newValue: string): void => {
@@ -58,6 +169,15 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
const isRune = query.trim().startsWith('{');
+ const autoCompletePopover = <div className={"auto-complete-checkbox"}>
+ <Checkbox
+ checked={isRune ? false : autoCompleteOn}
+ disabled={isRune}
+ label={"Auto complete"}
+ onChange={() => this.setState({autoCompleteOn: !autoCompleteOn})}
+ />
+ </div>;
+
// Set the key in the AceEditor to force a rebind and prevent an error that happens otherwise
return <div className="sql-control">
<AceEditor
@@ -83,9 +203,14 @@ export class SqlControl extends React.Component<SqlControlProps, SqlControlState
}}
/>
<div className="buttons">
- <Button rightIconName={IconNames.CARET_RIGHT} onClick={() => onRun(query)}>{isRune ? 'Rune' : 'Run'}</Button>
+ <Button rightIconName={IconNames.CARET_RIGHT} onClick={() => onRun(query)}>
+ {isRune ? 'Rune' : 'Run'}
+ </Button>
+ <Popover position={Position.BOTTOM_LEFT} content={autoCompletePopover} inline>
+ <Button className={`${Classes.MINIMAL} pt-icon-more`}/>
+ </Popover>
</div>
- </div>
+ </div>;
}
}
diff --git a/web-console/tsconfig.json b/web-console/tsconfig.json
index 56fdba0..a445cfa 100644
--- a/web-console/tsconfig.json
+++ b/web-console/tsconfig.json
@@ -15,14 +15,15 @@
"moduleResolution": "node",
"lib": ["dom", "es2016"],
"jsx": "react",
- "rootDir": "src",
+ "rootDirs": ["lib","src"],
"outDir": "build"
},
"include": [
"src/**/*.ts",
- "src/**/*.tsx"
+ "src/**/*.tsx",
+ "lib/sql-function-doc.ts"
],
"exclude": [
"**/*.test.ts"
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org