Skip to content

SQL: Implement GREATEST and LEAST functions#35879

Merged
matriv merged 6 commits intoelastic:masterfrom
matriv:mt/impl-35878
Nov 26, 2018
Merged

SQL: Implement GREATEST and LEAST functions#35879
matriv merged 6 commits intoelastic:masterfrom
matriv:mt/impl-35878

Conversation

@matriv
Copy link
Copy Markdown
Contributor

@matriv matriv commented Nov 25, 2018

Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
NULL when all of their arguments evaluate to NULL.

Renamed CoalescePipe and CoalesceProcessor to ConditionalPipe and
ConditionalProcessor respectively, to be able to reuse them for
Greatest and Least evaluations. To achieve that ConditionalOperation
has been added to differentiate between the functionalities at execution
time.

Closes: #35878

Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
`NULL` when all of their arguments evaluate to `NULL`.

Renamed `CoalescePipe` and `CoalesceProcessor` to `ConditionalPipe` and
`ConditionalProcessor` respectively, to be able to reuse them for
`Greatest` and `Least` evaluations. To achieve that `ConditionalOperation`
has been added to differentiate between the functionalities at execution
time.

Closes: elastic#35878
@elasticmachine
Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-search

if (pj instanceof ScalarFunction) {
ScalarFunction f = (ScalarFunction) pj;
processors.put(f.toAttribute(), Expressions.pipe(f));
} else if (pj instanceof In) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover: In is already extending ScalarFunction.

Copy link
Copy Markdown
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good however the optimizer rule could be improved as well as the class hierarchy.

return least(values);
}

private static Object getMinOrMax(Collection<Object> values, BiFunction<Object, Object, Boolean> comparison) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since Min and Max are the actual functions, extremum seems like a better name.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can a type (Number) be inferred to the function instead of Object`?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the arguments can also be Strings.


@Override
public Object fold() {
LinkedHashSet<Object> values = new LinkedHashSet<>(children().size());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a candidate for Foldables.

}

@Override
protected String scriptMethodName() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to move this and the method below to the parent?
Make the function accept a ConditionalOperation and invoke its scriptMethodName and create a ConditionalPipe for it.

}
}

if (e instanceof Greatest || e instanceof Least) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic is exactly that of Coalesce so simply do a check for Greatest, Least and Coalesce - maybe they have a common parent class so use that then after filtering the children simply call expression.replaceChildren(newChildren).

}

@Override
protected String scriptMethodName() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?
This is typically an indication that the parent is not generic enough.


public enum ConditionalOperation implements Function<Collection<Object>, Object> {

COALESCE(Conditionals::coalesce, Conditionals::coalesceOnInput),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like coalesce/greatest/least should have a common parent more specialized then the others conditionals.

@matriv
Copy link
Copy Markdown
Contributor Author

matriv commented Nov 26, 2018

@costin Thanks for the review. Pushed commits to address comments.

Copy link
Copy Markdown
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some minor polishing comments.
LGTM!

return foldAndAdd(list, to, new LinkedHashSet<>(list.size()));
}

private static <T, C extends Collection<T>> C foldAndAdd(Collection<Expression> expressions, DataType to, C values) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename it to foldTo - I was thinking the values are folded and added together based on the current name.

*/
public abstract class ArbitraryConditionalFunction extends ConditionalFunction {

private ConditionalOperation operation;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final

return null;
}

static Object coalesceOnInput(List<Processor> processors, Object input) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The On is superfluous. - coalesceInput, greatestInput, etc...

Copy link
Copy Markdown
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Left one question only.

if ((i == 0) || (result == null) || (comparison.apply(value, result) == Boolean.TRUE)) {
result = value;
}
i++;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here the i logic can be replaced by a boolean? Seems to be a true/false scenario.

@matriv matriv merged commit 3f7cae3 into elastic:master Nov 26, 2018
@matriv matriv deleted the mt/impl-35878 branch November 26, 2018 17:21
matriv added a commit that referenced this pull request Nov 26, 2018
Add GREATEST(expr1, expr2, ... exprN) and LEAST(expr1, expr2, exprN)
functions which are in the family of CONDITIONAL functions.

Implementation follows PostgreSQL behaviour, so the functions return
`NULL` when all of their arguments evaluate to `NULL`.

Renamed `CoalescePipe` and `CoalesceProcessor` to `ConditionalPipe` and
`ConditionalProcessor` respectively, to be able to reuse them for
`Greatest` and `Least` evaluations. To achieve that `ConditionalOperation`
has been added to differentiate between the functionalities at execution
time.

Closes: #35878
@matriv
Copy link
Copy Markdown
Contributor Author

matriv commented Nov 26, 2018

Backported to 6.x with 44d6c74

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants