Browse Source

azuremonitor: more autocomplete suggestions for built-in functions

Alexander Zobnin 6 years ago
parent
commit
4b5bfd3da5

+ 89 - 108
public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx

@@ -96,57 +96,51 @@ export default class KustoQueryField extends QueryField {
       const wrapperClasses = wrapperNode.classList;
       const wrapperClasses = wrapperNode.classList;
       let typeaheadContext: string | null = null;
       let typeaheadContext: string | null = null;
 
 
+      // Built-in functions
       if (wrapperClasses.contains('function-context')) {
       if (wrapperClasses.contains('function-context')) {
         typeaheadContext = 'context-function';
         typeaheadContext = 'context-function';
-        if (this.fields) {
-          suggestionGroups = this.getKeywordSuggestions();
-        } else {
-          this._fetchFields();
-          return;
-        }
+        suggestionGroups = this.getColumnSuggestions();
+
+      // where
       } else if (modelPrefix.match(/(where\s(\w+\b)?$)/i)) {
       } else if (modelPrefix.match(/(where\s(\w+\b)?$)/i)) {
         typeaheadContext = 'context-where';
         typeaheadContext = 'context-where';
-        const fullQuery = Plain.serialize(this.state.value);
-        const table = this.getTableFromContext(fullQuery);
-        if (table) {
-          suggestionGroups = this.getWhereSuggestions(table);
-        } else {
-          return;
-        }
-      } else if (modelPrefix.match(/(,\s*$)/)) {
-        typeaheadContext = 'context-multiple-fields';
-        if (this.fields) {
-          suggestionGroups = this.getKeywordSuggestions();
-        } else {
-          this._fetchFields();
-          return;
-        }
-      } else if (modelPrefix.match(/(from\s$)/i)) {
-        typeaheadContext = 'context-from';
-        if (this.events) {
-          suggestionGroups = this.getKeywordSuggestions();
-        } else {
-          this._fetchEvents();
-          return;
-        }
-      } else if (modelPrefix.match(/(^select\s\w*$)/i)) {
-        typeaheadContext = 'context-select';
-        if (this.fields) {
-          suggestionGroups = this.getKeywordSuggestions();
-        } else {
-          this._fetchFields();
-          return;
-        }
-      } else if (modelPrefix.match(/from\s\S+\s\w*$/i)) {
-        prefix = '';
-        typeaheadContext = 'context-since';
-        suggestionGroups = this.getKeywordSuggestions();
-      // } else if (modelPrefix.match(/\d+\s\w*$/)) {
-      //   typeaheadContext = 'context-number';
-      //   suggestionGroups = this._getAfterNumberSuggestions();
-      } else if (modelPrefix.match(/ago\b/i) || modelPrefix.match(/facet\b/i) || modelPrefix.match(/\$__timefilter\b/i)) {
-        typeaheadContext = 'context-timeseries';
-        suggestionGroups = this.getKeywordSuggestions();
+        suggestionGroups = this.getColumnSuggestions();
+
+      // summarize by
+      } else if (modelPrefix.match(/(summarize\s(\w+\b)?$)/i)) {
+        typeaheadContext = 'context-summarize';
+        suggestionGroups = this.getFunctionSuggestions();
+      } else if (modelPrefix.match(/(summarize\s(.+\s)?by\s+([^,\s]+,\s*)*([^,\s]+\b)?$)/i)) {
+        typeaheadContext = 'context-summarize-by';
+        suggestionGroups = this.getColumnSuggestions();
+
+      // order by, top X by, ... by ...
+      } else if (modelPrefix.match(/(by\s+([^,\s]+,\s*)*([^,\s]+\b)?$)/i)) {
+        typeaheadContext = 'context-by';
+        suggestionGroups = this.getColumnSuggestions();
+
+      // join
+      } else if (modelPrefix.match(/(on\s(.+\b)?$)/i)) {
+        typeaheadContext = 'context-join-on';
+        suggestionGroups = this.getColumnSuggestions();
+      } else if (modelPrefix.match(/(join\s+(\(\s+)?(\w+\b)?$)/i)) {
+        typeaheadContext = 'context-join';
+        suggestionGroups = this.getTableSuggestions();
+
+      // distinct
+      } else if (modelPrefix.match(/(distinct\s(.+\b)?$)/i)) {
+        typeaheadContext = 'context-distinct';
+        suggestionGroups = this.getColumnSuggestions();
+
+      // database()
+      } else if (modelPrefix.match(/(database\(\"(\w+)\"\)\.(.+\b)?$)/i)) {
+        typeaheadContext = 'context-database-table';
+        const db = this.getDBFromDatabaseFunction(modelPrefix);
+        console.log(db);
+        suggestionGroups = this.getTableSuggestions(db);
+        prefix = prefix.replace('.', '');
+
+      // built-in
       } else if (prefix && !wrapperClasses.contains('argument')) {
       } else if (prefix && !wrapperClasses.contains('argument')) {
         if (modelPrefix.match(/\s$/i)) {
         if (modelPrefix.match(/\s$/i)) {
           prefix = '';
           prefix = '';
@@ -156,7 +150,7 @@ export default class KustoQueryField extends QueryField {
       } else if (Plain.serialize(this.state.value) === '') {
       } else if (Plain.serialize(this.state.value) === '') {
         typeaheadContext = 'context-new';
         typeaheadContext = 'context-new';
         if (this.schema) {
         if (this.schema) {
-          suggestionGroups = this._getInitialSuggestions();
+          suggestionGroups = this.getInitialSuggestions();
         } else {
         } else {
           this.fetchSchema();
           this.fetchSchema();
           setTimeout(this.onTypeahead, 0);
           setTimeout(this.onTypeahead, 0);
@@ -187,7 +181,7 @@ export default class KustoQueryField extends QueryField {
         .filter(group => group.items.length > 0);
         .filter(group => group.items.length > 0);
 
 
       // console.log('onTypeahead', selection.anchorNode, wrapperClasses, text, offset, prefix, typeaheadContext);
       // console.log('onTypeahead', selection.anchorNode, wrapperClasses, text, offset, prefix, typeaheadContext);
-      // console.log('onTypeahead', modelPrefix, prefix, typeaheadContext);
+      console.log('onTypeahead', modelPrefix, prefix, typeaheadContext);
 
 
       this.setState({
       this.setState({
         typeaheadPrefix: prefix,
         typeaheadPrefix: prefix,
@@ -293,6 +287,10 @@ export default class KustoQueryField extends QueryField {
   //   ];
   //   ];
   // }
   // }
 
 
+  private getInitialSuggestions(): SuggestionGroup[] {
+    return this.getTableSuggestions();
+  }
+
   private getKeywordSuggestions(): SuggestionGroup[] {
   private getKeywordSuggestions(): SuggestionGroup[] {
     return [
     return [
       {
       {
@@ -323,50 +321,28 @@ export default class KustoQueryField extends QueryField {
     ];
     ];
   }
   }
 
 
-  private _getInitialSuggestions(): SuggestionGroup[] {
+  private getFunctionSuggestions(): SuggestionGroup[] {
     return [
     return [
       {
       {
         prefixMatch: true,
         prefixMatch: true,
-        label: 'Tables',
-        items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name }))
+        label: 'Functions',
+        items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
+      },
+      {
+        prefixMatch: true,
+        label: 'Macros',
+        items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
       }
       }
     ];
     ];
-
-    // return [
-    //   {
-    //     prefixMatch: true,
-    //     label: 'Keywords',
-    //     items: KEYWORDS.map(wrapText)
-    //   },
-    //   {
-    //     prefixMatch: true,
-    //     label: 'Operators',
-    //     items: operatorTokens.map((s: any) => { s.type = 'function'; return s; })
-    //   },
-    //   {
-    //     prefixMatch: true,
-    //     label: 'Functions',
-    //     items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
-    //   },
-    //   {
-    //     prefixMatch: true,
-    //     label: 'Macros',
-    //     items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
-    //   }
-    // ];
   }
   }
 
 
-  private getWhereSuggestions(table: string): SuggestionGroup[] {
-    const tableSchema = this.schema.Databases.Default.Tables[table];
-    if (tableSchema) {
+  getTableSuggestions(db = 'Default'): SuggestionGroup[] {
+    if (this.schema.Databases[db]) {
       return [
       return [
         {
         {
           prefixMatch: true,
           prefixMatch: true,
-          label: 'Fields',
-          items: _.map(tableSchema.OrderedColumns, (f: any) => ({
-            text: f.Name,
-            hint: f.Type
-          }))
+          label: 'Tables',
+          items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name }))
         }
         }
       ];
       ];
     } else {
     } else {
@@ -374,7 +350,28 @@ export default class KustoQueryField extends QueryField {
     }
     }
   }
   }
 
 
-  private getTableFromContext(query: string) {
+  private getColumnSuggestions(): SuggestionGroup[] {
+    const table = this.getTableFromContext();
+    if (table) {
+      const tableSchema = this.schema.Databases.Default.Tables[table];
+      if (tableSchema) {
+        return [
+          {
+            prefixMatch: true,
+            label: 'Fields',
+            items: _.map(tableSchema.OrderedColumns, (f: any) => ({
+              text: f.Name,
+              hint: f.Type
+            }))
+          }
+        ];
+      }
+    }
+    return [];
+  }
+
+  private getTableFromContext() {
+    const query = Plain.serialize(this.state.value);
     const tablePattern = /^\s*(\w+)\s*|/g;
     const tablePattern = /^\s*(\w+)\s*|/g;
     const normalizedQuery = normalizeQuery(query);
     const normalizedQuery = normalizeQuery(query);
     const match = tablePattern.exec(normalizedQuery);
     const match = tablePattern.exec(normalizedQuery);
@@ -385,30 +382,14 @@ export default class KustoQueryField extends QueryField {
     }
     }
   }
   }
 
 
-  private async _fetchEvents() {
-    // const query = 'events';
-    // const result = await this.request(query);
-
-    // if (result === undefined) {
-    //   this.events = [];
-    // } else {
-    //   this.events = result;
-    // }
-    // setTimeout(this.onTypeahead, 0);
-
-    //Stub
-    this.events = [];
-  }
-
-  private async _fetchFields() {
-    // const query = 'fields';
-    // const result = await this.request(query);
-
-    // this.fields = result || [];
-
-    // setTimeout(this.onTypeahead, 0);
-    // Stub
-    this.fields = [];
+  private getDBFromDatabaseFunction(prefix: string) {
+    const databasePattern = /database\(\"(\w+)\"\)/gi;
+    const match = databasePattern.exec(prefix);
+    if (match && match.length > 1 && match[0] && match[1]) {
+      return match[1];
+    } else {
+      return null;
+    }
   }
   }
 
 
   private async fetchSchema() {
   private async fetchSchema() {