Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.

Support aggregations min, max #541

Merged
merged 18 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public abstract class AbstractExprValue implements ExprValue {
public int compareTo(ExprValue other) {
if (this.isNull() || this.isMissing() || other.isNull() || other.isMissing()) {
throw new IllegalStateException(
String.format("[BUG] Unreachable, Comparing with NULL or MISSING is undefined"));
String.format("[BUG] Unreachable, Comparing with NULL or MISSING is undefined"));
}
if ((this.isNumber() && other.isNumber()) || this.type() == other.type()) {
return compare(other);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,14 @@ public Aggregator count(Expression... expressions) {
return aggregate(BuiltinFunctionName.COUNT, expressions);
}

public Aggregator min(Expression... expressions) {
return aggregate(BuiltinFunctionName.MIN, expressions);
}

public Aggregator max(Expression... expressions) {
return aggregate(BuiltinFunctionName.MAX, expressions);
}

private FunctionExpression function(BuiltinFunctionName functionName, Expression... expressions) {
return (FunctionExpression) repository.compile(
functionName.getName(), Arrays.asList(expressions));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.ARRAY;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.BOOLEAN;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DATE;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DATETIME;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DOUBLE;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRUCT;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.TIME;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.TIMESTAMP;

import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository;
Expand Down Expand Up @@ -52,6 +56,8 @@ public static void register(BuiltinFunctionRepository repository) {
repository.register(avg());
repository.register(sum());
repository.register(count());
repository.register(min());
repository.register(max());
}

private static FunctionResolver avg() {
Expand Down Expand Up @@ -106,4 +112,57 @@ private static FunctionResolver sum() {
.build()
);
}

private static FunctionResolver min() {
FunctionName functionName = BuiltinFunctionName.MIN.getName();
return new FunctionResolver(
functionName,
new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>()
.put(new FunctionSignature(functionName, Collections.singletonList(INTEGER)),
arguments -> new MinAggregator(arguments, INTEGER))
.put(new FunctionSignature(functionName, Collections.singletonList(LONG)),
arguments -> new MinAggregator(arguments, LONG))
.put(new FunctionSignature(functionName, Collections.singletonList(FLOAT)),
arguments -> new MinAggregator(arguments, FLOAT))
.put(new FunctionSignature(functionName, Collections.singletonList(DOUBLE)),
arguments -> new MinAggregator(arguments, DOUBLE))
.put(new FunctionSignature(functionName, Collections.singletonList(STRING)),
arguments -> new MinAggregator(arguments, STRING))
.put(new FunctionSignature(functionName, Collections.singletonList(DATE)),
arguments -> new MinAggregator(arguments, DATE))
.put(new FunctionSignature(functionName, Collections.singletonList(DATETIME)),
arguments -> new MinAggregator(arguments, DATETIME))
.put(new FunctionSignature(functionName, Collections.singletonList(TIME)),
arguments -> new MinAggregator(arguments, TIME))
.put(new FunctionSignature(functionName, Collections.singletonList(TIMESTAMP)),
arguments -> new MinAggregator(arguments, TIMESTAMP))
.build());
}

private static FunctionResolver max() {
FunctionName functionName = BuiltinFunctionName.MAX.getName();
return new FunctionResolver(
functionName,
new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>()
.put(new FunctionSignature(functionName, Collections.singletonList(INTEGER)),
arguments -> new MaxAggregator(arguments, INTEGER))
.put(new FunctionSignature(functionName, Collections.singletonList(LONG)),
arguments -> new MaxAggregator(arguments, LONG))
.put(new FunctionSignature(functionName, Collections.singletonList(FLOAT)),
arguments -> new MaxAggregator(arguments, FLOAT))
.put(new FunctionSignature(functionName, Collections.singletonList(DOUBLE)),
arguments -> new MaxAggregator(arguments, DOUBLE))
.put(new FunctionSignature(functionName, Collections.singletonList(STRING)),
arguments -> new MaxAggregator(arguments, STRING))
.put(new FunctionSignature(functionName, Collections.singletonList(DATE)),
arguments -> new MaxAggregator(arguments, DATE))
.put(new FunctionSignature(functionName, Collections.singletonList(DATETIME)),
arguments -> new MaxAggregator(arguments, DATETIME))
.put(new FunctionSignature(functionName, Collections.singletonList(TIME)),
arguments -> new MaxAggregator(arguments, TIME))
.put(new FunctionSignature(functionName, Collections.singletonList(TIMESTAMP)),
arguments -> new MaxAggregator(arguments, TIMESTAMP))
.build()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ public AvgState create() {
public AvgState iterate(BindingTuple tuple, AvgState state) {
Expression expression = getArguments().get(0);
ExprValue value = expression.valueOf(tuple);
if (value.isNull() || value.isMissing()) {
state.isNullResult = true;
} else {
if (!(value.isNull() || value.isMissing())) {
state.count++;
state.total += ExprValueUtils.getDoubleValue(value);
}
Expand All @@ -63,19 +61,18 @@ public String toString() {
/**
* Average State.
*/
protected class AvgState implements AggregationState {
protected static class AvgState implements AggregationState {
private int count;
private double total;
private boolean isNullResult = false;

public AvgState() {
AvgState() {
this.count = 0;
this.total = 0d;
}

@Override
public ExprValue result() {
return isNullResult ? ExprNullValue.of() : ExprValueUtils.doubleValue(total / count);
return count == 0 ? ExprNullValue.of() : ExprValueUtils.doubleValue(total / count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public String toString() {
/**
* Count State.
*/
protected class CountState implements AggregationState {
protected static class CountState implements AggregationState {
private int count;

public CountState() {
CountState() {
this.count = 0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amazon.opendistroforelasticsearch.sql.expression.aggregation;

import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.doubleValue;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DOUBLE;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT;
import static com.amazon.opendistroforelasticsearch.sql.utils.ExpressionUtils.format;

import com.amazon.opendistroforelasticsearch.sql.data.model.ExprNullValue;
import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue;
import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType;
import com.amazon.opendistroforelasticsearch.sql.expression.Expression;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName;
import com.amazon.opendistroforelasticsearch.sql.storage.bindingtuple.BindingTuple;
import java.util.Arrays;
import java.util.List;

public class MaxAggregator extends Aggregator<MaxAggregator.MaxState> {

public MaxAggregator(List<Expression> arguments, ExprCoreType returnType) {
super(BuiltinFunctionName.MAX.getName(), arguments, returnType);
}

@Override
public MaxState create() {
return new MaxState(returnType);
}

@Override
public MaxState iterate(BindingTuple tuple, MaxState state) {
Expression expression = getArguments().get(0);
ExprValue value = expression.valueOf(tuple);
if (!(value.isNull() || value.isMissing())) {
state.isEmptyCollection = false;
state.max(value);
}
return state;
}

@Override
public String toString() {
return String.format("max(%s)", format(getArguments()));
}

protected static class MaxState implements AggregationState {
private ExprValue maxResult;
private boolean isEmptyCollection;

MaxState(ExprCoreType type) {
maxResult = isNumber(type) ? doubleValue(Double.MIN_VALUE) : LITERAL_NULL;
isEmptyCollection = true;
}

public void max(ExprValue value) {
maxResult = maxResult.isNull() ? value : maxResult.compareTo(value) > 0 ? maxResult : value;
}

@Override
public ExprValue result() {
return isEmptyCollection ? ExprNullValue.of() : maxResult;
}

private boolean isNumber(ExprCoreType type) {
return Arrays.asList(SHORT, INTEGER, LONG, FLOAT, DOUBLE).contains(type);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amazon.opendistroforelasticsearch.sql.expression.aggregation;

import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.doubleValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.floatValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getDoubleValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getFloatValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getIntegerValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getLongValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getStringValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.integerValue;
import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.longValue;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DOUBLE;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG;
import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT;
import static com.amazon.opendistroforelasticsearch.sql.utils.ExpressionUtils.format;

import com.amazon.opendistroforelasticsearch.sql.data.model.ExprNullValue;
import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue;
import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType;
import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException;
import com.amazon.opendistroforelasticsearch.sql.expression.Expression;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName;
import com.amazon.opendistroforelasticsearch.sql.storage.bindingtuple.BindingTuple;
import java.util.Arrays;
import java.util.List;

/**
* The minimum aggregator aggregate the value evaluated by the expression.
* If the expression evaluated result is NULL or MISSING, then the result is NULL.
*/
public class MinAggregator extends Aggregator<MinAggregator.MinState> {

public MinAggregator(List<Expression> arguments, ExprCoreType returnType) {
super(BuiltinFunctionName.MIN.getName(), arguments, returnType);
}


@Override
public MinState create() {
return new MinState(returnType);
}

@Override
public MinState iterate(BindingTuple tuple, MinState state) {
Expression expression = getArguments().get(0);
ExprValue value = expression.valueOf(tuple);
if (!(value.isNull() || value.isMissing())) {
state.isEmptyCollection = false;
state.min(value);
}
return state;
}

@Override
public String toString() {
return String.format("min(%s)", format(getArguments()));
}

protected static class MinState implements AggregationState {
private ExprValue minResult;
private boolean isEmptyCollection;

MinState(ExprCoreType type) {
minResult = isNumber(type) ? doubleValue(Double.MAX_VALUE) : LITERAL_NULL;
isEmptyCollection = true;
}

public void min(ExprValue value) {
minResult = minResult.isNull() ? value : minResult.compareTo(value) < 0 ? minResult : value;
}

@Override
public ExprValue result() {
return isEmptyCollection ? ExprNullValue.of() : minResult;
}


private boolean isNumber(ExprCoreType type) {
return Arrays.asList(SHORT, INTEGER, LONG, FLOAT, DOUBLE).contains(type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ public SumState create() {
public SumState iterate(BindingTuple tuple, SumState state) {
Expression expression = getArguments().get(0);
ExprValue value = expression.valueOf(tuple);
if (value.isNull() || value.isMissing()) {
state.isNullResult = true;
} else {
if (!(value.isNull() || value.isMissing())) {
state.isEmptyCollection = false;
state.add(value);
}
return state;
Expand All @@ -72,15 +71,16 @@ public String toString() {
/**
* Sum State.
*/
protected class SumState implements AggregationState {
protected static class SumState implements AggregationState {

private final ExprCoreType type;
private ExprValue sumResult;
private boolean isNullResult = false;
private boolean isEmptyCollection;

public SumState(ExprCoreType type) {
SumState(ExprCoreType type) {
this.type = type;
sumResult = ExprValueUtils.integerValue(0);
isEmptyCollection = true;
}

/**
Expand Down Expand Up @@ -108,7 +108,7 @@ public void add(ExprValue value) {

@Override
public ExprValue result() {
return isNullResult ? ExprNullValue.of() : sumResult;
return isEmptyCollection ? ExprNullValue.of() : sumResult;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public enum BuiltinFunctionName {
AVG(FunctionName.of("avg")),
SUM(FunctionName.of("sum")),
COUNT(FunctionName.of("count")),
MIN(FunctionName.of("min")),
MAX(FunctionName.of("max")),

/**
* NULL Test.
Expand Down
Loading