Skip to content

Commit 9353842

Browse files
ES|QL: more complex expressions in generative tests (#135766)
Let generative tests create more complex expressions, both in EVAL and in STATS
1 parent e7295f0 commit 9353842

File tree

3 files changed

+81
-12
lines changed

3 files changed

+81
-12
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public abstract class GenerativeRestTest extends ESRestTestCase implements Query
6666
"The incoming YAML document exceeds the limit:", // still to investigate, but it seems to be specific to the test framework
6767
"Data too large", // Circuit breaker exceptions eg. https://github.com/elastic/elasticsearch/issues/130072
6868
"optimized incorrectly due to missing references", // https://github.com/elastic/elasticsearch/issues/131509
69+
"long overflow", // https://github.com/elastic/elasticsearch/issues/135759
6970
"can't build page out of released blocks", // https://github.com/elastic/elasticsearch/issues/135679
7071

7172
// Awaiting fixes for correctness

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

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -343,23 +343,43 @@ yield switch ((randomIntBetween(0, 2))) {
343343
}
344344

345345
public static String agg(List<Column> previousOutput) {
346-
String name = randomNumericOrDateField(previousOutput);
346+
String name = randomNumericField(previousOutput);
347+
// complex with numerics
347348
if (name != null && randomBoolean()) {
348-
// numerics only
349-
return switch (randomIntBetween(0, 1)) {
350-
case 0 -> "max(" + name + ")";
351-
default -> "min(" + name + ")";
352-
// TODO more numerics
353-
};
349+
int ops = randomIntBetween(1, 3);
350+
StringBuilder result = new StringBuilder();
351+
for (int i = 0; i < ops; i++) {
352+
if (i > 0) {
353+
result.append(" + ");
354+
}
355+
String agg = switch (randomIntBetween(0, 5)) {
356+
case 0 -> "max(" + name + ")";
357+
case 1 -> "min(" + name + ")";
358+
case 2 -> "avg(" + name + ")";
359+
case 3 -> "median(" + name + ")";
360+
case 4 -> "sum(" + name + ")";
361+
default -> "count(" + name + ")";
362+
// TODO more numerics
363+
};
364+
result.append(agg);
365+
}
366+
return result.toString();
354367
}
355368
// all types
356369
name = randomBoolean() ? randomStringField(previousOutput) : randomNumericOrDateField(previousOutput);
357370
if (name == null) {
358371
return "count(*)";
359372
}
360-
return switch (randomIntBetween(0, 2)) {
373+
if (randomBoolean()) {
374+
String exp = expression(previousOutput, false);
375+
name = exp == null ? name : exp;
376+
}
377+
return switch (randomIntBetween(0, 5)) {
361378
case 0 -> "count(*)";
362379
case 1 -> "count(" + name + ")";
380+
case 2 -> "absent(" + name + ")";
381+
case 3 -> "present(" + name + ")";
382+
case 4 -> "values(" + name + ")";
363383
default -> "count_distinct(" + name + ")";
364384
};
365385
}
@@ -416,9 +436,50 @@ public static String randomName(List<Column> cols, Set<String> allowedTypes) {
416436
return items.get(randomIntBetween(0, items.size() - 1));
417437
}
418438

419-
public static String expression(List<Column> previousOutput) {
420-
// TODO improve!!!
421-
return constantExpression();
439+
/**
440+
* @param previousOutput columns that can be used in the expression
441+
* @param allowConstants if set to true, this will never return a constant expression.
442+
* If no expression can be generated, it will return null
443+
* @return an expression or null
444+
*/
445+
public static String expression(List<Column> previousOutput, boolean allowConstants) {
446+
if (randomBoolean() && allowConstants) {
447+
return constantExpression();
448+
}
449+
if (randomBoolean()) {
450+
StringBuilder result = new StringBuilder();
451+
for (int i = 0; i < randomIntBetween(1, 3); i++) {
452+
String field = randomNumericField(previousOutput);
453+
if (field == null) {
454+
return allowConstants ? constantExpression() : null;
455+
}
456+
if (i > 0) {
457+
result.append(" + ");
458+
}
459+
result.append(field);
460+
}
461+
return result.toString();
462+
}
463+
if (randomBoolean()) {
464+
String field = randomKeywordField(previousOutput);
465+
if (field == null) {
466+
return allowConstants ? constantExpression() : null;
467+
}
468+
return switch (randomIntBetween(0, 3)) {
469+
case 0 -> "substring(" + field + ", 1, 3)";
470+
case 1 -> "to_lower(" + field + ")";
471+
case 2 -> "to_upper(" + field + ")";
472+
default -> "length(" + field + ")";
473+
};
474+
}
475+
if (randomBoolean() || allowConstants == false) {
476+
String field = randomStringField(previousOutput);
477+
if (field == null || randomBoolean()) {
478+
field = randomNumericOrDateField(previousOutput);
479+
}
480+
return field;
481+
}
482+
return allowConstants ? constantExpression() : null;
422483
}
423484

424485
public static String indexPattern(String indexName) {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.ArrayList;
1616
import java.util.List;
1717
import java.util.Map;
18+
import java.util.stream.Collectors;
1819

1920
import static org.elasticsearch.test.ESTestCase.randomBoolean;
2021
import static org.elasticsearch.test.ESTestCase.randomIntBetween;
@@ -34,6 +35,7 @@ public CommandDescription generate(
3435
) {
3536
StringBuilder cmd = new StringBuilder(" | eval ");
3637
int nFields = randomIntBetween(1, 10);
38+
Map<String, Column> usablePrevious = previousOutput.stream().collect(Collectors.toMap(Column::name, c -> c));
3739
// TODO pass newly created fields to next expressions
3840
var newColumns = new ArrayList<>();
3941
for (int i = 0; i < nFields; i++) {
@@ -46,7 +48,7 @@ public CommandDescription generate(
4648
name = EsqlQueryGenerator.randomIdentifier();
4749
}
4850
}
49-
String expression = EsqlQueryGenerator.expression(previousOutput);
51+
String expression = EsqlQueryGenerator.expression(usablePrevious.values().stream().toList(), true);
5052
if (i > 0) {
5153
cmd.append(",");
5254
}
@@ -56,6 +58,11 @@ public CommandDescription generate(
5658
newColumns.add(unquote(name));
5759
cmd.append(" = ");
5860
cmd.append(expression);
61+
62+
// there could be collisions in many ways, remove all of them
63+
usablePrevious.remove(name);
64+
usablePrevious.remove("`" + name + "`");
65+
usablePrevious.remove(unquote(name));
5966
}
6067
String cmdString = cmd.toString();
6168
return new CommandDescription(EVAL, this, cmdString, Map.ofEntries(Map.entry(NEW_COLUMNS, newColumns)));

0 commit comments

Comments
 (0)