| 
28 | 28 | import org.elasticsearch.xcontent.XContentBuilder;  | 
29 | 29 | import org.elasticsearch.xcontent.XContentType;  | 
30 | 30 | import org.elasticsearch.xcontent.json.JsonXContent;  | 
 | 31 | +import org.elasticsearch.xpack.esql.action.EsqlCapabilities;  | 
31 | 32 | import org.hamcrest.Matcher;  | 
32 | 33 | import org.junit.Before;  | 
33 | 34 | 
 
  | 
@@ -1107,6 +1108,323 @@ public void testTypeConflictInObject() throws IOException {  | 
1107 | 1108 |         );  | 
1108 | 1109 |     }  | 
1109 | 1110 | 
 
  | 
 | 1111 | +    /**  | 
 | 1112 | +     * Test for https://github.com/elastic/elasticsearch/issues/117054 fix  | 
 | 1113 | +     */  | 
 | 1114 | +    public void testOneNestedSubField_AndSameNameSupportedField() throws IOException {  | 
 | 1115 | +        assumeIndexResolverNestedFieldsNameClashFixed();  | 
 | 1116 | +        ESRestTestCase.createIndex("test", Settings.EMPTY, """  | 
 | 1117 | +            "properties": {  | 
 | 1118 | +              "Responses": {  | 
 | 1119 | +                "properties": {  | 
 | 1120 | +                  "process": {  | 
 | 1121 | +                    "type": "nested",  | 
 | 1122 | +                    "properties": {  | 
 | 1123 | +                      "pid": {  | 
 | 1124 | +                        "type": "long"  | 
 | 1125 | +                      }  | 
 | 1126 | +                    }  | 
 | 1127 | +                  }  | 
 | 1128 | +                }  | 
 | 1129 | +              },  | 
 | 1130 | +              "process": {  | 
 | 1131 | +                "properties": {  | 
 | 1132 | +                  "parent": {  | 
 | 1133 | +                    "properties": {  | 
 | 1134 | +                      "command_line": {  | 
 | 1135 | +                        "type": "wildcard",  | 
 | 1136 | +                        "fields": {  | 
 | 1137 | +                          "text": {  | 
 | 1138 | +                            "type": "text"  | 
 | 1139 | +                          }  | 
 | 1140 | +                        }  | 
 | 1141 | +                      }  | 
 | 1142 | +                    }  | 
 | 1143 | +                  }  | 
 | 1144 | +                }  | 
 | 1145 | +              }  | 
 | 1146 | +            }  | 
 | 1147 | +            """);  | 
 | 1148 | + | 
 | 1149 | +        Map<String, Object> result = runEsql("FROM test");  | 
 | 1150 | +        assertMap(  | 
 | 1151 | +            result,  | 
 | 1152 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1153 | +                "columns",  | 
 | 1154 | +                List.of(columnInfo("process.parent.command_line", "keyword"), columnInfo("process.parent.command_line.text", "text"))  | 
 | 1155 | +            ).entry("values", Collections.EMPTY_LIST)  | 
 | 1156 | +        );  | 
 | 1157 | + | 
 | 1158 | +        index("test", """  | 
 | 1159 | +            {"Responses.process.pid": 123,"process.parent.command_line":"run.bat"}""");  | 
 | 1160 | + | 
 | 1161 | +        result = runEsql("FROM test");  | 
 | 1162 | +        assertMap(  | 
 | 1163 | +            result,  | 
 | 1164 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1165 | +                "columns",  | 
 | 1166 | +                List.of(columnInfo("process.parent.command_line", "keyword"), columnInfo("process.parent.command_line.text", "text"))  | 
 | 1167 | +            ).entry("values", List.of(matchesList().item("run.bat").item("run.bat")))  | 
 | 1168 | +        );  | 
 | 1169 | + | 
 | 1170 | +        result = runEsql("""  | 
 | 1171 | +            FROM test | where process.parent.command_line == "run.bat"  | 
 | 1172 | +            """);  | 
 | 1173 | +        assertMap(  | 
 | 1174 | +            result,  | 
 | 1175 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1176 | +                "columns",  | 
 | 1177 | +                List.of(columnInfo("process.parent.command_line", "keyword"), columnInfo("process.parent.command_line.text", "text"))  | 
 | 1178 | +            ).entry("values", List.of(matchesList().item("run.bat").item("run.bat")))  | 
 | 1179 | +        );  | 
 | 1180 | + | 
 | 1181 | +        ResponseException e = expectThrows(ResponseException.class, () -> runEsql("FROM test | SORT Responses.process.pid"));  | 
 | 1182 | +        String err = EntityUtils.toString(e.getResponse().getEntity());  | 
 | 1183 | +        assertThat(err, containsString("line 1:18: Unknown column [Responses.process.pid]"));  | 
 | 1184 | + | 
 | 1185 | +        e = expectThrows(ResponseException.class, () -> runEsql("""  | 
 | 1186 | +            FROM test  | 
 | 1187 | +            | SORT Responses.process.pid  | 
 | 1188 | +            | WHERE Responses.process IS NULL  | 
 | 1189 | +            """));  | 
 | 1190 | +        err = EntityUtils.toString(e.getResponse().getEntity());  | 
 | 1191 | +        assertThat(err, containsString("line 2:8: Unknown column [Responses.process.pid]"));  | 
 | 1192 | +    }  | 
 | 1193 | + | 
 | 1194 | +    public void testOneNestedSubField_AndSameNameSupportedField_TwoIndices() throws IOException {  | 
 | 1195 | +        assumeIndexResolverNestedFieldsNameClashFixed();  | 
 | 1196 | +        ESRestTestCase.createIndex("test1", Settings.EMPTY, """  | 
 | 1197 | +                  "properties": {  | 
 | 1198 | +                    "Responses": {  | 
 | 1199 | +                      "properties": {  | 
 | 1200 | +                        "process": {  | 
 | 1201 | +                          "type": "nested",  | 
 | 1202 | +                          "properties": {  | 
 | 1203 | +                            "pid": {  | 
 | 1204 | +                              "type": "long"  | 
 | 1205 | +                            }  | 
 | 1206 | +                          }  | 
 | 1207 | +                        }  | 
 | 1208 | +                      }  | 
 | 1209 | +                    }  | 
 | 1210 | +                  }  | 
 | 1211 | +            """);  | 
 | 1212 | +        ESRestTestCase.createIndex("test2", Settings.EMPTY, """  | 
 | 1213 | +                  "properties": {  | 
 | 1214 | +                    "process": {  | 
 | 1215 | +                      "properties": {  | 
 | 1216 | +                        "parent": {  | 
 | 1217 | +                          "properties": {  | 
 | 1218 | +                            "command_line": {  | 
 | 1219 | +                              "type": "wildcard",  | 
 | 1220 | +                              "fields": {  | 
 | 1221 | +                                "text": {  | 
 | 1222 | +                                  "type": "text"  | 
 | 1223 | +                                }  | 
 | 1224 | +                              }  | 
 | 1225 | +                            }  | 
 | 1226 | +                          }  | 
 | 1227 | +                        }  | 
 | 1228 | +                      }  | 
 | 1229 | +                    }  | 
 | 1230 | +                  }  | 
 | 1231 | +            """);  | 
 | 1232 | +        index("test1", """  | 
 | 1233 | +            {"Responses.process.pid": 123}""");  | 
 | 1234 | +        index("test2", """  | 
 | 1235 | +            {"process.parent.command_line":"run.bat"}""");  | 
 | 1236 | + | 
 | 1237 | +        Map<String, Object> result = runEsql("FROM test* | SORT process.parent.command_line ASC NULLS FIRST");  | 
 | 1238 | +        assertMap(  | 
 | 1239 | +            result,  | 
 | 1240 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1241 | +                "columns",  | 
 | 1242 | +                List.of(columnInfo("process.parent.command_line", "keyword"), columnInfo("process.parent.command_line.text", "text"))  | 
 | 1243 | +            ).entry("values", List.of(matchesList().item(null).item(null), matchesList().item("run.bat").item("run.bat")))  | 
 | 1244 | +        );  | 
 | 1245 | + | 
 | 1246 | +        result = runEsql("""  | 
 | 1247 | +            FROM test* | where process.parent.command_line == "run.bat"  | 
 | 1248 | +            """);  | 
 | 1249 | +        assertMap(  | 
 | 1250 | +            result,  | 
 | 1251 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1252 | +                "columns",  | 
 | 1253 | +                List.of(columnInfo("process.parent.command_line", "keyword"), columnInfo("process.parent.command_line.text", "text"))  | 
 | 1254 | +            ).entry("values", List.of(matchesList().item("run.bat").item("run.bat")))  | 
 | 1255 | +        );  | 
 | 1256 | + | 
 | 1257 | +        ResponseException e = expectThrows(ResponseException.class, () -> runEsql("FROM test* | SORT Responses.process.pid"));  | 
 | 1258 | +        String err = EntityUtils.toString(e.getResponse().getEntity());  | 
 | 1259 | +        assertThat(err, containsString("line 1:19: Unknown column [Responses.process.pid]"));  | 
 | 1260 | + | 
 | 1261 | +        e = expectThrows(ResponseException.class, () -> runEsql("""  | 
 | 1262 | +            FROM test*  | 
 | 1263 | +            | SORT Responses.process.pid  | 
 | 1264 | +            | WHERE Responses.process IS NULL  | 
 | 1265 | +            """));  | 
 | 1266 | +        err = EntityUtils.toString(e.getResponse().getEntity());  | 
 | 1267 | +        assertThat(err, containsString("line 2:8: Unknown column [Responses.process.pid]"));  | 
 | 1268 | +    }  | 
 | 1269 | + | 
 | 1270 | +    public void testOneNestedField_AndSameNameSupportedField_TwoIndices() throws IOException {  | 
 | 1271 | +        assumeIndexResolverNestedFieldsNameClashFixed();  | 
 | 1272 | +        ESRestTestCase.createIndex("test1", Settings.EMPTY, """  | 
 | 1273 | +            "properties": {  | 
 | 1274 | +              "Responses": {  | 
 | 1275 | +                "properties": {  | 
 | 1276 | +                  "process": {  | 
 | 1277 | +                    "type": "nested",  | 
 | 1278 | +                    "properties": {  | 
 | 1279 | +                      "pid": {  | 
 | 1280 | +                        "type": "long"  | 
 | 1281 | +                      }  | 
 | 1282 | +                    }  | 
 | 1283 | +                  }  | 
 | 1284 | +                }  | 
 | 1285 | +              },  | 
 | 1286 | +              "process": {  | 
 | 1287 | +                "properties": {  | 
 | 1288 | +                  "parent": {  | 
 | 1289 | +                    "properties": {  | 
 | 1290 | +                      "command_line": {  | 
 | 1291 | +                        "type": "wildcard",  | 
 | 1292 | +                        "fields": {  | 
 | 1293 | +                          "text": {  | 
 | 1294 | +                            "type": "text"  | 
 | 1295 | +                          }  | 
 | 1296 | +                        }  | 
 | 1297 | +                      }  | 
 | 1298 | +                    }  | 
 | 1299 | +                  }  | 
 | 1300 | +                }  | 
 | 1301 | +              }  | 
 | 1302 | +            }  | 
 | 1303 | +            """);  | 
 | 1304 | +        ESRestTestCase.createIndex("test2", Settings.EMPTY, """  | 
 | 1305 | +            "properties": {  | 
 | 1306 | +              "Responses": {  | 
 | 1307 | +                "properties": {  | 
 | 1308 | +                  "process": {  | 
 | 1309 | +                    "type": "integer",  | 
 | 1310 | +                    "fields": {  | 
 | 1311 | +                      "pid": {  | 
 | 1312 | +                        "type": "long"  | 
 | 1313 | +                      }  | 
 | 1314 | +                    }  | 
 | 1315 | +                  }  | 
 | 1316 | +                }  | 
 | 1317 | +              },  | 
 | 1318 | +              "process": {  | 
 | 1319 | +                "properties": {  | 
 | 1320 | +                  "parent": {  | 
 | 1321 | +                    "properties": {  | 
 | 1322 | +                      "command_line": {  | 
 | 1323 | +                        "type": "wildcard",  | 
 | 1324 | +                        "fields": {  | 
 | 1325 | +                          "text": {  | 
 | 1326 | +                            "type": "text"  | 
 | 1327 | +                          }  | 
 | 1328 | +                        }  | 
 | 1329 | +                      }  | 
 | 1330 | +                    }  | 
 | 1331 | +                  }  | 
 | 1332 | +                }  | 
 | 1333 | +              }  | 
 | 1334 | +            }  | 
 | 1335 | +            """);  | 
 | 1336 | +        index("test1", """  | 
 | 1337 | +            {"Responses.process.pid": 111,"process.parent.command_line":"run1.bat"}""");  | 
 | 1338 | +        index("test2", """  | 
 | 1339 | +            {"Responses.process": 222,"process.parent.command_line":"run2.bat"}""");  | 
 | 1340 | + | 
 | 1341 | +        Map<String, Object> result = runEsql("FROM test* | SORT process.parent.command_line");  | 
 | 1342 | +        assertMap(  | 
 | 1343 | +            result,  | 
 | 1344 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1345 | +                "columns",  | 
 | 1346 | +                List.of(  | 
 | 1347 | +                    columnInfo("Responses.process", "integer"),  | 
 | 1348 | +                    columnInfo("Responses.process.pid", "long"),  | 
 | 1349 | +                    columnInfo("process.parent.command_line", "keyword"),  | 
 | 1350 | +                    columnInfo("process.parent.command_line.text", "text")  | 
 | 1351 | +                )  | 
 | 1352 | +            )  | 
 | 1353 | +                .entry(  | 
 | 1354 | +                    "values",  | 
 | 1355 | +                    List.of(  | 
 | 1356 | +                        matchesList().item(null).item(null).item("run1.bat").item("run1.bat"),  | 
 | 1357 | +                        matchesList().item(222).item(222).item("run2.bat").item("run2.bat")  | 
 | 1358 | +                    )  | 
 | 1359 | +                )  | 
 | 1360 | +        );  | 
 | 1361 | + | 
 | 1362 | +        result = runEsql("""  | 
 | 1363 | +            FROM test* | where Responses.process.pid == 111  | 
 | 1364 | +            """);  | 
 | 1365 | +        assertMap(  | 
 | 1366 | +            result,  | 
 | 1367 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1368 | +                "columns",  | 
 | 1369 | +                List.of(  | 
 | 1370 | +                    columnInfo("Responses.process", "integer"),  | 
 | 1371 | +                    columnInfo("Responses.process.pid", "long"),  | 
 | 1372 | +                    columnInfo("process.parent.command_line", "keyword"),  | 
 | 1373 | +                    columnInfo("process.parent.command_line.text", "text")  | 
 | 1374 | +                )  | 
 | 1375 | +            ).entry("values", List.of())  | 
 | 1376 | +        );  | 
 | 1377 | + | 
 | 1378 | +        result = runEsql("FROM test* | SORT process.parent.command_line");  | 
 | 1379 | +        assertMap(  | 
 | 1380 | +            result,  | 
 | 1381 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1382 | +                "columns",  | 
 | 1383 | +                List.of(  | 
 | 1384 | +                    columnInfo("Responses.process", "integer"),  | 
 | 1385 | +                    columnInfo("Responses.process.pid", "long"),  | 
 | 1386 | +                    columnInfo("process.parent.command_line", "keyword"),  | 
 | 1387 | +                    columnInfo("process.parent.command_line.text", "text")  | 
 | 1388 | +                )  | 
 | 1389 | +            )  | 
 | 1390 | +                .entry(  | 
 | 1391 | +                    "values",  | 
 | 1392 | +                    List.of(  | 
 | 1393 | +                        matchesList().item(null).item(null).item("run1.bat").item("run1.bat"),  | 
 | 1394 | +                        matchesList().item(222).item(222).item("run2.bat").item("run2.bat")  | 
 | 1395 | +                    )  | 
 | 1396 | +                )  | 
 | 1397 | +        );  | 
 | 1398 | + | 
 | 1399 | +        result = runEsql("""  | 
 | 1400 | +            FROM test*  | 
 | 1401 | +            | SORT process.parent.command_line  | 
 | 1402 | +            | WHERE Responses.process IS NULL  | 
 | 1403 | +            """);  | 
 | 1404 | +        assertMap(  | 
 | 1405 | +            result,  | 
 | 1406 | +            matchesMapWithOptionalTook(result.get("took")).entry(  | 
 | 1407 | +                "columns",  | 
 | 1408 | +                List.of(  | 
 | 1409 | +                    columnInfo("Responses.process", "integer"),  | 
 | 1410 | +                    columnInfo("Responses.process.pid", "long"),  | 
 | 1411 | +                    columnInfo("process.parent.command_line", "keyword"),  | 
 | 1412 | +                    columnInfo("process.parent.command_line.text", "text")  | 
 | 1413 | +                )  | 
 | 1414 | +            ).entry("values", List.of(matchesList().item(null).item(null).item("run1.bat").item("run1.bat")))  | 
 | 1415 | +        );  | 
 | 1416 | +    }  | 
 | 1417 | + | 
 | 1418 | +    private void assumeIndexResolverNestedFieldsNameClashFixed() throws IOException {  | 
 | 1419 | +        // especially for BWC tests but also for regular tests  | 
 | 1420 | +        var capsName = EsqlCapabilities.Cap.FIX_NESTED_FIELDS_NAME_CLASH_IN_INDEXRESOLVER.name().toLowerCase(Locale.ROOT);  | 
 | 1421 | +        boolean requiredClusterCapability = clusterHasCapability("POST", "/_query", List.of(), List.of(capsName)).orElse(false);  | 
 | 1422 | +        assumeTrue(  | 
 | 1423 | +            "This test makes sense for versions that have the fix for https://github.com/elastic/elasticsearch/issues/117054",  | 
 | 1424 | +            requiredClusterCapability  | 
 | 1425 | +        );  | 
 | 1426 | +    }  | 
 | 1427 | + | 
1110 | 1428 |     private CheckedConsumer<XContentBuilder, IOException> empNoInObject(String empNoType) {  | 
1111 | 1429 |         return index -> {  | 
1112 | 1430 |             index.startObject("properties");  | 
 | 
0 commit comments