Skip to content

Commit 3d2104c

Browse files
Add generative test for Lookup Join On expression
1 parent d1f8bba commit 3d2104c

File tree

2 files changed

+72
-13
lines changed

2 files changed

+72
-13
lines changed

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/GenerativeRestTest.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,18 @@ private List<String> availableIndices() throws IOException {
267267
private List<LookupIdx> lookupIndices() {
268268
List<LookupIdx> result = new ArrayList<>();
269269
// we don't have key info from the dataset loader, let's hardcode it for now
270-
result.add(new LookupIdx("languages_lookup", List.of(new LookupIdxColumn("language_code", "integer"))));
271-
result.add(new LookupIdx("message_types_lookup", List.of(new LookupIdxColumn("message", "keyword"))));
270+
result.add(
271+
new LookupIdx(
272+
"languages_lookup",
273+
List.of(new LookupIdxColumn("language_code", "integer"), new LookupIdxColumn("language_name", "keyword"))
274+
)
275+
);
276+
result.add(
277+
new LookupIdx(
278+
"message_types_lookup",
279+
List.of(new LookupIdxColumn("message", "keyword"), new LookupIdxColumn("type", "keyword"))
280+
)
281+
);
272282
List<LookupIdxColumn> multiColumnJoinableLookupKeys = List.of(
273283
new LookupIdxColumn("id_int", "integer"),
274284
new LookupIdxColumn("name_str", "keyword"),

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/generator/command/pipe/LookupJoinGenerator.java

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.elasticsearch.xpack.esql.generator.command.CommandGenerator;
1616

1717
import java.util.ArrayList;
18+
import java.util.HashMap;
1819
import java.util.HashSet;
1920
import java.util.List;
2021
import java.util.Map;
@@ -64,17 +65,65 @@ public CommandDescription generate(
6465
return EMPTY_DESCRIPTION;
6566
}
6667
StringBuilder stringBuilder = new StringBuilder();
67-
for (int i = 0; i < keyNames.size(); i++) {
68-
stringBuilder.append("| rename ");
69-
stringBuilder.append(keyNames.get(i));
70-
stringBuilder.append(" as ");
71-
stringBuilder.append(joinOn.get(i));
72-
}
73-
stringBuilder.append(" | lookup join ").append(lookupIdxName).append(" on ");
74-
for (int i = 0; i < keyNames.size(); i++) {
75-
stringBuilder.append(joinOn.get(i));
76-
if (i < keyNames.size() - 1) {
77-
stringBuilder.append(", ");
68+
69+
// Randomly choose between field-based join and expression-based join
70+
boolean useExpressionJoin = ESTestCase.randomBoolean();
71+
72+
if (useExpressionJoin) {
73+
// Get all right side column names (from lookup index)
74+
Set<String> rightColumnNames = lookupIdx.keys()
75+
.stream()
76+
.map(LookupIdxColumn::name)
77+
.collect(java.util.stream.Collectors.toSet());
78+
79+
// Generate rename commands for ALL left columns that exist in right side
80+
Set<String> allLeftColumnNames = previousOutput.stream().map(Column::name).collect(java.util.stream.Collectors.toSet());
81+
Map<String, String> leftColumnName2Renamed = new HashMap<>();
82+
// Rename left columns that conflict with right side columns (either in lookup index or used in join)
83+
for (String leftColumnName : allLeftColumnNames) {
84+
if (rightColumnNames.contains(leftColumnName)) {
85+
String renamedColumn = leftColumnName + "_left";
86+
stringBuilder.append("| rename ");
87+
stringBuilder.append(leftColumnName);
88+
stringBuilder.append(" as ");
89+
stringBuilder.append(renamedColumn);
90+
leftColumnName2Renamed.put(leftColumnName, renamedColumn);
91+
}
92+
}
93+
94+
// Generate expression join syntax
95+
stringBuilder.append(" | lookup join ").append(lookupIdxName).append(" on ");
96+
97+
// Add join conditions for all columns
98+
for (int i = 0; i < keyNames.size(); i++) {
99+
String leftColumnName = keyNames.get(i);
100+
String rightColumnName = joinOn.get(i);
101+
102+
// Only == and != are allowed, as the rest of the operators don's support all types
103+
String[] booleanOperators = { "==", "!=" };
104+
String operator = randomFrom(booleanOperators);
105+
106+
// Use renamed column if it was renamed, otherwise use original
107+
String finalLeftColumnName = leftColumnName2Renamed.getOrDefault(leftColumnName, leftColumnName);
108+
if (i > 0) {
109+
stringBuilder.append(" AND ");
110+
}
111+
stringBuilder.append(finalLeftColumnName).append(" ").append(operator).append(" ").append(rightColumnName);
112+
}
113+
} else {
114+
// Generate field-based join (original behavior)
115+
for (int i = 0; i < keyNames.size(); i++) {
116+
stringBuilder.append("| rename ");
117+
stringBuilder.append(keyNames.get(i));
118+
stringBuilder.append(" as ");
119+
stringBuilder.append(joinOn.get(i));
120+
}
121+
stringBuilder.append(" | lookup join ").append(lookupIdxName).append(" on ");
122+
for (int i = 0; i < keyNames.size(); i++) {
123+
stringBuilder.append(joinOn.get(i));
124+
if (i < keyNames.size() - 1) {
125+
stringBuilder.append(", ");
126+
}
78127
}
79128
}
80129
String cmdString = stringBuilder.toString();

0 commit comments

Comments
 (0)