[ASTERIXDB-2932][IDX] Fixing open type building for array indexes.

- user mode changes: no
- storage format changes: no
- interface changes: no

Fix to allow for more complex nesting structures for array indexes 
(partially typed indexes + records that hold subrecords that hold 
arrays). This also fixes the open type names to be more consistent
with the closed types.

Change-Id: If6cf7d13081a6f58457ceefed5c090208fedf8a0
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/12403
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Glenn Galvizo <ggalvizo@uci.edu>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.1.ddl.sqlpp
new file mode 100644
index 0000000..c0a4d1d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.1.ddl.sqlpp
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+DROP DATAVERSE    TestDataverse IF EXISTS;
+CREATE DATAVERSE  TestDataverse;
+USE               TestDataverse;
+CREATE TYPE       TestType AS {
+  _id: uuid,
+  closed_field_1: {
+    closed_field_2: [{
+        closed_field_3: {
+            closed_field_4: [{
+                closed_field_5: bigint
+            }]
+        }
+    }]
+  }
+};
+CREATE DATASET    TestDataset (TestType)
+PRIMARY KEY       _id AUTOGENERATED;
+
+
+-- Fully open index 1. Unnest flags: [0, 1, 0]
+CREATE INDEX      testIndex1
+ON                TestDataset ( UNNEST open_field_1.open_field_2
+                                SELECT open_field_3a : bigint );
+
+-- Fully open index 2. Unnest flags: [0, 1, 0, 0]
+CREATE INDEX      testIndex2
+ON                TestDataset ( UNNEST open_field_1.open_field_2
+                                SELECT open_field_3b.open_field_4 : bigint );
+
+-- Fully open index 3. Unnest flags: [0, 1, 0, 1]
+CREATE INDEX      testIndex3
+ON                TestDataset ( UNNEST open_field_1.open_field_2
+                                UNNEST open_field_3c.open_field_4a : bigint );
+
+-- Fully open index 4. Unnest flags: [0, 1, 0, 1, 0]
+CREATE INDEX      testIndex4
+ON                TestDataset ( UNNEST open_field_1.open_field_2
+                                UNNEST open_field_3c.open_field_4b
+                                SELECT open_field_5 : bigint );
+
+
+-- Partially open index 1.  [0, 1, 0]
+CREATE INDEX      testIndex1c
+ON                TestDataset ( UNNEST closed_field_1.open_field_2
+                                SELECT open_field_3a : bigint );
+
+-- Partially open index 2. Unnest flags: [0, 1, 0, 0]
+CREATE INDEX      testIndex2c
+ON                TestDataset ( UNNEST closed_field_1.closed_field_2
+                                SELECT open_field_3b.open_field_4 : bigint );
+
+-- Partially open index 3. Unnest flags: [0, 1, 0, 1]
+CREATE INDEX      testIndex3c
+ON                TestDataset ( UNNEST closed_field_1.closed_field_2
+                                UNNEST closed_field_3.open_field_4a : bigint );
+
+-- Partially open index 4. Unnest flags: [0, 1, 0, 1, 0]
+CREATE INDEX      testIndex4c
+ON                TestDataset ( UNNEST closed_field_1.closed_field_2
+                                UNNEST closed_field_3.closed_field_4
+                                SELECT open_field_5 : bigint );
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.2.query.sqlpp
new file mode 100644
index 0000000..1b854c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/array-index/metadata/open/complex-structures/complex-structures-01.2.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+FROM      Metadata.`Index` D
+WHERE     D.IndexName LIKE "testIndex%" AND
+          D.DataverseName = "TestDataverse"
+SELECT    D.SearchKey, D.SearchKeyElements, D.SearchKeyType
+ORDER BY  D.IndexName ASC;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/array-index/metadata/open/complex-structures/complex-structures-01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/array-index/metadata/open/complex-structures/complex-structures-01.1.adm
new file mode 100644
index 0000000..7618b68
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/array-index/metadata/open/complex-structures/complex-structures-01.1.adm
@@ -0,0 +1,8 @@
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "open_field_1", "open_field_2" ] ], "ProjectList": [ [ "open_field_3a" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "closed_field_1", "open_field_2" ] ], "ProjectList": [ [ "open_field_3a" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "open_field_1", "open_field_2" ] ], "ProjectList": [ [ "open_field_3b", "open_field_4" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "closed_field_1", "closed_field_2" ] ], "ProjectList": [ [ "open_field_3b", "open_field_4" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "open_field_1", "open_field_2" ], [ "open_field_3c", "open_field_4a" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "closed_field_1", "closed_field_2" ], [ "closed_field_3", "open_field_4a" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "open_field_1", "open_field_2" ], [ "open_field_3c", "open_field_4b" ] ], "ProjectList": [ [ "open_field_5" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
+{ "SearchKey": [  ], "SearchKeyElements": [ { "UnnestList": [ [ "closed_field_1", "closed_field_2" ], [ "closed_field_3", "closed_field_4" ] ], "ProjectList": [ [ "open_field_5" ] ] } ], "SearchKeyType": [ [ "int64" ] ] }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 74dc47b..03be890 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -8066,6 +8066,11 @@
           <output-dir compare="Text">use-case-4</output-dir>
         </compilation-unit>
       </test-case>
+      <test-case FilePath="array-index/metadata/open">
+        <compilation-unit name="complex-structures">
+          <output-dir compare="Text">complex-structures</output-dir>
+        </compilation-unit>
+      </test-case>
       <!--      <test-case FilePath="array-index/metadata/closed">-->
       <!--        <compilation-unit name="with-composite-sk">-->
       <!--          <output-dir compare="Text">with-composite-sk</output-dir>-->
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
index b1d610f..8a9236e 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/TypeUtil.java
@@ -131,23 +131,26 @@
         }
 
         private IAType buildNewForOpenType() {
-            boolean isTypeWithUnnest = keyUnnestFlags.subList(indexOfOpenPart + 1, keyUnnestFlags.size()).stream()
-                    .filter(i -> i).findFirst().orElse(false);
-            IAType resultant = nestArrayType(keyFieldType, isTypeWithUnnest);
+            // Walk backwards through our flags and construct the desired type.
+            List<Boolean> unnestFlagsForOpenType = keyUnnestFlags.subList(indexOfOpenPart, keyUnnestFlags.size());
+            List<String> fieldNamesForOpenType = keyFieldNames.subList(indexOfOpenPart, keyFieldNames.size());
+            IAType resultant = keyFieldType;
+            for (int i = unnestFlagsForOpenType.size() - 1; i >= 0; i--) {
+                // Construct the type name.
+                StringBuilder recordTypeNameBuilder = new StringBuilder();
+                recordTypeNameBuilder.append(baseRecordType.getTypeName());
+                for (int j = 0; j < i + indexOfOpenPart; j++) {
+                    recordTypeNameBuilder.append("_").append(keyFieldNames.get(j));
+                    if (keyUnnestFlags.get(j)) {
+                        recordTypeNameBuilder.append("_Item");
+                    }
+                }
 
-            // Build the type (list or record) that holds the type (list or record) above.
-            resultant = nestArrayType(
-                    new ARecordType(keyFieldNames.get(keyFieldNames.size() - 2),
-                            new String[] { keyFieldNames.get(keyFieldNames.size() - 1) },
-                            new IAType[] { AUnionType.createUnknownableType(resultant) }, true),
-                    keyUnnestFlags.get(indexOfOpenPart));
-
-            // Create open part of the nested field.
-            for (int i = keyFieldNames.size() - 3; i > (indexOfOpenPart - 1); i--) {
-                resultant = nestArrayType(
-                        new ARecordType(keyFieldNames.get(i), new String[] { keyFieldNames.get(i + 1) },
-                                new IAType[] { AUnionType.createUnknownableType(resultant) }, true),
-                        keyUnnestFlags.get(i));
+                // Construct the type itself and account for any array steps.
+                resultant = nestArrayType(resultant, unnestFlagsForOpenType.get(i));
+                resultant =
+                        new ARecordType(recordTypeNameBuilder.toString(), new String[] { fieldNamesForOpenType.get(i) },
+                                new IAType[] { AUnionType.createUnknownableType(resultant) }, true);
             }
 
             // Now update the parent to include this optional field, accounting for intermediate arrays.
@@ -155,9 +158,9 @@
             ARecordType parentRecord =
                     (ARecordType) unnestArrayType(TypeComputeUtils.getActualType(gapTriple.first), gapTriple.third);
             IAType[] parentFieldTypes = ArrayUtils.addAll(parentRecord.getFieldTypes().clone(),
-                    AUnionType.createUnknownableType(resultant));
+                    ((ARecordType) resultant).getFieldTypes().clone());
             resultant = new ARecordType(bridgeNameFoundFromOpenTypeBuild,
-                    ArrayUtils.addAll(parentRecord.getFieldNames(), resultant.getTypeName()), parentFieldTypes, true);
+                    ArrayUtils.addAll(parentRecord.getFieldNames(), gapTriple.second), parentFieldTypes, true);
             resultant = keepUnknown(gapTriple.first, nestArrayType(resultant, gapTriple.third));
 
             return resultant;
@@ -227,7 +230,15 @@
         }
 
         private static IAType nestArrayType(IAType originalType, boolean isWithinArray) {
-            return (isWithinArray) ? new AOrderedListType(originalType, originalType.getTypeName()) : originalType;
+            if (isWithinArray) {
+                String newTypeName = originalType.getTypeName().endsWith("_Item")
+                        ? originalType.getTypeName().substring(0, originalType.getTypeName().length() - 5)
+                        : originalType.getTypeName();
+                return new AOrderedListType(originalType, newTypeName + "_Array");
+
+            } else {
+                return originalType;
+            }
         }
 
         private static IAType unnestArrayType(IAType originalType, boolean isWithinArray) {