-
Notifications
You must be signed in to change notification settings - Fork 181
Enable concat() string function to support multiple string arguments
#1279
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 1 commit
1f924f5
4ac26a2
bed81bf
30716a3
201f90a
2eee626
d066caa
4e2422e
0654fbb
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 |
|---|---|---|
|
|
@@ -9,12 +9,14 @@ | |
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
| import lombok.experimental.UtilityClass; | ||
| import org.apache.commons.lang3.tuple.Pair; | ||
| import org.opensearch.sql.data.model.ExprValue; | ||
| import org.opensearch.sql.data.model.ExprValueUtils; | ||
| import org.opensearch.sql.data.type.ExprCoreType; | ||
Yury-Fridlyand marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import org.opensearch.sql.data.type.ExprType; | ||
| import org.opensearch.sql.expression.Expression; | ||
| import org.opensearch.sql.expression.FunctionExpression; | ||
|
|
@@ -58,6 +60,39 @@ public static DefaultFunctionResolver define(FunctionName functionName, List< | |
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Define varargs function with implementation. | ||
| * | ||
| * @param functionName function name. | ||
| * @param functions a list of function implementation. | ||
| * @return VarargsFunctionResolver. | ||
| */ | ||
| public static VarargsFunctionResolver defineVarargsFunction(FunctionName functionName, | ||
| SerializableFunction<FunctionName, | ||
| Pair<FunctionSignature, FunctionBuilder>>... functions) { | ||
| return defineVarargsFunction(functionName, List.of(functions)); | ||
| } | ||
|
|
||
| /** | ||
| * Define varargs function with implementation. | ||
| * | ||
| * @param functionName function name. | ||
| * @param functions a list of function implementation. | ||
| * @return VarargsFunctionResolver. | ||
| */ | ||
| public static VarargsFunctionResolver defineVarargsFunction(FunctionName functionName, List< | ||
| SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>> functions) { | ||
|
|
||
| VarargsFunctionResolver.VarargsFunctionResolverBuilder builder = | ||
| VarargsFunctionResolver.builder(); | ||
| builder.functionName(functionName); | ||
| for (SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>> func | ||
| : functions) { | ||
| Pair<FunctionSignature, FunctionBuilder> functionBuilder = func.apply(functionName); | ||
| builder.functionBundle(functionBuilder.getKey(), functionBuilder.getValue()); | ||
| } | ||
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Implementation of no args function that uses FunctionProperties. | ||
|
|
@@ -212,6 +247,56 @@ public static SerializableFunction<FunctionName, Pair<FunctionSignature, Functio | |
| return implWithProperties((fp, arg) -> function.apply(arg), returnType, argsType); | ||
| } | ||
|
|
||
| /** | ||
| * Varargs Function Implementation. | ||
| * This implementation considers 1...n args of the same type. | ||
| * | ||
| * @param function {@link ExprValue} based varargs function. | ||
| * @param returnType return type. | ||
| * @param argsType argument type. | ||
| * @return Varargs Function Implementation. | ||
| */ | ||
| public static SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>> impl( | ||
| SerializableVarargsFunction<ExprValue, ExprValue> function, | ||
| ExprType returnType, | ||
| ExprType argsType, | ||
| boolean withVarargs) { | ||
|
|
||
| return functionName -> { | ||
| AtomicInteger argsCount = new AtomicInteger(0); | ||
| FunctionBuilder functionBuilder = | ||
| (functionProperties, arguments) -> new FunctionExpression(functionName, arguments) { | ||
| @Override | ||
| public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) { | ||
| argsCount.set(arguments.size()); | ||
dai-chen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ExprValue[] args = arguments.stream() | ||
| .map(arg -> arg.valueOf(valueEnv)) | ||
| .collect(Collectors.toList()) | ||
| .toArray(new ExprValue[arguments.size()]); | ||
|
|
||
| return function.apply(args); | ||
| } | ||
|
|
||
| @Override | ||
| public ExprType type() { | ||
| return returnType; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return String.format("%s(%s)", functionName, arguments.stream() | ||
| .map(Object::toString) | ||
| .collect(Collectors.joining(", "))); | ||
| } | ||
| }; | ||
| ExprCoreType[] argsTypes = new ExprCoreType[argsCount.get()]; | ||
| Arrays.fill(argsTypes, argsType); | ||
| FunctionSignature functionSignature = | ||
| new FunctionSignature(functionName, List.of(argsTypes)); | ||
| return Pair.of(functionSignature, functionBuilder); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Binary Function Implementation. | ||
| * | ||
|
|
@@ -323,13 +408,29 @@ public SerializableTriFunction<ExprValue, ExprValue, ExprValue, ExprValue> nullM | |
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Wrapper the varargs ExprValue function with default NULL and MISSING handling. | ||
| */ | ||
| public SerializableVarargsFunction<ExprValue, ExprValue> nullMissingHandling( | ||
| SerializableVarargsFunction<ExprValue, ExprValue> function, boolean withVarargs) { | ||
| return (args) -> { | ||
| if (Arrays.stream(args).anyMatch(ExprValue::isMissing)) { | ||
| return ExprValueUtils.missingValue(); | ||
| } else if (Arrays.stream(args).anyMatch(ExprValue::isNull)) { | ||
|
||
| return ExprValueUtils.nullValue(); | ||
| } else { | ||
|
||
| return function.apply(args); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Wrapper the unary ExprValue function that is aware of FunctionProperties, | ||
| * with default NULL and MISSING handling. | ||
| */ | ||
| public static SerializableBiFunction<FunctionProperties, ExprValue, ExprValue> | ||
| nullMissingHandlingWithProperties( | ||
| SerializableBiFunction<FunctionProperties, ExprValue, ExprValue> implementation) { | ||
| SerializableBiFunction<FunctionProperties, ExprValue, ExprValue> implementation) { | ||
| return (functionProperties, v1) -> { | ||
| if (v1.isMissing()) { | ||
| return ExprValueUtils.missingValue(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
|
|
||
| package org.opensearch.sql.expression.function; | ||
|
|
||
| import java.io.Serializable; | ||
|
|
||
| /** | ||
| * Serializable Varargs Function. | ||
| */ | ||
| public interface SerializableVarargsFunction<T, R> extends Serializable { | ||
| /** | ||
| * Applies this function to the given arguments. | ||
| * | ||
| * @param t the function argument | ||
| * @return the function result | ||
| */ | ||
| R apply(T... t); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.expression.function; | ||
|
|
||
| import java.util.AbstractMap; | ||
| import java.util.Map; | ||
| import java.util.PriorityQueue; | ||
| import java.util.Set; | ||
| import java.util.stream.Collectors; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.Singular; | ||
| import org.apache.commons.lang3.tuple.Pair; | ||
| import org.opensearch.sql.exception.ExpressionEvaluationException; | ||
|
|
||
| /** | ||
| * The Function Resolver hold the overload {@link FunctionBuilder} implementation. | ||
| * is composed by {@link FunctionName} which identified the function name | ||
| * and a map of {@link FunctionSignature} and {@link FunctionBuilder} | ||
| * to represent the overloaded implementation | ||
| */ | ||
| @Builder | ||
| @RequiredArgsConstructor | ||
| public class VarargsFunctionResolver implements FunctionResolver { | ||
dai-chen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| @Getter | ||
| private final FunctionName functionName; | ||
| @Singular("functionBundle") | ||
| private final Map<FunctionSignature, FunctionBuilder> functionBundle; | ||
|
|
||
| /** | ||
| * Resolve the {@link FunctionBuilder} by using input {@link FunctionSignature}. | ||
| * If the {@link FunctionBuilder} exactly match the input {@link FunctionSignature}, return it. | ||
| * If applying the widening rule, found the most match one, return it. | ||
| * If nothing found, throw {@link ExpressionEvaluationException} | ||
| * | ||
| * @return function signature and its builder | ||
| */ | ||
| @Override | ||
| public Pair<FunctionSignature, FunctionBuilder> resolve(FunctionSignature unresolvedSignature) { | ||
| PriorityQueue<Map.Entry<Integer, FunctionSignature>> functionMatchQueue = new PriorityQueue<>( | ||
| Map.Entry.comparingByKey()); | ||
|
|
||
| for (FunctionSignature functionSignature : functionBundle.keySet()) { | ||
| functionMatchQueue.add( | ||
| new AbstractMap.SimpleEntry<>(unresolvedSignature.match(functionSignature), | ||
| functionSignature)); | ||
| } | ||
| Map.Entry<Integer, FunctionSignature> bestMatchEntry = functionMatchQueue.peek(); | ||
| if (unresolvedSignature.getParamTypeList().isEmpty()) { | ||
| throw new ExpressionEvaluationException( | ||
| String.format("%s function expected %s, but get %s", functionName, | ||
| formatFunctions(functionBundle.keySet()), | ||
| unresolvedSignature.formatTypes() | ||
| )); | ||
| } else { | ||
| FunctionSignature resolvedSignature = bestMatchEntry.getValue(); | ||
| return Pair.of(resolvedSignature, functionBundle.get(resolvedSignature)); | ||
| } | ||
| } | ||
|
|
||
| private String formatFunctions(Set<FunctionSignature> functionSignatures) { | ||
| return functionSignatures.stream().map(FunctionSignature::formatTypes) | ||
| .collect(Collectors.joining(",", "{", "}")); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /* | ||
| * Copyright OpenSearch Contributors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package org.opensearch.sql.expression.function; | ||
|
|
||
| import static org.opensearch.sql.expression.function.FunctionDSL.impl; | ||
|
|
||
| import java.util.List; | ||
| import org.apache.commons.lang3.tuple.Pair; | ||
| import org.opensearch.sql.expression.DSL; | ||
| import org.opensearch.sql.expression.Expression; | ||
|
|
||
| class FunctionDSLimplVarargsTest extends FunctionDSLimplTestBase { | ||
|
|
||
| @Override | ||
| SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>> | ||
| getImplementationGenerator() { | ||
| return impl(varrgs, ANY_TYPE, ANY_TYPE, true); | ||
| } | ||
|
|
||
| @Override | ||
| List<Expression> getSampleArguments() { | ||
| return List.of(DSL.literal(ANY)); | ||
| } | ||
|
|
||
| @Override | ||
| String getExpected_toString() { | ||
| return "sample(ANY)"; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.