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

Support mathematical functions ceil/ceiling, exp, floor, ln, log #540

Merged
merged 16 commits into from
Jul 12, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -58,6 +58,38 @@ public FunctionExpression abs(Environment<Expression, ExprType> env, Expression.
repository.compile(BuiltinFunctionName.ABS.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression ceil(Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.CEIL.getName(), Arrays.asList(expressions), env);
chloe-zh marked this conversation as resolved.
Show resolved Hide resolved
}

public FunctionExpression ceiling(
Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.CEILING.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression exp(Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.EXP.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression floor(
Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.FLOOR.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression ln(Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.LN.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression log(Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.LOG.getName(), Arrays.asList(expressions), env);
}

public FunctionExpression add(Environment<Expression, ExprType> env, Expression... expressions) {
return (FunctionExpression)
repository.compile(BuiltinFunctionName.ADD.getName(), Arrays.asList(expressions), env);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import com.amazon.opendistroforelasticsearch.sql.expression.aggregation.AggregatorFunction;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository;
import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.ArithmeticFunction;
import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.UnaryFunction;
import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.MathematicalFunction;
import com.amazon.opendistroforelasticsearch.sql.expression.operator.predicate.BinaryPredicateOperator;
import com.amazon.opendistroforelasticsearch.sql.expression.operator.predicate.UnaryPredicateOperator;
import java.util.HashMap;
Expand All @@ -40,7 +40,7 @@ public BuiltinFunctionRepository functionRepository() {
new BuiltinFunctionRepository(new HashMap<>());
ArithmeticFunction.register(builtinFunctionRepository);
BinaryPredicateOperator.register(builtinFunctionRepository);
UnaryFunction.register(builtinFunctionRepository);
MathematicalFunction.register(builtinFunctionRepository);
UnaryPredicateOperator.register(builtinFunctionRepository);
AggregatorFunction.register(builtinFunctionRepository);
return builtinFunctionRepository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,20 @@
@Getter
@RequiredArgsConstructor
public enum BuiltinFunctionName {
/**
* Mathematical Functions.
*/
ABS(FunctionName.of("abs")),
CEIL(FunctionName.of("ceil")),
CEILING(FunctionName.of("ceiling")),
EXP(FunctionName.of("exp")),
FLOOR(FunctionName.of("floor")),
LN(FunctionName.of("ln")),
LOG(FunctionName.of("log")),

/**
* Text Functions.
*/
TOSTRING(FunctionName.of("tostring")),

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,57 @@ public String toString() {
};
}

/**
* Construct {@link FunctionBuilder} which call function with arguments produced by observer1 and
* observer2 In general, if any operand evaluates to a MISSING value, the enclosing operator will
* return MISSING; if none of operands evaluates to a MISSING value but there is an operand
* evaluates to a NULL value, the enclosing operator will return NULL.
*
* @param functionName function name
* @param function {@link BiFunction}
* @param observer1 extract the value of type T from the first argument
* @param observer2 extract the value of type U from the second argument
* @param returnType return type
* @param <T> the type of the first argument to the function
* @param <U> the type of the second argument to the function
* @param <R> the type of the result of the function
* @return {@link FunctionBuilder}
*/
public static <T, U, R> FunctionBuilder doubleArgFunc(
chloe-zh marked this conversation as resolved.
Show resolved Hide resolved
FunctionName functionName,
BiFunction<T, U, R> function,
Function<ExprValue, T> observer1,
Function<ExprValue, U> observer2,
ExprType returnType) {
return arguments ->
new FunctionExpression(functionName, arguments) {
@Override
public ExprValue valueOf(Environment<Expression, ExprValue> env) {
ExprValue arg1 = arguments.get(0).valueOf(env);
ExprValue arg2 = arguments.get(1).valueOf(env);
if (arg1.isMissing() || arg2.isMissing()) {
return ExprValueUtils.missingValue();
} else if (arg1.isNull() || arg2.isNull()) {
return ExprValueUtils.nullValue();
} else {
return ExprValueUtils.fromObjectValue(
function.apply(observer1.apply(arg1), observer2.apply(arg2)));
}
}

@Override
public ExprType type(Environment<Expression, ExprType> env) {
return returnType;
}

@Override
public String toString() {
return String.format("%s(%s, %s)", functionName, arguments.get(0).toString(), arguments
.get(1).toString());
}
};
}

/**
* Construct {@link FunctionBuilder} which call function with arguments produced by observer In
* general, if any operand evaluates to a MISSING value, the enclosing operator will return
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright 2019 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.operator.arthmetic;

import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.doubleArgFunc;
import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator;

import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType;
import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName;
import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository;
import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionBuilder;
import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName;
import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionResolver;
import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionSignature;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import lombok.experimental.UtilityClass;

@UtilityClass
public class MathematicalFunction {
/**
* Register Mathematical Functions.
*
* @param repository {@link BuiltinFunctionRepository}.
*/
public static void register(BuiltinFunctionRepository repository) {
repository.register(abs());
repository.register(ceil());
repository.register(ceiling());
repository.register(exp());
repository.register(floor());
repository.register(ln());
repository.register(log());
}

/**
* Definition of abs() function.
* The supported signature of abs() function are
* INT -> INT
* LONG -> LONG
* FLOAT -> FLOAT
* DOUBLE -> DOUBLE
*/
private static FunctionResolver abs() {
return new FunctionResolver(
BuiltinFunctionName.ABS.getName(),
singleArgumentFunction(
BuiltinFunctionName.ABS.getName(), Math::abs, Math::abs, Math::abs, Math::abs));
}

/**
* Definition of ceil(x)/ceiling(x) function.
* Calculate the next highest integer that x rounds up to
* The supported signature of ceil/ceiling function is
* DOUBLE -> LONG
*/
private static FunctionResolver ceil() {
FunctionName functionName = BuiltinFunctionName.CEIL.getName();
return new FunctionResolver(
BuiltinFunctionName.CEIL.getName(),
new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>()
.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)),
unaryOperator(
functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue,
ExprType.LONG))
.build());
}

private static FunctionResolver ceiling() {
FunctionName functionName = BuiltinFunctionName.CEILING.getName();
return new FunctionResolver(
BuiltinFunctionName.CEILING.getName(),
new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>()
.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)),
unaryOperator(
functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue,
ExprType.LONG))
.build());
dai-chen marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Definition of exp(x) function.
* Calculate exponent function e to the x
* The supported signature of exp function is
* DOUBLE -> DOUBLE
*/
private static FunctionResolver exp() {
return new FunctionResolver(BuiltinFunctionName.EXP.getName(),
singleArgumentFunction(BuiltinFunctionName.EXP.getName(), Math::exp));
}

/**
* Definition of floor(x) function.
* Calculate the next nearest whole integer that x rounds down to
* The supported signature of floor function is
* DOUBLE -> LONG
*/
private static FunctionResolver floor() {
FunctionName functionName = BuiltinFunctionName.FLOOR.getName();
return new FunctionResolver(
BuiltinFunctionName.FLOOR.getName(),
new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>()
.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)),
unaryOperator(
functionName, v -> ((long) Math.floor(v)), ExprValueUtils::getDoubleValue,
ExprType.LONG))
.build());
}

