-
Notifications
You must be signed in to change notification settings - Fork 25.2k
SQL: Fix ORDER BY on aggregates and GROUPed BY fields #51894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
b49a23c
a68c6de
90cb862
ab0c627
9d25c93
aaf2bd3
a1ec293
65a1ca3
46b9700
637a9f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
package org.elasticsearch.xpack.sql.querydsl.container; | ||
|
||
import java.util.Objects; | ||
|
||
public class GroupingFunctionSort extends Sort { | ||
|
||
private final String id; | ||
|
||
public GroupingFunctionSort(String id, Direction direction, Missing missing) { | ||
super(direction, missing); | ||
this.id = id; | ||
} | ||
|
||
@Override | ||
public String id() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(direction(), missing(), id); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (obj == null || getClass() != obj.getClass()) { | ||
return false; | ||
} | ||
|
||
GroupingFunctionSort other = (GroupingFunctionSort) obj; | ||
return Objects.equals(direction(), other.direction()) | ||
&& Objects.equals(missing(), other.missing()) | ||
&& Objects.equals(id(), other.id()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,6 @@ | |
import org.elasticsearch.xpack.ql.expression.Expression; | ||
import org.elasticsearch.xpack.ql.expression.Expressions; | ||
import org.elasticsearch.xpack.ql.expression.FieldAttribute; | ||
import org.elasticsearch.xpack.ql.expression.function.aggregate.AggregateFunction; | ||
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction; | ||
import org.elasticsearch.xpack.ql.expression.gen.pipeline.ConstantInput; | ||
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe; | ||
|
@@ -134,45 +133,39 @@ public List<Tuple<Integer, Comparator>> sortingColumns() { | |
return emptyList(); | ||
} | ||
|
||
List<Tuple<Integer, Comparator>> sortingColumns = new ArrayList<>(sort.size()); | ||
|
||
boolean aggSort = false; | ||
for (Sort s : sort) { | ||
Tuple<Integer, Comparator> tuple = new Tuple<>(Integer.valueOf(-1), null); | ||
|
||
if (s instanceof AggregateSort) { | ||
AggregateSort as = (AggregateSort) s; | ||
// find the relevant column of each aggregate function | ||
AggregateFunction af = as.agg(); | ||
|
||
aggSort = true; | ||
int atIndex = -1; | ||
String id = Expressions.id(af); | ||
|
||
for (int i = 0; i < fields.size(); i++) { | ||
Tuple<FieldExtraction, String> field = fields.get(i); | ||
if (field.v2().equals(id)) { | ||
atIndex = i; | ||
break; | ||
} | ||
} | ||
if (atIndex == -1) { | ||
throw new SqlIllegalArgumentException("Cannot find backing column for ordering aggregation [{}]", s); | ||
} | ||
// assemble a comparator for it | ||
Comparator comp = s.direction() == Sort.Direction.ASC ? Comparator.naturalOrder() : Comparator.reverseOrder(); | ||
comp = s.missing() == Sort.Missing.FIRST ? Comparator.nullsFirst(comp) : Comparator.nullsLast(comp); | ||
|
||
tuple = new Tuple<>(Integer.valueOf(atIndex), comp); | ||
customSort = Boolean.TRUE; | ||
} | ||
sortingColumns.add(tuple); | ||
} | ||
|
||
|
||
// If no custom sort is used break early | ||
if (customSort == null) { | ||
customSort = Boolean.valueOf(aggSort); | ||
customSort = Boolean.FALSE; | ||
return emptyList(); | ||
} | ||
|
||
return aggSort ? sortingColumns : emptyList(); | ||
List<Tuple<Integer, Comparator>> sortingColumns = new ArrayList<>(sort.size()); | ||
for (Sort s: sort) { | ||
int atIndex = -1; | ||
for (int i = 0; i < fields.size(); i++) { | ||
Tuple<FieldExtraction, String> field = fields.get(i); | ||
if (field.v2().equals(s.id())) { | ||
atIndex = i; | ||
break; | ||
} | ||
} | ||
if (atIndex==-1) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Somehow I broke the formatting :( |
||
throw new SqlIllegalArgumentException("Cannot find backing column for ordering aggregation [{}]", s); | ||
} | ||
// assemble a comparator for it | ||
Comparator comp = s.direction()==Sort.Direction.ASC ? Comparator.naturalOrder():Comparator.reverseOrder(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
comp = s.missing()==Sort.Missing.FIRST ? Comparator.nullsFirst(comp):Comparator.nullsLast(comp); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
sortingColumns.add(new Tuple<>(Integer.valueOf(atIndex), comp)); | ||
} | ||
|
||
return sortingColumns; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,13 +8,22 @@ | |
import java.util.Objects; | ||
|
||
public class ScoreSort extends Sort { | ||
public ScoreSort(Direction direction, Missing missing) { | ||
|
||
final String id; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you explore the idea of having the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided not to, because 2 classes the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have done it differently. I feel like The main bothering aspect for me is that there is a required You can leave it as is, I just wanted to point out something that doesn't feel right to me. |
||
|
||
public ScoreSort(String id, Direction direction, Missing missing) { | ||
super(direction, missing); | ||
this.id = id; | ||
} | ||
|
||
@Override | ||
public String id() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(direction(), missing()); | ||
return Objects.hash(direction(), missing(), id()); | ||
} | ||
|
||
@Override | ||
|
@@ -29,6 +38,7 @@ public boolean equals(Object obj) { | |
|
||
ScriptSort other = (ScriptSort) obj; | ||
return Objects.equals(direction(), other.direction()) | ||
&& Objects.equals(missing(), other.missing()); | ||
&& Objects.equals(missing(), other.missing()) | ||
&& Objects.equals(id(), other.id()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not breaking early, if there's the first AggregateSort found?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot break after the first AggregateSort. Maybe we could break after the last AggregateSort.
If we have:
we cannot break after
max
, we could break aftermin
.I'd rather leave the fix as is and introduce this optimisation in a separate PR where it's properly tested that it works.
(Needs some carefully chosen data set to test this ordering case)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. That's a simple loop that, when finds an AggregateSort, will set
customSort
toTRUE
. It doesn't really matter what's after in the list ofsort
s because it doesn't change the value ofcustomSort
.Also, I meant breaking from inside the loop not from the method...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, misunderstood you there, sure we should break once the 1st is found.