Selaa lähdekoodia

HUE-7981 [frontend] Move all the NavOpt popularity data for tables to the data catalog

This introduces a MultiTableEntry in the DataCatalog as most of the popularity data is relative to multiple tables.
Johan Ahlen 7 vuotta sitten
vanhempi
commit
47a72a30ef

+ 60 - 195
desktop/core/src/desktop/static/desktop/js/apiHelper.js

@@ -1586,7 +1586,7 @@ var ApiHelper = (function () {
    *
    * @param {Object} options
    * @param {boolean} [options.silenceErrors]
-   * @param {string[string[]]} options.paths
+   * @param {string[][]} options.paths
    * @return {CancellablePromise}
    */
   ApiHelper.prototype.fetchNavOptPopularity = function (options) {
@@ -1623,30 +1623,31 @@ var ApiHelper = (function () {
   };
 
   /**
-   * Fetches navOpt meta for the given path, only possible for tables atm.
+   * Fetches the popularity for various aspects of the given tables
    *
+   * @param {ApiHelper} apiHelper
    * @param {Object} options
    * @param {boolean} [options.silenceErrors]
-   * @param {string[]} options.path
-   *
+   * @param {string[][]} options.paths
+   * @param {string} url
    * @return {CancellablePromise}
    */
-  ApiHelper.prototype.fetchNavOptMeta = function (options) {
-    var self = this;
+  var genericNavOptMultiTableFetch = function (apiHelper, options, url) {
     var deferred = $.Deferred();
 
-    var request = self.simplePost(NAV_OPT_URLS.TABLE_DETAILS, {
-      databaseName: options.path[0],
-      tableName: options.path[1]
-    }, {
+    var dbTables = {};
+    options.paths.forEach(function (path) {
+      dbTables[path.join('.')] = true;
+    });
+    var data = {
+      dbTables: ko.mapping.toJSON(Object.keys(dbTables))
+    };
+
+    var request = apiHelper.simplePost(url, data, {
       silenceErrors: options.silenceErrors,
-      successCallback: function (response) {
-        if (response.status === 0 && response.details) {
-          resonse.details.hueTimestamp = Date.now();
-          deferred.resolve(response.details);
-        } else {
-          deferred.reject();
-        }
+      successCallback: function (data) {
+        data.hueTimestamp = Date.now();
+        deferred.resolve(data);
       },
       errorCallback: deferred.reject
     });
@@ -1654,224 +1655,88 @@ var ApiHelper = (function () {
     return new CancellablePromise(deferred, request);
   };
 
-  ApiHelper.prototype.getClusterConfig = function (data) {
-    return $.post(FETCH_CONFIG, data);
-  };
-
-  ApiHelper.prototype.createNavOptDbTablesJson = function (options) {
-    var self = this;
-    var tables = [];
-    var tableIndex = {};
-
-    var promise = $.Deferred();
-
-    DataCatalog.getChildren({
-      sourceType: options.sourceType,
-      path: [],
-      silenceErrors: options.silenceErrors
-    }).done(function (dbEntries) {
-      var databases = $.map(dbEntries, function (entry) { return entry.name; });
-      options.tables.forEach(function (table) {
-        if (table.subQuery || !table.identifierChain) {
-          return;
-        }
-        var clonedIdentifierChain = table.identifierChain.concat();
-
-        var databasePrefix;
-        if (clonedIdentifierChain.length > 1 && clonedIdentifierChain[0].name && databases.indexOf(clonedIdentifierChain[0].name.toLowerCase()) > -1) {
-          databasePrefix = clonedIdentifierChain.shift().name + '.';
-        } else if (options.defaultDatabase) {
-          databasePrefix = options.defaultDatabase + '.';
-        } else {
-          databasePrefix = '';
-        }
-        var identifier = databasePrefix  + $.map(clonedIdentifierChain, function (identifier) { return identifier.name }).join('.');
-        if (!tableIndex[databasePrefix  + $.map(clonedIdentifierChain, function (identifier) { return identifier.name }).join('.')]) {
-          tables.push(identifier);
-          tableIndex[identifier] = true;
-        }
-      });
-      promise.resolve(ko.mapping.toJSON(tables));
-    }).fail(function () {
-      promise.resolve(ko.mapping.toJSON(tables));
-    });
-
-    return promise;
-  };
-
   /**
-   * Fetches the top tables for the given database
+   * Fetches the popular aggregate functions for the given tables
    *
    * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
-   *
-   * @param {Object[]} options.database
+   * @param {string[][]} options.paths
+   * @return {CancellablePromise}
    */
-  // TODO: Add to DataCatalog
-  ApiHelper.prototype.fetchNavOptTopTables = function (options) {
-    var self = this;
-    return self.fetchNavOptCached(NAV_OPT_URLS.TOP_TABLES, options, function (data) {
-      return data.status === 0;
-    });
+  ApiHelper.prototype.fetchNavOptTopAggs = function (options) {
+    return genericNavOptMultiTableFetch(this, options, NAV_OPT_URLS.TOP_AGGS);
   };
 
   /**
-   * Fetches the top columns for the given tables
+   * Fetches the popular columns for the given tables
    *
    * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
-   *
-   * @param {Object[]} options.tables
-   * @param {Object[]} options.tables.identifierChain
-   * @param {string} options.tables.identifierChain.name
-   * @param {string} [options.defaultDatabase]
+   * @param {string[][]} options.paths
+   * @return {CancellablePromise}
    */
-  // TODO: Add to DataCatalog
   ApiHelper.prototype.fetchNavOptTopColumns = function (options) {
-    var self = this;
-    return self.fetchNavOptCached(NAV_OPT_URLS.TOP_COLUMNS, options, function (data) {
-      return data.status === 0;
-    });
+    return genericNavOptMultiTableFetch(this, options, NAV_OPT_URLS.TOP_COLUMNS);
   };
 
   /**
-   * Fetches the popular joins for the given tables
+   * Fetches the popular filters for the given tables
    *
    * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
-   *
-   * @param {Object[]} options.tables
-   * @param {Object[]} options.tables.identifierChain
-   * @param {string} options.tables.identifierChain.name
-   * @param {string} [options.defaultDatabase]
+   * @param {string[][]} options.paths
+   * @return {CancellablePromise}
    */
-  ApiHelper.prototype.fetchNavOptPopularJoins = function (options) {
-    var self = this;
-    return self.fetchNavOptCached(NAV_OPT_URLS.TOP_JOINS, options, function (data) {
-      return data.status === 0;
-    });
+  ApiHelper.prototype.fetchNavOptTopFilters = function (options) {
+    return genericNavOptMultiTableFetch(this, options, NAV_OPT_URLS.TOP_FILTERS);
   };
 
   /**
-   * Fetches the popular filters for the given tables
+   * Fetches the popular joins for the given tables
    *
    * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
-   *
-   * @param {Object[]} options.tables
-   * @param {Object[]} options.tables.identifierChain
-   * @param {string} options.tables.identifierChain.name
-   * @param {string} [options.defaultDatabase]
+   * @param {string[][]} options.paths
+   * @return {CancellablePromise}
    */
-  ApiHelper.prototype.fetchNavOptTopFilters = function (options) {
-    var self = this;
-    return self.fetchNavOptCached(NAV_OPT_URLS.TOP_FILTERS, options, function (data) {
-      return data.status === 0;
-    });
+  ApiHelper.prototype.fetchNavOptTopJoins = function (options) {
+    return genericNavOptMultiTableFetch(this, options, NAV_OPT_URLS.TOP_JOINS);
   };
 
   /**
-   * Fetches the popular aggregate functions for the given tables
+   * Fetches navOpt meta for the given path, only possible for tables atm.
    *
    * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
    * @param {boolean} [options.silenceErrors]
+   * @param {string[]} options.path
    *
-   * @param {number} options.timeout
-   * @param {Object[]} options.tables
-   * @param {Object[]} options.tables.identifierChain
-   * @param {string} options.tables.identifierChain.name
-   * @param {string} [options.defaultDatabase]
+   * @return {CancellablePromise}
    */
-  ApiHelper.prototype.fetchNavOptTopAggs = function (options) {
-    var self = this;
-    return self.fetchNavOptCached(NAV_OPT_URLS.TOP_AGGS, options, function (data) {
-      return data.status === 0;
-    });
-  };
-
-  ApiHelper.prototype.fetchNavOptCached = function (url, options, cacheCondition) {
+  ApiHelper.prototype.fetchNavOptMeta = function (options) {
     var self = this;
+    var deferred = $.Deferred();
 
-    var performFetch = function (data, hash) {
-      var promise = self.queueManager.getQueued(url, hash);
-      var firstInQueue = typeof promise === 'undefined';
-      if (firstInQueue) {
-        promise = $.Deferred();
-        self.queueManager.addToQueue(promise, url, hash);
-      }
-
-      promise.done(options.successCallback).fail(self.assistErrorCallback(options)).always(function () {
-        if (typeof options.editor !== 'undefined' && options.editor !== null) {
-          options.editor.hideSpinner();
-        }
-      });
-
-      if (!firstInQueue) {
-        return;
-      }
-
-      var fetchFunction = function (storeInCache) {
-        if (options.timeout === 0) {
-          self.assistErrorCallback(options)({ status: -1 });
-          return;
+    var request = self.simplePost(NAV_OPT_URLS.TABLE_DETAILS, {
+      databaseName: options.path[0],
+      tableName: options.path[1]
+    }, {
+      silenceErrors: options.silenceErrors,
+      successCallback: function (response) {
+        if (response.status === 0 && response.details) {
+          resonse.details.hueTimestamp = Date.now();
+          deferred.resolve(response.details);
+        } else {
+          deferred.reject();
         }
+      },
+      errorCallback: deferred.reject
+    });
 
-        return $.ajax({
-          type: 'post',
-          url: url,
-          data: data,
-          timeout: options.timeout
-        })
-          .done(function (data) {
-            if (data.status === 0) {
-              if (cacheCondition(data)) {
-                storeInCache(data);
-              }
-              promise.resolve(data);
-            } else {
-              promise.reject(data);
-            }
-          })
-          .fail(promise.reject);
-      };
-
-      return fetchCached.bind(self)($.extend({}, options, {
-        url: url,
-        hash: hash,
-        cacheType: 'optimizer',
-        fetchFunction: fetchFunction,
-        promise: promise
-      }));
-    }
+    return new CancellablePromise(deferred, request);
+  };
 
-    var promise = $.Deferred();
-    if (options.tables) {
-      self.createNavOptDbTablesJson(options).done(function (json) {
-        promise.resolve(performFetch({
-          dbTables: json
-        }, json.hashCode()))
-      });
-    } else if (options.database) {
-      promise.resolve(performFetch({
-        database: options.database
-      }, options.database));
-    }
-    return promise;
+  ApiHelper.prototype.getClusterConfig = function (data) {
+    return $.post(FETCH_CONFIG, data);
   };
 
   ApiHelper.prototype.fetchHueDocsInteractive = function (query) {

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1637 - 1340
desktop/core/src/desktop/static/desktop/js/dataCatalog.js


+ 255 - 234
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -52,6 +52,22 @@ var SqlAutocompleter2 = (function () {
     JOIN: -1
   };
 
+  SqlAutocompleter2.prototype.tableIdentifierChainsToPaths = function (tables, activeDatabase) {
+    var paths = [];
+    tables.forEach(function (table) {
+      // Could be subquery
+      var isTable = table.identifierChain.every(function (identifier) { return typeof identifier.name !== 'undefined' });
+      if (isTable) {
+        var path = $.map(table.identifierChain, function (identifier) { return identifier.name; });
+        if (path.length === 1) {
+          path.unshift(activeDatabase);
+        }
+        paths.push(path)
+      }
+    });
+    return paths;
+  };
+
   SqlAutocompleter2.prototype.autocomplete = function (beforeCursor, afterCursor, callback, editor) {
     var self = this;
     var parseResult = sqlAutocompleteParser.parseSql(beforeCursor, afterCursor, self.snippet.type(), false);
@@ -129,96 +145,100 @@ var SqlAutocompleter2 = (function () {
     if (parseResult.suggestJoins && HAS_OPTIMIZER) {
       var joinsDeferral = $.Deferred();
       deferrals.push(joinsDeferral);
-      self.snippet.getApiHelper().fetchNavOptPopularJoins({
-        sourceType: self.snippet.type(),
-        timeout: self.timeout,
-        defaultDatabase: database,
-        silenceErrors: true,
-        tables: parseResult.suggestJoins.tables,
-        successCallback: function (data) {
-          data.values.forEach(function (value) {
-            var suggestionString = parseResult.suggestJoins.prependJoin ? (parseResult.lowerCase ? 'join ' : 'JOIN ') : '';
-            var first = true;
-
-            var existingTables = {};
-            parseResult.suggestJoins.tables.forEach(function (table) {
-              existingTables[table.identifierChain[table.identifierChain.length - 1].name] = true;
-            });
 
-            var joinRequired = false;
-            var tablesAdded = false;
-            value.tables.forEach(function (table) {
-              var tableParts = table.split('.');
-              if (!existingTables[tableParts[tableParts.length - 1]]) {
-                tablesAdded = true;
-                var identifier = self.convertNavOptQualifiedIdentifier(table, database, parseResult.suggestJoins.tables, false);
-                suggestionString += joinRequired ? (parseResult.lowerCase ? ' join ' : ' JOIN ') + identifier : identifier;
-                joinRequired = true;
-              }
-            });
+      var paths = self.tableIdentifierChainsToPaths(parseResult.suggestJoins.tables, database);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          multiTableEntry.getTopJoins({ silenceErrors: true }).done(function (topJoins) {
+            if (topJoins.values) {
+              topJoins.values.forEach(function (value) {
+                var suggestionString = parseResult.suggestJoins.prependJoin ? (parseResult.lowerCase ? 'join ' : 'JOIN ') : '';
+                var first = true;
+
+                var existingTables = {};
+                parseResult.suggestJoins.tables.forEach(function (table) {
+                  existingTables[table.identifierChain[table.identifierChain.length - 1].name] = true;
+                });
 
-            if (value.joinCols.length > 0) {
-              if (!tablesAdded && parseResult.suggestJoins.prependJoin) {
-                suggestionString = '';
-                tablesAdded = true;
-              }
-              suggestionString += parseResult.lowerCase ? ' on ' : ' ON ';
-            }
-            if (tablesAdded) {
-              value.joinCols.forEach(function (joinColPair) {
-                if (!first) {
-                  suggestionString += parseResult.lowerCase ? ' and ' : ' AND ';
+                var joinRequired = false;
+                var tablesAdded = false;
+                value.tables.forEach(function (table) {
+                  var tableParts = table.split('.');
+                  if (!existingTables[tableParts[tableParts.length - 1]]) {
+                    tablesAdded = true;
+                    var identifier = self.convertNavOptQualifiedIdentifier(table, database, parseResult.suggestJoins.tables, false);
+                    suggestionString += joinRequired ? (parseResult.lowerCase ? ' join ' : ' JOIN ') + identifier : identifier;
+                    joinRequired = true;
+                  }
+                });
+
+                if (value.joinCols.length > 0) {
+                  if (!tablesAdded && parseResult.suggestJoins.prependJoin) {
+                    suggestionString = '';
+                    tablesAdded = true;
+                  }
+                  suggestionString += parseResult.lowerCase ? ' on ' : ' ON ';
+                }
+                if (tablesAdded) {
+                  value.joinCols.forEach(function (joinColPair) {
+                    if (!first) {
+                      suggestionString += parseResult.lowerCase ? ' and ' : ' AND ';
+                    }
+                    suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], database, parseResult.suggestJoins.tables, true) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], database, parseResult.suggestJoins.tables, true);
+                    first = false;
+                  });
+                  completions.push({
+                    value: suggestionString,
+                    meta: 'join',
+                    weight: parseResult.suggestJoins.prependJoin ? DEFAULT_WEIGHTS.JOIN : DEFAULT_WEIGHTS.POPULAR_ACTIVE_JOIN,
+                    docHTML: self.createJoinHtml(suggestionString)
+                  });
                 }
-                suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], database, parseResult.suggestJoins.tables, true) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], database, parseResult.suggestJoins.tables, true);
-                first = false;
-              });
-              completions.push({
-                value: suggestionString,
-                meta: 'join',
-                weight: parseResult.suggestJoins.prependJoin ? DEFAULT_WEIGHTS.JOIN : DEFAULT_WEIGHTS.POPULAR_ACTIVE_JOIN,
-                docHTML: self.createJoinHtml(suggestionString)
               });
             }
-          });
-          joinsDeferral.resolve();
-        },
-        errorCallback: joinsDeferral.resolve
-      });
+            joinsDeferral.resolve();
+          }).fail(joinsDeferral.resolve);
+        }).fail(joinsDeferral.resolve);
+      } else {
+        joinsDeferral.resolve();
+      }
     }
 
     if (parseResult.suggestJoinConditions && HAS_OPTIMIZER) {
       var joinConditionsDeferral = $.Deferred();
       deferrals.push(joinConditionsDeferral);
-      self.snippet.getApiHelper().fetchNavOptPopularJoins({
-        sourceType: self.snippet.type(),
-        timeout: self.timeout,
-        defaultDatabase: database,
-        silenceErrors: true,
-        tables: parseResult.suggestJoinConditions.tables,
-        successCallback: function (data) {
-          data.values.forEach(function (value) {
-            if (value.joinCols.length > 0) {
-              var suggestionString = parseResult.suggestJoinConditions.prependOn ? (parseResult.lowerCase ? 'on ' : 'ON ') : '';
-              var first = true;
-              value.joinCols.forEach(function (joinColPair) {
-                if (!first) {
-                  suggestionString += parseResult.lowerCase ? ' and ' : ' AND ';
+
+      var paths = self.tableIdentifierChainsToPaths(parseResult.suggestJoinConditions.tables, database);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          multiTableEntry.getTopJoins({ silenceErrors: true }).done(function (topJoins) {
+            if (topJoins.values) {
+              topJoins.values.forEach(function (value) {
+                if (value.joinCols.length > 0) {
+                  var suggestionString = parseResult.suggestJoinConditions.prependOn ? (parseResult.lowerCase ? 'on ' : 'ON ') : '';
+                  var first = true;
+                  value.joinCols.forEach(function (joinColPair) {
+                    if (!first) {
+                      suggestionString += parseResult.lowerCase ? ' and ' : ' AND ';
+                    }
+                    suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], database, parseResult.suggestJoinConditions.tables, true) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], database, parseResult.suggestJoinConditions.tables, true);
+                    first = false;
+                  });
+                  completions.push({
+                    value: suggestionString,
+                    meta: 'condition',
+                    weight: DEFAULT_WEIGHTS.POPULAR_JOIN_CONDITION,
+                    docHTML: self.createJoinHtml(suggestionString)
+                  });
                 }
-                suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], database, parseResult.suggestJoinConditions.tables, true) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], database,parseResult.suggestJoinConditions.tables, true);
-                first = false;
-              });
-              completions.push({
-                value: suggestionString,
-                meta: 'condition',
-                weight: DEFAULT_WEIGHTS.POPULAR_JOIN_CONDITION,
-                docHTML: self.createJoinHtml(suggestionString)
               });
             }
-          });
-          joinConditionsDeferral.resolve();
-        },
-        errorCallback: joinConditionsDeferral.resolve
-      });
+            joinConditionsDeferral.resolve();
+          }).fail(joinConditionsDeferral.resolve);
+        }).fail(joinConditionsDeferral.resolve);
+      } else {
+        joinConditionsDeferral.resolve();
+      }
     }
 
     if (parseResult.suggestFunctions) {
@@ -240,58 +260,58 @@ var SqlAutocompleter2 = (function () {
       if (HAS_OPTIMIZER && typeof parseResult.suggestAggregateFunctions !== 'undefined' && parseResult.suggestAggregateFunctions.tables.length > 0) {
         var suggestAggregatesDeferral = $.Deferred();
         deferrals.push(suggestAggregatesDeferral);
-        self.snippet.getApiHelper().fetchNavOptTopAggs({
-          sourceType: self.snippet.type(),
-          timeout: self.timeout,
-          defaultDatabase: database,
-          silenceErrors: true,
-          tables: parseResult.suggestAggregateFunctions.tables,
-          successCallback: function (data) {
-            if (data.values.length > 0) {
-
-              // TODO: Handle column conflicts with multiple tables
-
-              // Substitute qualified table identifiers with either alias or empty string
-              var substitutions = [];
-              parseResult.suggestAggregateFunctions.tables.forEach(function (table) {
-                var replaceWith = table.alias ? table.alias + '.' : '';
-                if (table.identifierChain.length > 1) {
-                  substitutions.push({
-                    replace: new RegExp($.map(table.identifierChain, function (identifier) {
-                          return identifier.name
-                        }).join('\.') + '\.', 'gi'),
-                    with: replaceWith
-                  })
-                } else if (table.identifierChain.length === 1) {
-                  substitutions.push({
-                    replace: new RegExp(database + '\.' + table.identifierChain[0].name + '\.', 'gi'),
-                    with: replaceWith
-                  });
-                  substitutions.push({
-                    replace: new RegExp(table.identifierChain[0].name + '\.', 'gi'),
-                    with: replaceWith
-                  })
-                }
-              });
 
-              data.values.forEach(function (value) {
-                var clean = value.aggregateClause;
-                substitutions.forEach(function (substitution) {
-                  clean = clean.replace(substitution.replace, substitution.with);
+        var paths = self.tableIdentifierChainsToPaths(parseResult.suggestAggregateFunctions.tables, database);
+        if (paths.length) {
+          DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+            multiTableEntry.getTopAggs({ silenceErrors: true }).done(function (topAggs) {
+              if (topAggs.values.length > 0) {
+
+                // TODO: Handle column conflicts with multiple tables
+
+                // Substitute qualified table identifiers with either alias or empty string
+                var substitutions = [];
+                parseResult.suggestAggregateFunctions.tables.forEach(function (table) {
+                  var replaceWith = table.alias ? table.alias + '.' : '';
+                  if (table.identifierChain.length > 1) {
+                    substitutions.push({
+                      replace: new RegExp($.map(table.identifierChain, function (identifier) {
+                        return identifier.name
+                      }).join('\.') + '\.', 'gi'),
+                      with: replaceWith
+                    })
+                  } else if (table.identifierChain.length === 1) {
+                    substitutions.push({
+                      replace: new RegExp(database + '\.' + table.identifierChain[0].name + '\.', 'gi'),
+                      with: replaceWith
+                    });
+                    substitutions.push({
+                      replace: new RegExp(table.identifierChain[0].name + '\.', 'gi'),
+                      with: replaceWith
+                    })
+                  }
                 });
 
-                completions.push({
-                  value: clean,
-                  meta: 'aggregate *',
-                  weight: DEFAULT_WEIGHTS.POPULAR_AGGREGATE + value.totalQueryCount,
-                  docHTML: self.createAggregateHtml(value)
-                });
-              })
-            }
-            suggestAggregatesDeferral.resolve();
-          },
-          errorCallback: suggestAggregatesDeferral.resolve
-        });
+                topAggs.values.forEach(function (value) {
+                  var clean = value.aggregateClause;
+                  substitutions.forEach(function (substitution) {
+                    clean = clean.replace(substitution.replace, substitution.with);
+                  });
+
+                  completions.push({
+                    value: clean,
+                    meta: 'aggregate *',
+                    weight: DEFAULT_WEIGHTS.POPULAR_AGGREGATE + value.totalQueryCount,
+                    docHTML: self.createAggregateHtml(value)
+                  });
+                })
+              }
+              suggestAggregatesDeferral.resolve();
+            }).fail(suggestAggregatesDeferral.resolve)
+          }).fail(suggestAggregatesDeferral.resolve);
+        } else {
+          suggestAggregatesDeferral.resolve();
+        }
       }
       deferrals.push(suggestFunctionsDeferral);
     }
@@ -374,83 +394,85 @@ var SqlAutocompleter2 = (function () {
     if (HAS_OPTIMIZER && typeof parseResult.suggestFilters !== 'undefined') {
       var topFiltersDeferral = $.Deferred();
       deferrals.push(topFiltersDeferral);
-      self.snippet.getApiHelper().fetchNavOptTopFilters({
-        sourceType: self.snippet.type(),
-        timeout: self.timeout,
-        defaultDatabase: database,
-        silenceErrors: true,
-        tables: parseResult.suggestFilters.tables,
-        successCallback: function (data) {
-          data.values.forEach(function (value) {
-            if (typeof value.popularValues !== 'undefined' && value.popularValues.length > 0) {
-              value.popularValues.forEach(function (popularValue) {
-                if (typeof popularValue.group !== 'undefined') {
-                  popularValue.group.forEach(function (grp) {
-                    var compVal = parseResult.suggestFilters.prefix ? (parseResult.lowerCase ? parseResult.suggestFilters.prefix.toLowerCase() : parseResult.suggestFilters.prefix) + ' ' : '';
-                    compVal += createNavOptIdentifier(value.tableName, grp.columnName, parseResult.suggestFilters.tables);
-                    if (!/^ /.test(grp.op)) {
-                      compVal += ' ';
-                    }
-                    compVal += parseResult.lowerCase ? grp.op.toLowerCase() : grp.op;
-                    if (!/ $/.test(grp.op)) {
-                      compVal += ' ';
+
+      var paths = self.tableIdentifierChainsToPaths(parseResult.suggestFilters.tables, database);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          multiTableEntry.getTopFilters({ silenceErrors: true }).done(function (topFilters) {
+            if (topFilters.values) {
+              topFilters.values.forEach(function (value) {
+                if (typeof value.popularValues !== 'undefined' && value.popularValues.length > 0) {
+                  value.popularValues.forEach(function (popularValue) {
+                    if (typeof popularValue.group !== 'undefined') {
+                      popularValue.group.forEach(function (grp) {
+                        var compVal = parseResult.suggestFilters.prefix ? (parseResult.lowerCase ? parseResult.suggestFilters.prefix.toLowerCase() : parseResult.suggestFilters.prefix) + ' ' : '';
+                        compVal += createNavOptIdentifier(value.tableName, grp.columnName, parseResult.suggestFilters.tables);
+                        if (!/^ /.test(grp.op)) {
+                          compVal += ' ';
+                        }
+                        compVal += parseResult.lowerCase ? grp.op.toLowerCase() : grp.op;
+                        if (!/ $/.test(grp.op)) {
+                          compVal += ' ';
+                        }
+                        compVal += grp.literal;
+                        completions.push({
+                          value: compVal,
+                          meta: 'filter *',
+                          weight: DEFAULT_WEIGHTS.POPULAR_FILTER,
+                          docHTML: self.createFilterHtml()
+                        });
+                      });
                     }
-                    compVal += grp.literal;
-                    completions.push({
-                      value: compVal,
-                      meta: 'filter *',
-                      weight: DEFAULT_WEIGHTS.POPULAR_FILTER,
-                      docHTML: self.createFilterHtml()
-                    });
                   });
                 }
               });
             }
-          });
-
-          topFiltersDeferral.resolve();
-        },
-        errorCallback: topFiltersDeferral.resolve
-      });
+            topFiltersDeferral.resolve();
+          }).fail(topFiltersDeferral.resolve);
+        }).fail(topFiltersDeferral.resolve);
+      } else {
+        topFiltersDeferral.resolve();
+      }
     }
 
     if (HAS_OPTIMIZER && (typeof parseResult.suggestGroupBys !== 'undefined' || typeof parseResult.suggestOrderBys !== 'undefined')) {
       var tables = typeof parseResult.suggestGroupBys !== 'undefined' ? parseResult.suggestGroupBys.tables : parseResult.suggestOrderBys.tables;
       var groupAndOrderByDeferral = $.Deferred();
       deferrals.push(groupAndOrderByDeferral);
-      self.snippet.getApiHelper().fetchNavOptTopColumns({
-        sourceType: self.snippet.type(),
-        timeout: self.timeout,
-        defaultDatabase: database,
-        silenceErrors: true,
-        tables: tables,
-        successCallback: function (data) {
-          if (parseResult.suggestGroupBys && typeof data.values.groupbyColumns !== 'undefined') {
-            var prefix = parseResult.suggestGroupBys.prefix ? (parseResult.lowerCase ? parseResult.suggestGroupBys.prefix.toLowerCase() : parseResult.suggestGroupBys.prefix) + ' ' : '';
-            data.values.groupbyColumns.forEach(function (col) {
-              completions.push({
-                value: prefix + createNavOptIdentifierForColumn(col, parseResult.suggestGroupBys.tables),
-                meta: 'group *',
-                weight: DEFAULT_WEIGHTS.POPULAR_GROUP_BY + Math.min(col.columnCount, 99),
-                docHTML: self.createGroupByHtml()
+
+      var paths = self.tableIdentifierChainsToPaths(tables, database);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          multiTableEntry.getTopColumns({ silenceErrors: true }).done(function (topColumns) {
+            if (topColumns.values && parseResult.suggestGroupBys && typeof topColumns.values.groupbyColumns !== 'undefined') {
+              var prefix = parseResult.suggestGroupBys.prefix ? (parseResult.lowerCase ? parseResult.suggestGroupBys.prefix.toLowerCase() : parseResult.suggestGroupBys.prefix) + ' ' : '';
+              topColumns.values.groupbyColumns.forEach(function (col) {
+                completions.push({
+                  value: prefix + createNavOptIdentifierForColumn(col, parseResult.suggestGroupBys.tables),
+                  meta: 'group *',
+                  weight: DEFAULT_WEIGHTS.POPULAR_GROUP_BY + Math.min(col.columnCount, 99),
+                  docHTML: self.createGroupByHtml()
+                });
               });
-            });
-          }
-          if (parseResult.suggestOrderBys && typeof data.values.orderbyColumns !== 'undefined') {
-            var prefix = parseResult.suggestOrderBys.prefix ? (parseResult.lowerCase ? parseResult.suggestOrderBys.prefix.toLowerCase() : parseResult.suggestOrderBys.prefix) + ' ' : '';
-            data.values.orderbyColumns.forEach(function (col) {
-              completions.push({
-                value: prefix + createNavOptIdentifierForColumn(col, parseResult.suggestOrderBys.tables),
-                meta: 'order *',
-                weight: DEFAULT_WEIGHTS.POPULAR_ORDER_BY + Math.min(col.columnCount, 99),
-                docHTML: self.createOrderByHtml()
+            }
+            if (topColumns.values && parseResult.suggestOrderBys && typeof topColumns.values.orderbyColumns !== 'undefined') {
+              var prefix = parseResult.suggestOrderBys.prefix ? (parseResult.lowerCase ? parseResult.suggestOrderBys.prefix.toLowerCase() : parseResult.suggestOrderBys.prefix) + ' ' : '';
+              topColumns.values.orderbyColumns.forEach(function (col) {
+                completions.push({
+                  value: prefix + createNavOptIdentifierForColumn(col, parseResult.suggestOrderBys.tables),
+                  meta: 'order *',
+                  weight: DEFAULT_WEIGHTS.POPULAR_ORDER_BY + Math.min(col.columnCount, 99),
+                  docHTML: self.createOrderByHtml()
+                });
               });
-            });
-          }
-          groupAndOrderByDeferral.resolve();
-        },
-        errorCallback: groupAndOrderByDeferral.resolve
-      });
+            }
+            groupAndOrderByDeferral.resolve();
+          }).fail(groupAndOrderByDeferral.resolve);
+        }).fail(groupAndOrderByDeferral.resolve);
+      } else {
+        groupAndOrderByDeferral.resolve();
+      }
+
     }
 
     if (parseResult.suggestColumns) {
@@ -511,38 +533,39 @@ var SqlAutocompleter2 = (function () {
       }
 
       if (HAS_OPTIMIZER && typeof parseResult.suggestColumns.source !== 'undefined') {
-        self.snippet.getApiHelper().fetchNavOptTopColumns({
-          sourceType: self.snippet.type(),
-          timeout: self.timeout,
-          defaultDatabase: database,
-          silenceErrors: true,
-          tables: parseResult.suggestColumns.tables,
-          successCallback: function (data) {
-            var topColumns = [];
-            var values = [];
-            switch (parseResult.suggestColumns.source) {
-              case 'select':
-                values = data.values.selectColumns;
-                break;
-              case 'group by':
-                values = data.values.groupbyColumns;
-                break;
-              case 'order by':
-                values = data.values.orderbyColumns;
-                break;
-              default:
-                values = [];
-            }
-            values.forEach(function (col) {
-              col.path = col.tableName.split('.').concat(col.columnName.split('.').slice(1)).join('.');
+        var paths = self.tableIdentifierChainsToPaths(parseResult.suggestColumns.tables, database);
+        if (paths.length) {
+          DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+            multiTableEntry.getTopColumns({ silenceErrors: true }).done(function (topColumns) {
+              var values = [];
+              if (topColumns.values) {
+                switch (parseResult.suggestColumns.source) {
+                  case 'select':
+                    values = topColumns.values.selectColumns;
+                    break;
+                  case 'group by':
+                    values = topColumns.values.groupbyColumns;
+                    break;
+                  case 'order by':
+                    values = topColumns.values.orderbyColumns;
+                    break;
+                  default:
+                    values = [];
+                }
+                values.forEach(function (col) {
+                  col.path = col.tableName.split('.').concat(col.columnName.split('.').slice(1)).join('.');
+                });
+              }
+              topColumnsDeferral.resolve(values);
+            }).fail(function () {
+              topColumnsDeferral.resolve([])
             });
-
-            topColumnsDeferral.resolve(values);
-          },
-          errorCallback: function () {
-            topColumnsDeferral.resolve([]);
-          }
-        });
+          }).fail(function () {
+            topColumnsDeferral.resolve([])
+          });
+        } else {
+          topColumnsDeferral.resolve([])
+        }
       } else {
         topColumnsDeferral.resolve([]);
       }
@@ -560,21 +583,19 @@ var SqlAutocompleter2 = (function () {
       if (HAS_OPTIMIZER) {
         var topTablesDeferral = $.Deferred();
         deferrals.push(topTablesDeferral);
-        self.snippet.getApiHelper().fetchNavOptTopTables({
-          database: database,
-          sourceType: self.snippet.type(),
-          successCallback: function (data) {
-            var popularityIndex = {};
-            data.top_tables.forEach(function (topTable) {
-              popularityIndex[topTable.name] = topTable.popularity;
-            });
 
-            topTablesDeferral.resolve(popularityIndex);
-          },
-          errorCallback: function () {
-            topTablesDeferral.resolve({});
-          }
+        DataCatalog.getCatalog(self.snippet.type()).loadNavOptPopularityForTables({ paths: [[database]], silenceErrors: true }).done(function (popularTables) {
+          var popularityIndex = {};
+          popularTables.forEach(function (popularTable) {
+            if (popularTable.navOptPopularity) {
+              popularityIndex[popularTable.name] = popularTable.navOptPopularity.popularity;
+            }
+          });
+          topTablesDeferral.resolve(popularityIndex);
+        }).fail(function () {
+          topTablesDeferral.resolve({});
         });
+
         $.when(topTablesDeferral, tableDeferral).done(function (popularityIndex, tableCompletions) {
           tableCompletions.forEach(function (tableCompletion) {
             if (typeof popularityIndex[tableCompletion.name] !== 'undefined') {

+ 215 - 194
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter3.js

@@ -955,6 +955,25 @@ var AutocompleteResults = (function () {
     return pathsDeferred;
   };
 
+  AutocompleteResults.prototype.tableIdentifierChainsToPaths = function (tables) {
+    var self = this;
+    var paths = [];
+    tables.forEach(function (table) {
+      // Could be subquery
+      var isTable = table.identifierChain.every(function (identifier) { return typeof identifier.name !== 'undefined' });
+      if (isTable) {
+        var path = $.map(table.identifierChain, function (identifier) {
+          return identifier.name;
+        });
+        if (path.length === 1) {
+          path.unshift(self.activeDatabase);
+        }
+        paths.push(path);
+      }
+    });
+    return paths;
+  };
+
   AutocompleteResults.prototype.handleJoins = function () {
     var self = this;
     var joinsDeferred = $.Deferred();
@@ -963,72 +982,73 @@ var AutocompleteResults = (function () {
       initLoading(self.loadingJoins, joinsDeferred);
       joinsDeferred.done(self.appendEntries);
 
-      self.lastKnownRequests.push(self.apiHelper.fetchNavOptPopularJoins({
-        sourceType: self.snippet.type(),
-        timeout: AUTOCOMPLETE_TIMEOUT,
-        defaultDatabase: self.activeDatabase,
-        silenceErrors: true,
-        tables: suggestJoins.tables,
-        successCallback: function (data) {
+      var paths = self.tableIdentifierChainsToPaths(suggestJoins.tables);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+        self.cancellablePromises.push(multiTableEntry.getTopJoins({ silenceErrors: true, cancellable: true  }).done(function (topJoins) {
           var joinSuggestions = [];
           var totalCount = 0;
-          data.values.forEach(function (value) {
-
-            var joinType = value.joinType || 'join';
-            joinType += ' ';
-            var suggestionString = suggestJoins.prependJoin ? (self.parseResult.lowerCase ? joinType.toLowerCase() : joinType.toUpperCase()) : '';
-            var first = true;
+          if (topJoins.values) {
+            topJoins.values.forEach(function (value) {
 
-            var existingTables = {};
-            suggestJoins.tables.forEach(function (table) {
-              existingTables[table.identifierChain[table.identifierChain.length - 1].name] = true;
-            });
+              var joinType = value.joinType || 'join';
+              joinType += ' ';
+              var suggestionString = suggestJoins.prependJoin ? (self.parseResult.lowerCase ? joinType.toLowerCase() : joinType.toUpperCase()) : '';
+              var first = true;
 
-            var joinRequired = false;
-            var tablesAdded = false;
-            value.tables.forEach(function (table) {
-              var tableParts = table.split('.');
-              if (!existingTables[tableParts[tableParts.length - 1]]) {
-                tablesAdded = true;
-                var identifier = self.convertNavOptQualifiedIdentifier(table, suggestJoins.tables);
-                suggestionString += joinRequired ? (self.parseResult.lowerCase ? ' join ' : ' JOIN ') + identifier : identifier;
-                joinRequired = true;
-              }
-            });
+              var existingTables = {};
+              suggestJoins.tables.forEach(function (table) {
+                existingTables[table.identifierChain[table.identifierChain.length - 1].name] = true;
+              });
 
-            if (value.joinCols.length > 0) {
-              if (!tablesAdded && suggestJoins.prependJoin) {
-                suggestionString = '';
-                tablesAdded = true;
-              }
-              suggestionString += self.parseResult.lowerCase ? ' on ' : ' ON ';
-            }
-            if (tablesAdded) {
-              value.joinCols.forEach(function (joinColPair) {
-                if (!first) {
-                  suggestionString += self.parseResult.lowerCase ? ' and ' : ' AND ';
+              var joinRequired = false;
+              var tablesAdded = false;
+              value.tables.forEach(function (table) {
+                var tableParts = table.split('.');
+                if (!existingTables[tableParts[tableParts.length - 1]]) {
+                  tablesAdded = true;
+                  var identifier = self.convertNavOptQualifiedIdentifier(table, suggestJoins.tables);
+                  suggestionString += joinRequired ? (self.parseResult.lowerCase ? ' join ' : ' JOIN ') + identifier : identifier;
+                  joinRequired = true;
                 }
-                suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], suggestJoins.tables, self.snippet.type()) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], suggestJoins.tables, self.snippet.type());
-                first = false;
-              });
-              totalCount += value.totalQueryCount;
-              joinSuggestions.push({
-                value: suggestionString,
-                meta: HUE_I18n.autocomplete.meta.join,
-                category: suggestJoins.prependJoin ? CATEGORIES.POPULAR_JOIN : CATEGORIES.POPULAR_ACTIVE_JOIN,
-                popular: ko.observable(true),
-                details: value
               });
-            }
-          });
-          joinSuggestions.forEach(function (suggestion) {
-            suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
-            suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
-          });
+
+              if (value.joinCols.length > 0) {
+                if (!tablesAdded && suggestJoins.prependJoin) {
+                  suggestionString = '';
+                  tablesAdded = true;
+                }
+                suggestionString += self.parseResult.lowerCase ? ' on ' : ' ON ';
+              }
+              if (tablesAdded) {
+                value.joinCols.forEach(function (joinColPair) {
+                  if (!first) {
+                    suggestionString += self.parseResult.lowerCase ? ' and ' : ' AND ';
+                  }
+                  suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], suggestJoins.tables, self.snippet.type()) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], suggestJoins.tables, self.snippet.type());
+                  first = false;
+                });
+                totalCount += value.totalQueryCount;
+                joinSuggestions.push({
+                  value: suggestionString,
+                  meta: HUE_I18n.autocomplete.meta.join,
+                  category: suggestJoins.prependJoin ? CATEGORIES.POPULAR_JOIN : CATEGORIES.POPULAR_ACTIVE_JOIN,
+                  popular: ko.observable(true),
+                  details: value
+                });
+              }
+            });
+            joinSuggestions.forEach(function (suggestion) {
+              suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
+              suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
+            });
+          }
           joinsDeferred.resolve(joinSuggestions);
-        },
-        errorCallback: joinsDeferred.reject
-      }));
+        }).fail(joinsDeferred.reject));
+      }).fail(joinsDeferred.reject);
+      } else {
+        joinsDeferred.reject();
+      }
     } else {
       joinsDeferred.reject();
     }
@@ -1043,45 +1063,46 @@ var AutocompleteResults = (function () {
       initLoading(self.loadingJoinConditions, joinConditionsDeferred);
       joinConditionsDeferred.done(self.appendEntries);
 
-      self.lastKnownRequests.push(self.apiHelper.fetchNavOptPopularJoins({
-        sourceType: self.snippet.type(),
-        timeout: AUTOCOMPLETE_TIMEOUT,
-        defaultDatabase: self.activeDatabase,
-        silenceErrors: true,
-        tables: suggestJoinConditions.tables,
-        successCallback: function (data) {
+      var paths = self.tableIdentifierChainsToPaths(suggestJoinConditions.tables);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          self.cancellablePromises.push(multiTableEntry.getTopJoins({ silenceErrors: true, cancellable: true }).done(function (topJoins) {
           var joinConditionSuggestions = [];
           var totalCount = 0;
-          data.values.forEach(function (value) {
-            if (value.joinCols.length > 0) {
-              var suggestionString = suggestJoinConditions.prependOn ? (self.parseResult.lowerCase ? 'on ' : 'ON ') : '';
-              var first = true;
-              value.joinCols.forEach(function (joinColPair) {
-                if (!first) {
-                  suggestionString += self.parseResult.lowerCase ? ' and ' : ' AND ';
-                }
-                suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], suggestJoinConditions.tables) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], suggestJoinConditions.tables);
-                first = false;
-              });
-              totalCount += value.totalQueryCount;
-              joinConditionSuggestions.push({
-                value: suggestionString,
-                meta: HUE_I18n.autocomplete.meta.joinCondition,
-                category: CATEGORIES.POPULAR_JOIN_CONDITION,
-                popular: ko.observable(true),
-                details: value
-              });
-            }
-          });
-          joinConditionSuggestions.forEach(function (suggestion) {
-            suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
-            suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
-          });
+          if (topJoins.values) {
+            topJoins.values.forEach(function (value) {
+              if (value.joinCols.length > 0) {
+                var suggestionString = suggestJoinConditions.prependOn ? (self.parseResult.lowerCase ? 'on ' : 'ON ') : '';
+                var first = true;
+                value.joinCols.forEach(function (joinColPair) {
+                  if (!first) {
+                    suggestionString += self.parseResult.lowerCase ? ' and ' : ' AND ';
+                  }
+                  suggestionString += self.convertNavOptQualifiedIdentifier(joinColPair.columns[0], suggestJoinConditions.tables) + ' = ' + self.convertNavOptQualifiedIdentifier(joinColPair.columns[1], suggestJoinConditions.tables);
+                  first = false;
+                });
+                totalCount += value.totalQueryCount;
+                joinConditionSuggestions.push({
+                  value: suggestionString,
+                  meta: HUE_I18n.autocomplete.meta.joinCondition,
+                  category: CATEGORIES.POPULAR_JOIN_CONDITION,
+                  popular: ko.observable(true),
+                  details: value
+                });
+              }
+            });
+            joinConditionSuggestions.forEach(function (suggestion) {
+              suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
+              suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
+            });
+          }
 
           joinConditionsDeferred.resolve(joinConditionSuggestions);
-        },
-        errorCallback: joinConditionsDeferred.reject
-      }));
+        }).fail(joinConditionsDeferred.reject));
+        }).fail(joinConditionsDeferred.reject);
+      } else {
+        joinConditionsDeferred.reject();
+      }
     } else {
       joinConditionsDeferred.reject();
     }
@@ -1098,73 +1119,72 @@ var AutocompleteResults = (function () {
       initLoading(self.loadingAggregateFunctions, aggregateFunctionsDeferred);
       aggregateFunctionsDeferred.done(self.appendEntries);
 
-      self.lastKnownRequests.push(self.apiHelper.fetchNavOptTopAggs({
-        sourceType: self.snippet.type(),
-        timeout: AUTOCOMPLETE_TIMEOUT,
-        defaultDatabase: self.activeDatabase,
-        silenceErrors: true,
-        tables: suggestAggregateFunctions.tables,
-        successCallback: function (data) {
-          var aggregateFunctionsSuggestions = [];
-          if (data.values.length > 0) {
+      var paths = self.tableIdentifierChainsToPaths(suggestAggregateFunctions.tables);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          self.cancellablePromises.push(multiTableEntry.getTopAggs({ silenceErrors: true, cancellable: true }).done(function (topAggs) {
+            var aggregateFunctionsSuggestions = [];
+            if (topAggs.values && topAggs.values.length > 0) {
+
+              // Expand all column names to the fully qualified name including db and table.
+              topAggs.values.forEach(function (value) {
+                value.aggregateInfo.forEach(function (info) {
+                  value.aggregateClause = value.aggregateClause.replace(new RegExp('([^.])' + info.columnName, 'gi'), '$1' + info.databaseName + '.' + info.tableName + '.' + info.columnName);
+                });
+              });
 
-            // Expand all column names to the fully qualified name including db and table.
-            data.values.forEach(function (value) {
-              value.aggregateInfo.forEach(function (info) {
-                value.aggregateClause = value.aggregateClause.replace(new RegExp('([^.])' + info.columnName, 'gi'), '$1' + info.databaseName + '.' + info.tableName + '.' + info.columnName);
+              // Substitute qualified table identifiers with either alias or table when multiple tables are present or just empty string
+              var substitutions = [];
+              suggestAggregateFunctions.tables.forEach(function (table) {
+                var replaceWith = table.alias ? table.alias + '.' : (suggestAggregateFunctions.tables.length > 1 ? table.identifierChain[table.identifierChain.length - 1].name + '.' : '');
+                if (table.identifierChain.length > 1) {
+                  substitutions.push({
+                    replace: new RegExp($.map(table.identifierChain, function (identifier) {
+                      return identifier.name
+                    }).join('\.') + '\.', 'gi'),
+                    with: replaceWith
+                  })
+                } else if (table.identifierChain.length === 1) {
+                  substitutions.push({
+                    replace: new RegExp(self.activeDatabase + '\.' + table.identifierChain[0].name + '\.', 'gi'),
+                    with: replaceWith
+                  });
+                  substitutions.push({
+                    replace: new RegExp(table.identifierChain[0].name + '\.', 'gi'),
+                    with: replaceWith
+                  })
+                }
               });
-            });
 
-            // Substitute qualified table identifiers with either alias or table when multiple tables are present or just empty string
-            var substitutions = [];
-            suggestAggregateFunctions.tables.forEach(function (table) {
-              var replaceWith = table.alias ? table.alias + '.' : (suggestAggregateFunctions.tables.length > 1 ? table.identifierChain[table.identifierChain.length - 1].name + '.' : '');
-              if (table.identifierChain.length > 1) {
-                substitutions.push({
-                  replace: new RegExp($.map(table.identifierChain, function (identifier) {
-                        return identifier.name
-                      }).join('\.') + '\.', 'gi'),
-                  with: replaceWith
-                })
-              } else if (table.identifierChain.length === 1) {
-                substitutions.push({
-                  replace: new RegExp(self.activeDatabase + '\.' + table.identifierChain[0].name + '\.', 'gi'),
-                  with: replaceWith
+              var totalCount = 0;
+              topAggs.values.forEach(function (value) {
+                var clean = value.aggregateClause;
+                substitutions.forEach(function (substitution) {
+                  clean = clean.replace(substitution.replace, substitution.with);
+                });
+                totalCount += value.totalQueryCount;
+                value.function = SqlFunctions.findFunction(self.snippet.type(), value.aggregateFunction);
+                aggregateFunctionsSuggestions.push({
+                  value: clean,
+                  meta: value.function.returnTypes.join('|'),
+                  category: CATEGORIES.POPULAR_AGGREGATE,
+                  weightAdjust: Math.min(value.totalQueryCount, 99),
+                  popular: ko.observable(true),
+                  details: value
                 });
-                substitutions.push({
-                  replace: new RegExp(table.identifierChain[0].name + '\.', 'gi'),
-                  with: replaceWith
-                })
-              }
-            });
-
-            var totalCount = 0;
-            data.values.forEach(function (value) {
-              var clean = value.aggregateClause;
-              substitutions.forEach(function (substitution) {
-                clean = clean.replace(substitution.replace, substitution.with);
-              });
-              totalCount += value.totalQueryCount;
-              value.function = SqlFunctions.findFunction(self.snippet.type(), value.aggregateFunction);
-              aggregateFunctionsSuggestions.push({
-                value: clean,
-                meta: value.function.returnTypes.join('|'),
-                category: CATEGORIES.POPULAR_AGGREGATE,
-                weightAdjust: Math.min(value.totalQueryCount, 99),
-                popular: ko.observable(true),
-                details: value
               });
-            });
 
-            aggregateFunctionsSuggestions.forEach(function (suggestion) {
-              suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
-              suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
-            });
-          }
-          aggregateFunctionsDeferred.resolve(aggregateFunctionsSuggestions);
-        },
-        errorCallback: aggregateFunctionsDeferred.reject
-      }));
+              aggregateFunctionsSuggestions.forEach(function (suggestion) {
+                suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.totalQueryCount : Math.round(100 * suggestion.details.totalQueryCount / totalCount);
+                suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
+              });
+            }
+            aggregateFunctionsDeferred.resolve(aggregateFunctionsSuggestions);
+          }).fail(aggregateFunctionsDeferred.reject));
+        }).fail(aggregateFunctionsDeferred.reject);
+      } else {
+        aggregateFunctionsDeferred.reject();
+      }
     } else {
       aggregateFunctionsDeferred.reject();
     }
@@ -1281,52 +1301,53 @@ var AutocompleteResults = (function () {
       initLoading(self.loadingFilters, filtersDeferred);
       filtersDeferred.done(self.appendEntries);
 
-      self.lastKnownRequests.push(self.apiHelper.fetchNavOptTopFilters({
-        sourceType: self.snippet.type(),
-        timeout: AUTOCOMPLETE_TIMEOUT,
-        defaultDatabase: self.activeDatabase,
-        silenceErrors: true,
-        tables: suggestFilters.tables,
-        successCallback: function (data) {
-          var filterSuggestions = [];
-          var totalCount = 0;
-          data.values.forEach(function (value) {
-            if (typeof value.popularValues !== 'undefined' && value.popularValues.length > 0) {
-              value.popularValues.forEach(function (popularValue) {
-                if (typeof popularValue.group !== 'undefined') {
-                  popularValue.group.forEach(function (grp) {
-                    var compVal = suggestFilters.prefix ? (self.parseResult.lowerCase ? suggestFilters.prefix.toLowerCase() : suggestFilters.prefix) + ' ' : '';
-                    compVal += self.createNavOptIdentifier(value.tableName, grp.columnName, suggestFilters.tables);
-                    if (!/^ /.test(grp.op)) {
-                      compVal += ' ';
-                    }
-                    compVal += self.parseResult.lowerCase ? grp.op.toLowerCase() : grp.op;
-                    if (!/ $/.test(grp.op)) {
-                      compVal += ' ';
+      var paths = self.tableIdentifierChainsToPaths(suggestFilters.tables);
+      if (paths.length) {
+        DataCatalog.getMultiTableEntry({ sourceType: self.snippet.type(), paths: paths }).done(function (multiTableEntry) {
+          self.cancellablePromises.push(multiTableEntry.getTopFilters({ silenceErrors: true, cancellable: true }).done(function (topFilters) {
+            var filterSuggestions = [];
+            var totalCount = 0;
+            if (topFilters.values) {
+              topFilters.values.forEach(function (value) {
+                if (typeof value.popularValues !== 'undefined' && value.popularValues.length > 0) {
+                  value.popularValues.forEach(function (popularValue) {
+                    if (typeof popularValue.group !== 'undefined') {
+                      popularValue.group.forEach(function (grp) {
+                        var compVal = suggestFilters.prefix ? (self.parseResult.lowerCase ? suggestFilters.prefix.toLowerCase() : suggestFilters.prefix) + ' ' : '';
+                        compVal += self.createNavOptIdentifier(value.tableName, grp.columnName, suggestFilters.tables);
+                        if (!/^ /.test(grp.op)) {
+                          compVal += ' ';
+                        }
+                        compVal += self.parseResult.lowerCase ? grp.op.toLowerCase() : grp.op;
+                        if (!/ $/.test(grp.op)) {
+                          compVal += ' ';
+                        }
+                        compVal += grp.literal;
+                        totalCount += popularValue.count;
+                        filterSuggestions.push({
+                          value: compVal,
+                          meta: HUE_I18n.autocomplete.meta.filter,
+                          category: CATEGORIES.POPULAR_FILTER,
+                          popular: ko.observable(true),
+                          details: popularValue
+                        });
+                      });
                     }
-                    compVal += grp.literal;
-                    totalCount += popularValue.count;
-                    filterSuggestions.push({
-                      value: compVal,
-                      meta: HUE_I18n.autocomplete.meta.filter,
-                      category: CATEGORIES.POPULAR_FILTER,
-                      popular: ko.observable(true),
-                      details: popularValue
-                    });
                   });
                 }
               });
             }
-          });
-          filterSuggestions.forEach(function (suggestion) {
-            suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.count : Math.round(100 * suggestion.details.count / totalCount);
-            suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
-          });
+            filterSuggestions.forEach(function (suggestion) {
+              suggestion.details.relativePopularity = totalCount === 0 ? suggestion.details.count : Math.round(100 * suggestion.details.count / totalCount);
+              suggestion.weightAdjust = suggestion.details.relativePopularity + 1;
+            });
 
-          filtersDeferred.resolve(filterSuggestions);
-        },
-        errorCallback: filtersDeferred.reject
-      }));
+            filtersDeferred.resolve(filterSuggestions);
+          }).fail(filtersDeferred.reject));
+        }).fail(filtersDeferred.reject);
+      } else {
+        filtersDeferred.reject();
+      }
     } else {
       filtersDeferred.reject();
     }

+ 0 - 116
desktop/core/src/desktop/static/desktop/spec/apiHelperSpec.js

@@ -59,121 +59,5 @@
         expect(subject.successResponseIsError({ traceback: {} })).toBeTruthy();
       });
     });
-
-    describe('NavOpt', function () {
-      describe('Tables JSON generation', function () {
-        it('should add the default database when no database is found in the identifier chain', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve([]);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [{ identifierChain: [{ name: 'some_table' }] }]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["default.some_table"]');
-          });
-        });
-
-        it('should add the database from the identifier chain if found', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve(['some_db']);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [{ identifierChain: [{ name: 'some_db' }, { name: 'some_table' }] }]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["some_db.some_table"]');
-          });
-
-        });
-
-        it('should support tables with same names as databases', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve(['table_and_db_name']);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [{ identifierChain: [{ name: 'table_and_db_name' }] }]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["default.table_and_db_name"]');
-          });
-        });
-
-        it('should support tables with same names as databases', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve(['table_and_db_name']);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [{ identifierChain: [{ name: 'table_and_db_name' }, { name: 'table_and_db_name' }] }]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["table_and_db_name.table_and_db_name"]');
-          });
-        });
-
-        it('should support multiple tables some with databases some without', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve(['a_table_from_default', 'other_db']);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [{ identifierChain: [{ name: 'a_table_from_default' }] }, { identifierChain: [{ name: 'other_db' }, { name: 'a_table_from_other_db' }] }]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["default.a_table_from_default","other_db.a_table_from_other_db"]');
-          });
-        });
-
-        it('should remove duplicates', function () {
-          spyOn(subject, 'getDatabases').and.callFake(function () {
-            return $.Deferred().resolve(['sometable', 'somedb', 'default']);
-          });
-
-          var promise = subject.createNavOptDbTablesJson({
-            defaultDatabase: 'default',
-            sourceType: 'hive',
-            tables: [
-              { identifierChain: [{ name: 'someTable' }] },
-              { identifierChain: [{ name: 'someDb' }, { name: 'someTable' }] },
-              { identifierChain: [{ name: 'default' }, { name: 'someTable' }] },
-              { identifierChain: [{ name: 'someDb' }, { name: 'otherTable' }] },
-              { identifierChain: [{ name: 'someDb' }, { name: 'someTable' }] },
-              { identifierChain: [{ name: 'someTable' }] },
-              { identifierChain: [{ name: 'someDb' }, { name: 'otherTable' }] },
-              { identifierChain: [{ name: 'someDb' }, { name: 'otherTable' }] }
-            ]
-          });
-
-          expect(promise.state()).toEqual('resolved');
-          promise.done(function (result) {
-            expect(result).toEqual('["default.someTable","someDb.someTable","someDb.otherTable"]');
-          });
-        })
-      });
-    })
   });
 })();

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä