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(/&#124;/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