Skip to content

Commit a7d08ca

Browse files
Marios Trivyzaskcm
Marios Trivyzas
authored andcommitted
SQL: Allow min/max aggregates on date fields (#34699)
Allow `MIN()` and `MAX()` aggregate functions to operate also on arguments of type DATE apart from the numeric ones. Fixes: #34477
1 parent c0d7a06 commit a7d08ca

File tree

5 files changed

+40
-12
lines changed

5 files changed

+40
-12
lines changed

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Expressions.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
99
import org.elasticsearch.xpack.sql.expression.Expression.TypeResolution;
1010
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
11+
import org.elasticsearch.xpack.sql.type.DataType;
1112

1213
import java.util.ArrayList;
1314
import java.util.Collection;
@@ -16,15 +17,10 @@
1617

1718
import static java.util.Collections.emptyList;
1819
import static java.util.Collections.emptyMap;
19-
import static java.util.stream.Collectors.toList;
2020

21-
public abstract class Expressions {
21+
public final class Expressions {
2222

23-
public static List<NamedExpression> asNamed(List<? extends Expression> exp) {
24-
return exp.stream()
25-
.map(NamedExpression.class::cast)
26-
.collect(toList());
27-
}
23+
private Expressions() {}
2824

2925
public static NamedExpression wrapAsNamed(Expression exp) {
3026
return exp instanceof NamedExpression ? (NamedExpression) exp : new Alias(exp.location(), exp.nodeName(), exp);
@@ -126,7 +122,16 @@ public static TypeResolution typeMustBe(Expression e, Predicate<Expression> pred
126122
}
127123

128124
public static TypeResolution typeMustBeNumeric(Expression e) {
129-
return e.dataType().isNumeric()? TypeResolution.TYPE_RESOLVED : new TypeResolution(
130-
"Argument required to be numeric ('" + Expressions.name(e) + "' of type '" + e.dataType().esType + "')");
125+
return e.dataType().isNumeric() ? TypeResolution.TYPE_RESOLVED : new TypeResolution(numericErrorMessage(e));
126+
}
127+
128+
public static TypeResolution typeMustBeNumericOrDate(Expression e) {
129+
return e.dataType().isNumeric() || e.dataType() == DataType.DATE ?
130+
TypeResolution.TYPE_RESOLVED :
131+
new TypeResolution(numericErrorMessage(e));
132+
}
133+
134+
private static String numericErrorMessage(Expression e) {
135+
return "Argument required to be numeric ('" + Expressions.name(e) + "' of type '" + e.dataType().esType + "')";
131136
}
132137
}

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Max.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66
package org.elasticsearch.xpack.sql.expression.function.aggregate;
77

8-
import java.util.List;
98
import org.elasticsearch.xpack.sql.expression.Expression;
9+
import org.elasticsearch.xpack.sql.expression.Expressions;
1010
import org.elasticsearch.xpack.sql.tree.Location;
1111
import org.elasticsearch.xpack.sql.tree.NodeInfo;
1212
import org.elasticsearch.xpack.sql.type.DataType;
1313

14+
import java.util.List;
15+
1416
/**
1517
* Find the maximum value in matching documents.
1618
*/
@@ -39,4 +41,9 @@ public DataType dataType() {
3941
public String innerName() {
4042
return "max";
4143
}
44+
45+
@Override
46+
protected TypeResolution resolveType() {
47+
return Expressions.typeMustBeNumericOrDate(field());
48+
}
4249
}

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Min.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66
package org.elasticsearch.xpack.sql.expression.function.aggregate;
77

8-
import java.util.List;
98
import org.elasticsearch.xpack.sql.expression.Expression;
9+
import org.elasticsearch.xpack.sql.expression.Expressions;
1010
import org.elasticsearch.xpack.sql.tree.Location;
1111
import org.elasticsearch.xpack.sql.tree.NodeInfo;
1212
import org.elasticsearch.xpack.sql.type.DataType;
1313

14+
import java.util.List;
15+
1416
/**
1517
* Find the minimum value in matched documents.
1618
*/
@@ -42,4 +44,9 @@ public DataType dataType() {
4244
public String innerName() {
4345
return "min";
4446
}
47+
48+
@Override
49+
protected TypeResolution resolveType() {
50+
return Expressions.typeMustBeNumericOrDate(field());
51+
}
4552
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public void testGroupByAggregate() {
125125
verify("SELECT AVG(int) FROM test GROUP BY AVG(int)"));
126126
}
127127

128+
public void testNotSupportedAggregateOnDate() {
129+
assertEquals("1:8: Argument required to be numeric ('date' of type 'date')",
130+
verify("SELECT AVG(date) FROM test"));
131+
}
132+
128133
public void testGroupByOnNested() {
129134
assertEquals("1:38: Grouping isn't (yet) compatible with nested fields [dep.dep_id]",
130135
verify("SELECT dep.dep_id FROM test GROUP BY dep.dep_id"));
@@ -169,4 +174,4 @@ public void testHavingOnScalar() {
169174
assertEquals("1:42: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
170175
verify("SELECT int FROM test GROUP BY int HAVING 2 < ABS(int)"));
171176
}
172-
}
177+
}

x-pack/qa/sql/src/main/resources/agg.sql-spec

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ aggMinWithCastAndFilter
216216
SELECT gender g, CAST(MIN(emp_no) AS SMALLINT) m, COUNT(1) c FROM "test_emp" WHERE emp_no < 10020 GROUP BY gender ORDER BY gender;
217217
aggMinWithAlias
218218
SELECT gender g, MIN(emp_no) m FROM "test_emp" GROUP BY g ORDER BY gender;
219+
aggMinOnDate
220+
SELECT gender, MIN(birth_date) m FROM "test_emp" GROUP BY gender ORDER BY gender;
219221

220222
// Conditional MIN
221223
aggMinWithHaving
@@ -270,6 +272,8 @@ aggMaxAndCountWithFilterAndLimit
270272
SELECT gender g, MAX(emp_no) m, COUNT(1) c FROM "test_emp" WHERE emp_no > 10000 GROUP BY gender ORDER BY gender LIMIT 1;
271273
aggMaxWithAlias
272274
SELECT gender g, MAX(emp_no) m FROM "test_emp" GROUP BY g ORDER BY gender;
275+
aggMaxOnDate
276+
SELECT gender, MAX(birth_date) m FROM "test_emp" GROUP BY gender ORDER BY gender;
273277

274278
// Conditional MAX
275279
aggMaxWithHaving

0 commit comments

Comments
 (0)