فهرست منبع

HUE-4174 [editor] Fix nested type autocompletion of arrays, maps and structs

Johan Ahlen 9 سال پیش
والد
کامیت
b0074bb

+ 144 - 70
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -68,8 +68,8 @@
 <hive>'USER'                        { return '<hive>USER'; }
 
 <hive>[.]                           { return '<hive>.'; }
-<hive>\[                            { return '<hive>['; }
-<hive>\]                            { return '<hive>]'; }
+<hive>'['                           { return '<hive>['; }
+<hive>']'                           { return '<hive>]'; }
 
 <impala>'AGGREGATE'                 { return '<impala>AGGREGATE'; }
 <impala>'ANALYTIC'                  { return '<impala>ANALYTIC'; }
@@ -109,8 +109,8 @@
 <impala>\[BROADCAST\]               { return '<impala>BROADCAST'; }
 
 <impala>[.]                         { return '<impala>.'; }
-<impala>\[                          { return '<impala>['; }
-<impala>\]                          { return '<impala>]'; }
+<impala>'['                          { return '<impala>['; }
+<impala>']'                          { return '<impala>]'; }
 
 <between>'AND'                      { this.popState(); return 'BETWEEN_AND'; }
 
@@ -737,6 +737,64 @@ LocalOrSchemaQualifiedName_EDIT
  | RegularOrBackTickedSchemaQualifiedName_EDIT RegularOrBacktickedIdentifier
  ;
 
