浏览代码

[core] Support autocomplete for array types in hive mode

This adds autocomplete support for array items. The api url to fetch the terms is slightly different than for maps so we have to make additional calls to fetch the type before we can give the suggestions. The calls go through the assistHelper so any previous responses for the same url are cached and returned synchronously.
Johan Ahlen 10 年之前
父节点
当前提交
05e4b39

+ 38 - 23
desktop/core/src/desktop/static/desktop/js/autocomplete.js

@@ -102,13 +102,17 @@ Autocompleter.prototype.extractFields = function (data, valuePrefix, includeStar
 };
 
 Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callback) {
+  var onFailure = function() {
+    callback([]);
+  };
+
   var self = this;
 
   if (typeof self.assistHelper.activeDatabase() == "undefined"
     || self.assistHelper.activeDatabase() == null
     || self.assistHelper.activeDatabase() == ""
     || (self.currentMode !== "hive" && self.currentMode !== "impala")) {
-    callback([]);
+    onFailure();
     return;
   }
 
@@ -156,9 +160,7 @@ Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callb
         fromKeyword += " ";
       }
       callback(self.extractFields(data, fromKeyword));
-    }, function() {
-      callback([]);
-    });
+    }, onFailure );
   } else if ((selectBefore && fromAfter) || fieldTermBefore) {
     var partialTermsMatch = beforeCursor.match(/([^ \-\+\<\>\,]*)$/);
     var parts = partialTermsMatch ? partialTermsMatch[0].split(".") : [];
@@ -185,32 +187,45 @@ Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callb
       return;
     } else {
       // No table refs
-      callback([]);
+      onFailure();
       return;
     }
-    var fields = [];
-    $.each(parts, function(index, part) {
-      if (part != '' && (index > 0 || part !== tableName)) {
+
+    var getFields = function (remainingParts, fields) {
+      if (remainingParts.length == 0) {
+        self.assistHelper.fetchFields(tableName, fields, function(data) {
+          callback(self.extractFields(data, "", !fieldTermBefore));
+        }, onFailure);
+        return; // break recursion
+      }
+      var part = remainingParts.shift();
+
+      if (part != '' && part !== tableName) {
         if (self.currentMode === "hive") {
-          var mapMatch = part.match(/([^\[]*)\[[^\]]+\]$/i);
-          if (mapMatch !== null) {
-            fields.push(mapMatch[1]);
-            fields.push("value");
-          } else {
-            fields.push(part);
+          var mapOrArrayMatch = part.match(/([^\[]*)\[[^\]]*\]$/i);
+          if (mapOrArrayMatch !== null) {
+            fields.push(mapOrArrayMatch[1]);
+            self.assistHelper.fetchFields(tableName, fields, function(data) {
+              if (data.type === "map") {
+                fields.push("value");
+                getFields(remainingParts, fields);
+              } else if (data.type === "array") {
+                fields.push("item");
+                getFields(remainingParts, fields);
+              } else {
+                onFailure();
+              }
+            }, onFailure);
+            return; // break recursion, it'll be async above
           }
-        } else {
-          fields.push(part);
         }
+        fields.push(part);
       }
-    });
+      getFields(remainingParts, fields);
+    };
 
-    self.assistHelper.fetchFields(tableName, fields, function(data) {
-      callback(self.extractFields(data, "", !fieldTermBefore));
-    }, function() {
-      callback([]);
-    });
+    getFields(parts, []);
   } else {
-    callback([]);
+    onFailure();
   }
 };

+ 69 - 0
desktop/core/src/desktop/static/desktop/spec/autocompleteSpec.js

@@ -166,6 +166,9 @@ describe("autocomplete.js", function() {
     it("should suggest struct from map values", function() {
       assertAutoComplete({
         serverResponses: {
+          "/testApp/api/autocomplete/testDb/testTable/testMap" : {
+            type: "map"
+          },
           "/testApp/api/autocomplete/testDb/testTable/testMap/value" : {
             fields: [
               {"type": "string", "name": "fieldA" },
@@ -183,9 +186,32 @@ describe("autocomplete.js", function() {
       });
     });
 
+    it("should suggest struct from map values without a given key", function() {
+      assertAutoComplete({
+        serverResponses: {
+          "/testApp/api/autocomplete/testDb/testTable/testMap" : {
+            type: "map"
+          },
+          "/testApp/api/autocomplete/testDb/testTable/testMap/value" : {
+            fields: [
+              {"type": "string", "name": "fieldA" },
+              {"type": "string", "name": "fieldB" }
+            ],
+            type: "struct"
+          }
+        },
+        beforeCursor: "SELECT testMap[].",
+        afterCursor: " FROM testTable",
+        expectedSuggestions: ["fieldA", "fieldB"]
+      });
+    });
+
     it("should suggest struct from structs from map values", function() {
       assertAutoComplete({
         serverResponses: {
+          "/testApp/api/autocomplete/testDb/testTable/testMap" : {
+            type: "map"
+          },
           "/testApp/api/autocomplete/testDb/testTable/testMap/value/fieldC" : {
             fields: [
               {"type": "string", "name": "fieldC_A" },
@@ -199,6 +225,49 @@ describe("autocomplete.js", function() {
         expectedSuggestions: ["fieldC_A", "fieldC_B"]
       });
     });
+
+    it("should suggest struct from structs from arrays", function() {
+      assertAutoComplete({
+        serverResponses: {
+          "/testApp/api/autocomplete/testDb/testTable/testArray" : {
+            type: "array"
+          },
+          "/testApp/api/autocomplete/testDb/testTable/testArray/item/fieldC" : {
+            fields: [
+              {"type": "string", "name": "fieldC_A" },
+              {"type": "boolean", "name": "fieldC_B"}
+            ],
+            type: "struct"
+          }
+        },
+        beforeCursor: "SELECT testArray[1].fieldC.",
+        afterCursor: " FROM testTable",
+        expectedSuggestions: ["fieldC_A", "fieldC_B"]
+      });
+    });
+
+    it("should suggest structs from maps from arrays", function() {
+      assertAutoComplete({
+        serverResponses: {
+          "/testApp/api/autocomplete/testDb/testTable/testArray" : {
+            type: "array"
+          },
+          "/testApp/api/autocomplete/testDb/testTable/testArray/item/testMap" : {
+            type: "map"
+          },
+          "/testApp/api/autocomplete/testDb/testTable/testArray/item/testMap/value" : {
+            fields: [
+              {"type": "string", "name": "fieldA" },
+              {"type": "boolean", "name": "fieldB"}
+            ],
+            type: "struct"
+          }
+        },
+        beforeCursor: "SELECT testArray[1].testMap[\"key\"].",
+        afterCursor: " FROM testTable",
+        expectedSuggestions: ["fieldA", "fieldB"]
+      });
+    });
   });
 
   describe("impala-specific stuff", function() {