/**
* Definition of ln(x) function.
* Calculate the natural logarithm of x
* The supported signature of ln function is
* DOUBLE -> DOUBLE
*/
private static FunctionResolver ln() {
return new FunctionResolver(BuiltinFunctionName.LN.getName(),
singleArgumentFunction(BuiltinFunctionName.LN.getName(), Math::log));
}

/**
* Definition of log(x, y) function.
* Calculate the logarithm of x using y as the base
* The supported signature of log function is
* DOUBLE -> DOUBLE
*/
private static FunctionResolver log() {
return new FunctionResolver(BuiltinFunctionName.LOG.getName(),
doubleArgumentsFunction(
BuiltinFunctionName.LOG.getName(), (v1, v2) -> Math.log(v2) / Math.log(v1)));
}

/**
* Util method to generate single argument function bundles. Applicable for
* INTEGER -> INTEGER
* LONG -> LONG
* FLOAT -> FLOAT
* DOUBLE -> DOUBLE
*/
private static Map<FunctionSignature, FunctionBuilder> singleArgumentFunction(
FunctionName functionName,
Function<Integer, Integer> integerFunc,
Function<Long, Long> longFunc,
Function<Float, Float> floatFunc,
Function<Double, Double> doubleFunc) {
ImmutableMap.Builder<FunctionSignature, FunctionBuilder> builder = new ImmutableMap.Builder<>();
builder.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.INTEGER)),
unaryOperator(
functionName, integerFunc, ExprValueUtils::getIntegerValue, ExprType.INTEGER));
builder.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.LONG)),
unaryOperator(functionName, longFunc, ExprValueUtils::getLongValue, ExprType.LONG));
builder.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.FLOAT)),
unaryOperator(functionName, floatFunc, ExprValueUtils::getFloatValue, ExprType.FLOAT));
builder.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)),
unaryOperator(functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprType.DOUBLE));
return builder.build();
}

/**
* Util method to generate single argument function bundles. Applicable for
* DOUBLE -> DOUBLE/LONG
*/
private static Map<FunctionSignature, FunctionBuilder> singleArgumentFunction(
FunctionName functionName,
Function<Double, Double> doubleFunc) {
ImmutableMap.Builder<FunctionSignature, FunctionBuilder> builder = new ImmutableMap.Builder<>();
return builder
.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)),
unaryOperator(
functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprType.DOUBLE))
.build();
}

/**
* Util method to generate single argument function bundles. Applicable for
* (DOUBLE, DOUBLE) -> DOUBLE
*/
private static Map<FunctionSignature, FunctionBuilder> doubleArgumentsFunction(
FunctionName functionName,
BiFunction<Double, Double, Double> doubleFunc) {
ImmutableMap.Builder<FunctionSignature, FunctionBuilder> builder = new ImmutableMap.Builder<>();
return builder
.put(
new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE, ExprType.DOUBLE)),
doubleArgFunc(functionName, doubleFunc, ExprValueUtils::getDoubleValue,
ExprValueUtils::getDoubleValue, ExprType.DOUBLE))
.build();
}
}
Loading