+ColumnReferenceList
+ : ColumnReference
+ | ColumnReferenceList ',' ColumnReference
+ ;
+
+ColumnReference
+ : BasicIdentifierChain
+ | BasicIdentifierChain AnyDot '*'
+ ;
+
+ColumnReference_EDIT
+ : BasicIdentifierChain_EDIT
+ ;
+
+BasicIdentifierChain
+ : ColumnIdentifier                              -> [ $1 ]
+ | BasicIdentifierChain AnyDot ColumnIdentifier
+   {
+     $1.push($3);
+   }
+ ;
+
+// TODO: Merge with DerivedColumnChain_EDIT ( issue is starting with PartialBacktickedOrPartialCursor)
+BasicIdentifierChain_EDIT
+ : ColumnIdentifier_EDIT
+   {
+     if ($1.insideKey) {
+       suggestKeyValues({ identifierChain: [{ name: $1.name }] });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | BasicIdentifierChain AnyDot ColumnIdentifier_EDIT
+   {
+     if ($3.insideKey) {
+       suggestKeyValues({ identifierChain: $1.concat({ name: $3.name }) });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | BasicIdentifierChain AnyDot ColumnIdentifier_EDIT AnyDot BasicIdentifierChain
+ | ColumnIdentifier_EDIT AnyDot BasicIdentifierChain
+ | BasicIdentifierChain AnyDot PartialBacktickedOrPartialCursor
+   {
+     suggestColumns({
+       identifierChain: $1
+     });
+     $$ = { suggestKeywords: ['*'] };
+   }
+ | BasicIdentifierChain AnyDot PartialBacktickedOrPartialCursor AnyDot BasicIdentifierChain
+   {
+     suggestColumns({
+       identifierChain: $1
+     });
+     $$ = { suggestKeywords: ['*'] };
+   }
+ ;
+
 DerivedColumnChain
  : ColumnIdentifier  -> [ $1 ]
  | DerivedColumnChain AnyDot ColumnIdentifier
@@ -746,19 +804,61 @@ DerivedColumnChain
  ;
 
 DerivedColumnChain_EDIT
- : DerivedColumnChain AnyDot PartialBacktickedIdentifierOrPartialCursor  -> { identifierChain: $1 }
- ;
-
-PartialBacktickedIdentifierOrPartialCursor
- : PartialBacktickedIdentifier
- | 'PARTIAL_CURSOR'
+ : ColumnIdentifier_EDIT
+   {
+     if ($1.insideKey) {
+       suggestKeyValues({ identifierChain: [{ name: $1.name }] });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | DerivedColumnChain AnyDot ColumnIdentifier_EDIT
+   {
+     if ($3.insideKey) {
+       suggestKeyValues({ identifierChain: $1.concat({ name: $3.name }) });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | DerivedColumnChain AnyDot ColumnIdentifier_EDIT AnyDot DerivedColumnChain
+   {
+     if ($3.insideKey) {
+       suggestKeyValues({ identifierChain: $1.concat({ name: $3.name }) });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | ColumnIdentifier_EDIT AnyDot DerivedColumnChain
+   {
+     if ($1.insideKey) {
+       suggestKeyValues({ identifierChain: [{ name: $1.name }] });
+       suggestColumns();
+       suggestFunctions();
+     }
+   }
+ | PartialBacktickedIdentifierOrPartialCursor
+   {
+     suggestColumns();
+   }
+ | DerivedColumnChain AnyDot PartialBacktickedIdentifierOrPartialCursor
+   {
+     suggestColumns({ identifierChain: $1 });
+   }
+ | DerivedColumnChain AnyDot PartialBacktickedIdentifierOrPartialCursor AnyDot DerivedColumnChain
+   {
+     suggestColumns({ identifierChain: $1 });
+   }
+ | PartialBacktickedIdentifierOrPartialCursor AnyDot DerivedColumnChain
+   {
+     suggestColumns();
+   }
  ;
 
 ColumnIdentifier
  : RegularOrBacktickedIdentifier OptionalMapOrArrayKey
    {
      if ($2) {
-       $$ = { name: $1, key: $2.key };
+       $$ = { name: $1, keySet: true };
      } else {
        $$ = { name: $1 };
      }
@@ -766,14 +866,30 @@ ColumnIdentifier
  ;
 
 ColumnIdentifier_EDIT
- : PartialBacktickedOrCursor OptionalMapOrArrayKey
+ : RegularOrBacktickedIdentifier HiveOrImpalaLeftSquareBracket AnyCursor HiveOrImpalaRightSquareBracketOrError
+   {
+     $$ = { name: $1, insideKey: true }
+   }
+ | RegularOrBacktickedIdentifier HiveOrImpalaLeftSquareBracket ValueExpression_EDIT HiveOrImpalaRightSquareBracketOrError
+   {
+     $$ = { name: $1 }
+   }
+ ;
+
+PartialBacktickedIdentifierOrPartialCursor
+ : PartialBacktickedIdentifier
+ | 'PARTIAL_CURSOR'
  ;
 
 OptionalMapOrArrayKey
  :
- | HiveOrImpalaLeftSquareBracket DoubleQuotedValue HiveOrImpalaRightSquareBracket   -> { key: '"' + $2 + '"' }
- | HiveOrImpalaLeftSquareBracket 'UNSIGNED_INTEGER' HiveOrImpalaRightSquareBracket  -> { key: parseInt($2) }
- | HiveOrImpalaLeftSquareBracket HiveOrImpalaRightSquareBracket                     -> { key: null }
+ | HiveOrImpalaLeftSquareBracket ValueExpression HiveOrImpalaRightSquareBracket
+ | HiveOrImpalaLeftSquareBracket HiveOrImpalaRightSquareBracket
+ ;
+
+HiveOrImpalaRightSquareBracketOrError
+ : HiveOrImpalaRightSquareBracket
+ | error
  ;
 
 // TODO: Support | DECIMAL(precision, scale)  -- (Note: Available in Hive 0.13.0 and later)
@@ -1047,7 +1163,6 @@ HiveDescribeStatement_EDIT
  | '<hive>DESCRIBE' OptionalExtendedOrFormatted SchemaQualifiedTableIdentifier DerivedColumnChain_EDIT
    {
      addTablePrimary($3);
-     suggestColumns($4);
    }
  | '<hive>DESCRIBE' OptionalExtendedOrFormatted SchemaQualifiedTableIdentifier 'CURSOR'
    {
@@ -2319,6 +2434,7 @@ ApproximateNumericLiteral
 
 GeneralLiteral
  : SingleQuotedValue  -> { types: [ 'STRING' ] }
+ | DoubleQuotedValue  -> { types: [ 'STRING' ] }
  | TruthValue         -> { types: [ 'BOOLEAN' ] }
  ;
 
@@ -2332,52 +2448,6 @@ OptionalNot
  | 'NOT'
  ;
 
-ColumnReferenceList
- : ColumnReference
- | ColumnReferenceList ',' ColumnReference
- ;
-
-ColumnReference
- : BasicIdentifierChain
- | BasicIdentifierChain AnyDot '*'
- ;
-
-ColumnReference_EDIT
- : BasicIdentifierChain_EDIT
- ;
-
-BasicIdentifierChain
- : Identifier                              -> [ $1 ]
- | BasicIdentifierChain AnyDot Identifier
-   {
-     $1.push($3);
-   }
- ;
-
-BasicIdentifierChain_EDIT
- : Identifier_EDIT
- | BasicIdentifierChain AnyDot Identifier_EDIT
- | BasicIdentifierChain AnyDot PartialBacktickedOrPartialCursor
-   {
-     suggestColumns({
-       identifierChain: $1
-     });
-     $$ = { suggestKeywords: ['*'] };
-   }
- ;
-
-Identifier
- : ColumnIdentifier
- | DoubleQuotedValue  -> { name: $1 }
- ;
-
-Identifier_EDIT
- : ColumnIdentifier 'PARTIAL_CURSOR'
-   {
-     suggestColumns();
-   }
- ;
-
 SelectSubList
  : ValueExpression OptionalCorrelationName
    {
@@ -2902,10 +2972,6 @@ UserDefinedTableGeneratingFunction_EDIT
    {
      suggestColumns($2);
    }
- | '<hive>EXPLODE(' PartialBacktickedOrPartialCursor error
-   {
-     suggestColumns();
-   }
  | '<hive>POSEXPLODE(' PartialBacktickedOrPartialCursor error
    {
      suggestColumns();
@@ -4047,6 +4113,7 @@ var applyArgumentTypesToSuggestions = function (funcToken, position) {
   var foundArguments = parser.yy.sqlFunctions.getArgumentTypes(parser.yy.activeDialect, funcName, position);
   if (foundArguments.length == 0 && parser.yy.result.suggestColumns) {
     delete parser.yy.result.suggestColumns;
+    delete parser.yy.result.suggestKeyValues;
     delete parser.yy.result.suggestValues;
     delete parser.yy.result.suggestFunctions;
     delete parser.yy.result.suggestIdentifiers;
@@ -4115,11 +4182,11 @@ var prioritizeSuggestions = function () {
  *
  * with an identifierChain from the select list:
  *
- * [ { name: 'm', key: 'foo' }, { name: 'bar' } ]
+ * [ { name: 'm', keySet: true }, { name: 'bar' } ]
  *
  * Calling this would return an expanded identifierChain, given the above it would be:
  *
- * [ { name: 't' }, { name: 'someMap', key: 'foo' }, { name: 'bar' } ]
+ * [ { name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' } ]
  */
 parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain) {
   if (typeof identifierChain === 'undefined' || identifierChain.length === 0) {
@@ -4134,11 +4201,11 @@ parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain)
   if (foundPrimary.length === 1) {
     var firstPart = foundPrimary[0].identifierChain.concat();
     var secondPart = identifierChain.slice(1);
-    if (typeof identifierChain[0].key !== 'undefined') {
+    if (typeof identifierChain[0].keySet !== 'undefined') {
       var lastFromFirst = firstPart.pop();
       firstPart.push({
         name: lastFromFirst.name,
-        key: identifierChain[0].key
+        keySet: identifierChain[0].keySet
       });
     }
     return firstPart.concat(secondPart);
@@ -4316,6 +4383,9 @@ var linkTablePrimaries = function () {
    if (typeof parser.yy.result.colRef !== 'undefined') {
      linkSuggestion(parser.yy.result.colRef, false);
    }
+   if (typeof parser.yy.result.suggestKeyValues !== 'undefined') {
+     linkSuggestion(parser.yy.result.suggestKeyValues, true);
+   }
 }
 
 var addTablePrimary = function (ref) {
@@ -4395,6 +4465,10 @@ var suggestColumns = function (details) {
   parser.yy.result.suggestColumns = details;
 }
 
+var suggestKeyValues = function (details) {
+  parser.yy.result.suggestKeyValues = details || {};
+}
+
 var suggestTables = function (details) {
   parser.yy.result.suggestTables = details || {};
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js


+ 100 - 89
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -67,22 +67,13 @@
     var colRef = null;
 
     if (parseResult.colRef) {
-      self.snippet.getApiHelper().fetchFields({
-        sourceType: self.snippet.type(),
-        databaseName: parseResult.colRef.database || database,
-        tableName: parseResult.colRef.table,
-        fields: $.map(parseResult.colRef.identifierChain, function (value) {
-          return value.name
-        }),
-        editor: editor,
-        timeout: self.timeout,
-        successCallback: function (data) {
-          colRef = data;
-          colRefDeferral.resolve();
-        },
-        silenceErrors: true,
-        errorCallback: colRefDeferral.resolve
-      });
+      var colRefCallback = function (data) {
+        colRef = data;
+        colRefDeferral.resolve();
+      };
+
+      self.fetchFieldsForIdentifiers(editor, parseResult.colRef.table, parseResult.colRef.database || database, parseResult.colRef.identifierChain, colRefCallback, colRefDeferral.resolve);
+
     } else {
       colRefDeferral.resolve();
     }
@@ -186,6 +177,44 @@
     });
   };
 
+  SqlAutocompleter2.prototype.fetchFieldsForIdentifiers = function (editor, tableName, databaseName, identifierChain, callback, errorCallback, fetchedFields) {
+    var self = this;
+    if (!fetchedFields) {
+      fetchedFields = [];
+    }
+    if (!identifierChain) {
+      identifierChain = [];
+    }
+    if (identifierChain.length > 0) {
+      fetchedFields.push(identifierChain[0].name);
+      identifierChain = identifierChain.slice(1);
+    }
+
+    self.snippet.getApiHelper().fetchFields({
+      sourceType: self.snippet.type(),
+      databaseName: databaseName,
+      tableName: tableName,
+      fields: fetchedFields,
+      editor: editor,
+      timeout: self.timeout,
+      successCallback: function (data) {
+        if (identifierChain.length > 0) {
+          if (data.type === 'array') {
+            fetchedFields.push('item')
+          }
+          if (data.type === 'map') {
+            fetchedFields.push('value')
+          }
+          self.fetchFieldsForIdentifiers(editor, tableName, databaseName, identifierChain, callback, errorCallback, fetchedFields)
+        } else {
+          callback(data);
+        }
+      },
+      silenceErrors: true,
+      errorCallback: errorCallback
+    });
+  };
+
   SqlAutocompleter2.prototype.addTables = function (parseResult, editor, database, completions) {
     var self = this;
     var tableDeferred = $.Deferred();
@@ -213,86 +242,68 @@
       timeout: self.timeout
     });
     return tableDeferred;
-  }
+  };
 
   SqlAutocompleter2.prototype.addColumns = function (parseResult, editor, database, types, completions) {
     var self = this;
     var addColumnsDeferred = $.Deferred();
-    var fields = [];
-    if (parseResult.suggestColumns.identifierChain) {
-      parseResult.suggestColumns.identifierChain.forEach(function (identifier) {
-        var field = identifier.name;
-        if (identifier.key) {
-          field += '[' + identifier.key + ']';
-        }
-        fields.push(field);
-      });
-    }
 
-    self.snippet.getApiHelper().fetchFields({
-      sourceType: self.snippet.type(),
-      databaseName: parseResult.suggestColumns.database || database,
-      tableName: parseResult.suggestColumns.table,
-      fields: fields,
-      editor: editor,
-      timeout: self.timeout,
-      successCallback: function (data) {
-        if (data.extended_columns) {
-          data.extended_columns.forEach(function (column) {
-            if (column.type.indexOf('map') === 0 && self.snippet.type() === 'hive') {
-              completions.push({value: column.name + '[]', meta: 'map', type: 'column'})
-            } else if (column.type.indexOf('map') === 0) {
-              completions.push({value: column.name, meta: 'map', type: 'column'})
-            } else if (column.type.indexOf('struct') === 0) {
-              completions.push({value: column.name, meta: 'struct', type: 'column'})
-            } else if (column.type.indexOf('array') === 0 && self.snippet.type() === 'hive') {
-              completions.push({value: column.name + '[]', meta: 'array', type: 'column'})
-            } else if (column.type.indexOf('array') === 0) {
-              completions.push({value: column.name, meta: 'array', type: 'column'})
-            } else if (sqlFunctions.matchesType(self.snippet.type(), types, [column.type.toUpperCase()]) ||
-                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
-              completions.push({value: column.name, meta: column.type, type: 'column'})
-            }
-          });
-        } else if (data.columns) {
-          data.columns.forEach(function (column) {
-            completions.push({value: column, meta: 'column', type: 'column'})
-          });
-        }
-        if (data.type === 'map' && self.snippet.type() === 'impala') {
-          completions.push({value: 'key', meta: 'key', type: 'column'});
-          completions.push({value: 'value', meta: 'value', type: 'column'});
-        }
-        if (data.type === 'struct') {
-          data.fields.forEach(function (field) {
-            completions.push({value: field.name, meta: 'struct', type: 'column'})
-          });
-        } else if (data.type === 'map' && (data.value && data.value.fields)) {
-          data.value.fields.forEach(function (field) {
-            if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
-                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
-              completions.push({value: field.name, meta: field.type, type: 'column'});
-            }
-          });
-        } else if (data.type === 'array' && (data.item && data.item.fields)) {
-          data.item.fields.forEach(function (field) {
-            if ((field.type === 'array' || field.type === 'map')) {
-              if (self.snippet.type() === 'hive') {
-                completions.push({value: field.name + '[]', meta: field.type, type: 'column'});
-              } else {
-                completions.push({value: field.name, meta: field.type, type: 'column'});
-              }
-            } else if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
-                sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+    var callback = function (data) {
+      if (data.extended_columns) {
+        data.extended_columns.forEach(function (column) {
+          if (column.type.indexOf('map') === 0 && self.snippet.type() === 'hive') {
+            completions.push({value: column.name + '[]', meta: 'map', type: 'column'})
+          } else if (column.type.indexOf('map') === 0) {
+            completions.push({value: column.name, meta: 'map', type: 'column'})
+          } else if (column.type.indexOf('struct') === 0) {
+            completions.push({value: column.name, meta: 'struct', type: 'column'})
+          } else if (column.type.indexOf('array') === 0 && self.snippet.type() === 'hive') {
+            completions.push({value: column.name + '[]', meta: 'array', type: 'column'})
+          } else if (column.type.indexOf('array') === 0) {
+            completions.push({value: column.name, meta: 'array', type: 'column'})
+          } else if (sqlFunctions.matchesType(self.snippet.type(), types, [column.type.toUpperCase()]) ||
+              sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+            completions.push({value: column.name, meta: column.type, type: 'column'})
+          }
+        });
+      } else if (data.columns) {
+        data.columns.forEach(function (column) {
+          completions.push({value: column, meta: 'column', type: 'column'})
+        });
+      }
+      if (data.type === 'map' && self.snippet.type() === 'impala') {
+        completions.push({value: 'key', meta: 'key', type: 'column'});
+        completions.push({value: 'value', meta: 'value', type: 'column'});
+      }
+      if (data.type === 'struct') {
+        data.fields.forEach(function (field) {
+          completions.push({value: field.name, meta: 'struct', type: 'column'})
+        });
+      } else if (data.type === 'map' && (data.value && data.value.fields)) {
+        data.value.fields.forEach(function (field) {
+          if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
+              sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+            completions.push({value: field.name, meta: field.type, type: 'column'});
+          }
+        });
+      } else if (data.type === 'array' && (data.item && data.item.fields)) {
+        data.item.fields.forEach(function (field) {
+          if ((field.type === 'array' || field.type === 'map')) {
+            if (self.snippet.type() === 'hive') {
+              completions.push({value: field.name + '[]', meta: field.type, type: 'column'});
+            } else {
               completions.push({value: field.name, meta: field.type, type: 'column'});
             }
-          });
-        }
-        addColumnsDeferred.resolve();
-      },
-      silenceErrors: true,
-      errorCallback: addColumnsDeferred.resolve
-    });
+          } else if (sqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
+              sqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
+            completions.push({value: field.name, meta: field.type, type: 'column'});
+          }
+        });
+      }
+      addColumnsDeferred.resolve();
+    };
+
+    self.fetchFieldsForIdentifiers(editor, parseResult.suggestColumns.table, parseResult.suggestColumns.database || database, parseResult.suggestColumns.identifierChain, callback, addColumnsDeferred.resolve);
 
     return addColumnsDeferred;
   };

+ 4 - 4
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpec.js

@@ -199,9 +199,9 @@ define([
 
       it('should expand 3', function () {
         var tablePrimaries = [{ identifierChain: [{ name: 'testTable' }] }];
-        var identifierChain = [{ name: 'testMap', key: 'anyKey' }];
+        var identifierChain = [{ name: 'testMap', keySet: true }];
         var result = sql.expandLateralViews(tablePrimaries, identifierChain);
-        expect(result).toEqual([{ name: 'testMap', key: 'anyKey' }]);
+        expect(result).toEqual([{ name: 'testMap', keySet: true }]);
       });
 
       it('should expand 4', function () {
@@ -341,11 +341,11 @@ define([
           { alias: 'm', identifierChain: [{ name: 't' }, { name: 'someMap' }] }
         ];
 
-        var identifierChain = [{ name: 'm', key: 'foo' }, { name: 'bar' }];
+        var identifierChain = [{ name: 'm', keySet: true }, { name: 'bar' }];
 
         var actual = sql.expandImpalaIdentifierChain(tablePrimaries, identifierChain);
 
-        expect(actual).toEqual([{ name: 't' }, { name: 'someMap', key: 'foo' }, { name: 'bar' }]);
+        expect(actual).toEqual([{ name: 't' }, { name: 'someMap', keySet: true }, { name: 'bar' }]);
       });
 
       it('should expand without map reference', function () {

+ 49 - 14
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js

@@ -1773,32 +1773,67 @@ define([
         });
       });
 
-      it('should suggest columns for "SELECT testMap[\"anyKey\"].| FROM testTable"', function() {
+      it('should suggest columns for "SELECT testMap[].| FROM testTable"', function() {
         assertAutoComplete({
-          beforeCursor: 'SELECT testMap[\"anyKey\"].',
+          beforeCursor: 'SELECT testMap[].',
           afterCursor: ' FROM testTable',
           dialect: 'hive',
           expectedResult: {
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testMap', key: '\"anyKey\"' }]
+              identifierChain: [{ name: 'testMap', keySet: true }]
             },
             suggestKeywords: ['*']
           }
         });
       });
 
-      it('should suggest columns for "SELECT testMap[].| FROM testTable"', function() {
+      it('should suggest values for "SELECT testMap[|] FROM testTable"', function() {
         assertAutoComplete({
-          beforeCursor: 'SELECT testMap[].',
+          beforeCursor: 'SELECT testMap[',
+          afterCursor: '] FROM testTable',
+          dialect: 'hive',
+          expectedResult: {
+            lowerCase: false,
+            suggestFunctions: { }, // TODO: types: ['COLREF_KEY']
+            suggestColumns : {
+              table: 'testTable'
+            },
+            suggestKeyValues: {
+              identifierChain: [{ name: 'testMap' }],
+              table: 'testTable'
+            }
+          }
+        });
+      });
+
+      it('should suggest columns for "SELECT testMap[\'anyKey\'].| FROM testTable"', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT testMap[\'anyKey\'].',
+          afterCursor: ' FROM testTable',
+          dialect: 'hive',
+          expectedResult: {
+            lowerCase: false,
+            suggestColumns : {
+              table: 'testTable',
+              identifierChain: [{ name: 'testMap', keySet: true }]
+            },
+            suggestKeywords: ['*']
+          }
+        });
+      });
+
+      it('should suggest columns for "SELECT testMap[substr(\'bla\', 1)].| FROM testTable"', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT testMap[substr(\'bla\', 1)].',
           afterCursor: ' FROM testTable',
           dialect: 'hive',
           expectedResult: {
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testMap', key: null }]
+              identifierChain: [{ name: 'testMap', keySet: true }]
             },
             suggestKeywords: ['*']
           }
@@ -1814,7 +1849,7 @@ define([
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testMap', key: '\"anyKey\"' }, { name: 'fieldC' }]
+              identifierChain: [{ name: 'testMap', keySet: true }, { name: 'fieldC' }]
             },
             suggestKeywords: ['*']
           }
@@ -1830,30 +1865,30 @@ define([
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testArray', key: 1 }, { name: 'fieldC' }]
+              identifierChain: [{ name: 'testArray', keySet: true }, { name: 'fieldC' }]
             },
             suggestKeywords: ['*']
           }
         });
       });
 
-      it('should suggest columns for "SELECT testArray[1].testMap[\"key\"].| FROM testTable"', function() {
+      it('should suggest columns for "SELECT testFoo[1].testBar[\"key\"].| FROM testTable"', function() {
         assertAutoComplete({
-          beforeCursor: 'SELECT testArray[1].testMap[\"key\"].',
+          beforeCursor: 'SELECT testFoo[1].testBar[\"key\"].',
           afterCursor: ' FROM testTable',
           dialect: 'hive',
           expectedResult: {
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testArray', key: 1 }, { name: 'testMap', key: '\"key\"' }]
+              identifierChain: [{ name: 'testFoo', keySet: true }, { name: 'testBar', keySet: true }]
             },
             suggestKeywords: ['*']
           }
         });
       });
 
-      it('should suggest identifiers for "SELECT * FROM testTable WHERE testMap[].|"', function() {
+      it('should suggest columns for "SELECT * FROM testTable WHERE testMap[].|"', function() {
         assertAutoComplete({
           beforeCursor: 'SELECT * FROM testTable WHERE testMap[].',
           afterCursor: '',
@@ -1862,7 +1897,7 @@ define([
             lowerCase: false,
             suggestColumns : {
               table: 'testTable',
-              identifierChain: [{ name: 'testMap', key: null }]
+              identifierChain: [{ name: 'testMap', keySet: true }]
             }
           }
         });
@@ -2175,7 +2210,7 @@ define([
             lowerCase: false,
             suggestColumns: {
               table: 'testTable',
-              identifierChain: [{ name: 'testMap',  key: '\"anyKey\"' }]
+              identifierChain: [{ name: 'testMap',  keySet: true }]
             },
             suggestKeywords: ['*'] // TODO: Verify that this is true
           }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است