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/06/21 04:15:00 UTC
[incubator-druid] branch master updated: more acurate keyword auto
complete (#7934)
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 2bc8e7c more acurate keyword auto complete (#7934)
2bc8e7c is described below
commit 2bc8e7c0e897418a53a0b26a477442b95d87c041
Author: Vadim Ogievetsky <va...@gmail.com>
AuthorDate: Thu Jun 20 21:14:54 2019 -0700
more acurate keyword auto complete (#7934)
---
web-console/script/create-sql-function-doc.js | 94 ++++++++++------------
.../views/query-view/column-tree/column-tree.tsx | 6 +-
.../src/views/query-view/query-input/keywords.ts | 81 +++++++++++++++++++
.../views/query-view/query-input/query-input.tsx | 37 ++++-----
.../src/views/query-view/run-button/run-button.tsx | 2 +-
5 files changed, 144 insertions(+), 76 deletions(-)
diff --git a/web-console/script/create-sql-function-doc.js b/web-console/script/create-sql-function-doc.js
index dc55989..d4553a6 100755
--- a/web-console/script/create-sql-function-doc.js
+++ b/web-console/script/create-sql-function-doc.js
@@ -23,7 +23,41 @@ const fs = require('fs-extra');
const readfile = '../docs/content/querying/sql.md';
const writefile = 'lib/sql-function-doc.ts';
-const heading = `/*
+const readDoc = async () => {
+ const data = await fs.readFile(readfile, 'utf-8');
+ const lines = data.split('\n');
+
+ const functionDocs = [];
+ const dataTypeDocs = [];
+ for (let line of lines) {
+ const functionMatch = line.match(/^\|`(.+\(.*\))`\|(.+)\|$/);
+ if (functionMatch) {
+ functionDocs.push({
+ syntax: functionMatch[1],
+ description: functionMatch[2]
+ })
+ }
+
+ const dataTypeMatch = line.match(/^\|([A-Z]+)\|([A-Z]+)\|(.*)\|(.*)\|$/);
+ if (dataTypeMatch) {
+ dataTypeDocs.push({
+ syntax: dataTypeMatch[1],
+ description: dataTypeMatch[4] || `Druid runtime type: ${dataTypeMatch[2]}`
+ })
+ }
+ }
+
+ // Make sure there are at least 10 functions for sanity
+ if (functionDocs.length < 10) {
+ throw new Error(`Did not find enough function entries did the structure of '${readfile}' change? (found ${functionDocs.length})`);
+ }
+
+ // Make sure there are at least 5 data types for sanity
+ if (dataTypeDocs.length < 10) {
+ throw new Error(`Did not find enough data type entries did the structure of '${readfile}' change? (found ${dataTypeDocs.length})`);
+ }
+
+ const content = `/*
* 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
@@ -43,65 +77,19 @@ const heading = `/*
// This file is auto generated and should not be modified
-export interface FunctionDescription {
+export interface SyntaxDescription {
syntax: string;
description: string;
}
/* tslint:disable:quotemark */
-export const SQLFunctionDoc: FunctionDescription[] = `;
-
-const readDoc = async () => {
- try {
- const data = await fs.readFile(readfile, 'utf-8');
- const sections = data.split("##");
-
- let entries = [];
- sections.forEach((section) => {
- if (!/^#.*function/.test(section)) return;
-
- entries = entries.concat(
- section.split('\n').map(line => {
- if (line.startsWith('|`')) {
- const rawSyntax = line.match(/\|`(.*)`\|/);
- if (rawSyntax == null) return null;
- const syntax = rawSyntax[1]
- .replace(/\\/g,'')
- .replace(/|/g,'|');
-
- // Must have an uppercase letter
- if (!/[A-Z]/.test(syntax)) return null;
-
- const rawDescription = line.match(/`\|(.*)\|/);
- if (rawDescription == null) return null;
- const description = rawDescription[1];
-
- return {
- syntax: syntax,
- description: description
- };
- }
- }).filter(Boolean)
- );
- });
-
- // Make sure there are at least 10 functions for sanity
- if (entries.length < 10) {
- throw new Error(`Did not find any entries did the structure of '${readfile}' change?`);
- }
-
- const content = heading + JSON.stringify(entries, null, 2) + ';\n';
+export const SQL_FUNCTIONS: SyntaxDescription[] = ${JSON.stringify(functionDocs, null, 2)};
- try {
- await fs.writeFile(writefile, content, 'utf-8');
- } catch (e) {
- console.log(`Error when writing to ${writefile}: `, e);
- }
+export const SQL_DATE_TYPES: SyntaxDescription[] = ${JSON.stringify(dataTypeDocs, null, 2)};
+`;
- } catch (e) {
- console.log(`Error when reading ${readfile}: `, e);
- }
-}
+ await fs.writeFile(writefile, content, 'utf-8');
+};
readDoc();
diff --git a/web-console/src/views/query-view/column-tree/column-tree.tsx b/web-console/src/views/query-view/column-tree/column-tree.tsx
index b3b2213..5e92b0f 100644
--- a/web-console/src/views/query-view/column-tree/column-tree.tsx
+++ b/web-console/src/views/query-view/column-tree/column-tree.tsx
@@ -150,10 +150,12 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
case 1: // Datasource
const tableSchema = columnTree[selectedTreeIndex].label;
if (tableSchema === 'druid') {
- onQueryStringChange(`SELECT * FROM "${nodeData.label}"
+ onQueryStringChange(`SELECT *
+FROM "${nodeData.label}"
WHERE "__time" >= CURRENT_TIMESTAMP - INTERVAL '1' DAY`);
} else {
- onQueryStringChange(`SELECT * FROM ${tableSchema}.${nodeData.label}`);
+ onQueryStringChange(`SELECT *
+FROM ${tableSchema}.${nodeData.label}`);
}
break;
diff --git a/web-console/src/views/query-view/query-input/keywords.ts b/web-console/src/views/query-view/query-input/keywords.ts
new file mode 100644
index 0000000..34d80fb
--- /dev/null
+++ b/web-console/src/views/query-view/query-input/keywords.ts
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+// Hand picked from https://druid.apache.org/docs/latest/querying/sql.html
+
+export const SQL_KEYWORDS: string[] = [
+ 'EXPLAIN PLAN FOR',
+ 'WITH',
+ 'AS',
+ 'SELECT',
+ 'ALL',
+ 'DISTINCT',
+ 'FROM',
+ 'WHERE',
+ 'GROUP BY',
+ 'HAVING',
+ 'ORDER BY',
+ 'ASC',
+ 'DESC',
+ 'LIMIT',
+ 'UNION ALL'
+];
+
+export const SQL_EXPRESSION_PARTS: string[] = [
+ 'FILTER',
+ 'END',
+ 'ELSE',
+ 'WHEN',
+ 'CASE',
+ 'OR',
+ 'AND',
+ 'NOT',
+ 'IN',
+ 'IS',
+ 'TO',
+ 'BETWEEN',
+ 'LIKE',
+ 'ESCAPE',
+ 'BOTH',
+ 'LEADING',
+ 'TRAILING',
+ 'EPOCH',
+ 'SECOND',
+ 'MINUTE',
+ 'HOUR',
+ 'DAY',
+ 'DOW',
+ 'DOY',
+ 'WEEK',
+ 'MONTH',
+ 'QUARTER',
+ 'YEAR',
+ 'TIMESTAMP',
+ 'INTERVAL'
+];
+
+export const SQL_CONSTANTS: string[] = [
+ 'NULL',
+ 'FALSE',
+ 'TRUE'
+];
+
+export const SQL_DYNAMICS: string[] = [
+ 'CURRENT_TIMESTAMP',
+ 'CURRENT_DATE'
+];
diff --git a/web-console/src/views/query-view/query-input/query-input.tsx b/web-console/src/views/query-view/query-input/query-input.tsx
index ac7ad65..e52684f 100644
--- a/web-console/src/views/query-view/query-input/query-input.tsx
+++ b/web-console/src/views/query-view/query-input/query-input.tsx
@@ -16,17 +16,19 @@
* limitations under the License.
*/
-import { IResizeEntry, ITreeNode, ResizeSensor } from '@blueprintjs/core';
+import { IResizeEntry, ResizeSensor } from '@blueprintjs/core';
import ace from 'brace';
import React from 'react';
import AceEditor from 'react-ace';
import ReactDOMServer from 'react-dom/server';
-import { SQLFunctionDoc } from '../../../../lib/sql-function-doc';
+import { SQL_DATE_TYPES, SQL_FUNCTIONS, SyntaxDescription } from '../../../../lib/sql-function-doc';
import { uniq } from '../../../utils';
import { ColumnMetadata } from '../../../utils/column-metadata';
import { ColumnTreeProps, ColumnTreeState } from '../column-tree/column-tree';
+import { SQL_CONSTANTS, SQL_DYNAMICS, SQL_EXPRESSION_PARTS, SQL_KEYWORDS } from './keywords';
+
import './query-input.scss';
const langTools = ace.acequire('ace/ext/language_tools');
@@ -46,7 +48,6 @@ export interface QueryInputState {
}
export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputState> {
-
static getDerivedStateFromProps(props: ColumnTreeProps, state: ColumnTreeState) {
const { columnMetadata } = props;
@@ -80,33 +81,29 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
private replaceDefaultAutoCompleter = () => {
if (!langTools) return;
- /*
- Please refer to the source code @
- https://github.com/ajaxorg/ace/blob/9b5b63d1dc7c1b81b58d30c87d14b5905d030ca5/lib/ace/ext/language_tools.js#L41
- for the implementation of keyword completer
- */
+
+ const keywordList = ([] as any[]).concat(
+ SQL_KEYWORDS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })),
+ SQL_EXPRESSION_PARTS.map(v => ({ name: v, value: v, score: 0, meta: 'keyword' })),
+ SQL_CONSTANTS.map(v => ({ name: v, value: v, score: 0, meta: 'constant' })),
+ SQL_DYNAMICS.map(v => ({ name: v, value: v, score: 0, meta: 'dynamic' })),
+ SQL_DATE_TYPES.map(v => ({ name: v.syntax, value: v.syntax, score: 0, meta: 'keyword' }))
+ );
+
const keywordCompleter = {
getCompletions: (editor: any, session: any, pos: any, prefix: any, callback: any) => {
- if (session.$mode.completer) {
- return session.$mode.completer.getCompletions(editor, session, pos, prefix, callback);
- }
- const state = editor.session.getState(pos.row);
- let keywordCompletions = session.$mode.getCompletions(state, session, pos, prefix);
- keywordCompletions = keywordCompletions.map((d: any) => {
- return Object.assign(d, {name: d.name.toUpperCase(), value: d.value.toUpperCase()});
- });
- return callback(null, keywordCompletions);
+ return callback(null, keywordList);
}
};
+
langTools.setCompleters([langTools.snippetCompleter, langTools.textCompleter, keywordCompleter]);
}
private addFunctionAutoCompleter = (): void => {
if (!langTools) return;
- const functionList: any[] = SQLFunctionDoc.map((entry: any) => {
- let funcName: string = entry.syntax.replace(/\(.*\)/, '()');
- if (!funcName.includes('(')) funcName = funcName.substr(0, 10);
+ const functionList: any[] = SQL_FUNCTIONS.map((entry: SyntaxDescription) => {
+ const funcName: string = entry.syntax.replace(/\(.*\)/, '()');
return {
value: funcName,
score: 80,
diff --git a/web-console/src/views/query-view/run-button/run-button.tsx b/web-console/src/views/query-view/run-button/run-button.tsx
index 8b8e3de..b79c256 100644
--- a/web-console/src/views/query-view/run-button/run-button.tsx
+++ b/web-console/src/views/query-view/run-button/run-button.tsx
@@ -90,7 +90,7 @@ export class RunButton extends React.PureComponent<RunButtonProps, RunButtonStat
return <Menu>
<MenuItem
icon={IconNames.HELP}
- text="Docs"
+ text="Query docs"
href={runeMode ? DRUID_DOCS_RUNE : DRUID_DOCS_SQL}
target="_blank"
/>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org