diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx index 9b2df96fcdd..bbe34b8f46a 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx +++ b/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; let typeaheadContext: string | null = null; + // Built-in functions if (wrapperClasses.contains('function-context')) { 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)) { 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')) { if (modelPrefix.match(/\s$/i)) { prefix = ''; @@ -156,7 +150,7 @@ export default class KustoQueryField extends QueryField { } else if (Plain.serialize(this.state.value) === '') { typeaheadContext = 'context-new'; if (this.schema) { - suggestionGroups = this._getInitialSuggestions(); + suggestionGroups = this.getInitialSuggestions(); } else { this.fetchSchema(); setTimeout(this.onTypeahead, 0); @@ -187,7 +181,7 @@ export default class KustoQueryField extends QueryField { .filter(group => group.items.length > 0); // console.log('onTypeahead', selection.anchorNode, wrapperClasses, text, offset, prefix, typeaheadContext); - // console.log('onTypeahead', modelPrefix, prefix, typeaheadContext); + console.log('onTypeahead', modelPrefix, prefix, typeaheadContext); this.setState({ typeaheadPrefix: prefix, @@ -293,6 +287,10 @@ export default class KustoQueryField extends QueryField { // ]; // } + private getInitialSuggestions(): SuggestionGroup[] { + return this.getTableSuggestions(); + } + private getKeywordSuggestions(): SuggestionGroup[] { return [ { @@ -323,50 +321,28 @@ export default class KustoQueryField extends QueryField { ]; } - private _getInitialSuggestions(): SuggestionGroup[] { + private getFunctionSuggestions(): SuggestionGroup[] { return [ { 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 [ { 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 { @@ -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 normalizedQuery = normalizeQuery(query); 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() {