diff --git a/be/src/vec/CMakeLists.txt b/be/src/vec/CMakeLists.txt index 98375ab8e0..e88a4072c3 100644 --- a/be/src/vec/CMakeLists.txt +++ b/be/src/vec/CMakeLists.txt @@ -177,7 +177,7 @@ set(VEC_FILES exprs/table_function/vexplode_numbers.cpp exprs/table_function/vexplode_bitmap.cpp exprs/lambda_function/varray_map_function.cpp - exprs/lambda_function/varray_filter_function.cpp + exprs/lambda_function/varray_filter_function.cpp functions/array/function_array_index.cpp functions/array/function_array_element.cpp functions/array/function_array_register.cpp diff --git a/docs/en/docs/sql-manual/sql-functions/array-functions/array_last.md b/docs/en/docs/sql-manual/sql-functions/array-functions/array_last.md new file mode 100644 index 0000000000..d2931f245e --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/array-functions/array_last.md @@ -0,0 +1,71 @@ +--- +{ + "title": "array_last", + "language": "en" +} +--- + + + +## array_last + + + +array_last + + + +### description +Returns the last element in the array for which func(arr1[i]) returns something other than 0. + +#### Syntax + +``` +T array_last(lambda, ARRAY) +``` + +Use a lambda bool expression and an array as the input parameters, the lambda expression is used to evaluate the internal data of other input ARRAY parameters. + +### notice + +`Only supported in vectorized engine` + +### example + +``` +mysql> select array_last(x->x>2, [1,2,3,0]) ; ++------------------------------------------------------------------------------------------------+ +| array_last(array_filter(ARRAY(1, 2, 3, 0), array_map([x] -> x(0) > 2, ARRAY(1, 2, 3, 0))), -1) | ++------------------------------------------------------------------------------------------------+ +| 3 | ++------------------------------------------------------------------------------------------------+ + + +mysql> select array_last(x->x>4, [1,2,3,0]) ; ++------------------------------------------------------------------------------------------------+ +| array_last(array_filter(ARRAY(1, 2, 3, 0), array_map([x] -> x(0) > 4, ARRAY(1, 2, 3, 0))), -1) | ++------------------------------------------------------------------------------------------------+ +| NULL | ++------------------------------------------------------------------------------------------------+ + + +### keywords + +ARRAY, LAST, ARRAY_LAST diff --git a/docs/sidebars.json b/docs/sidebars.json index c543983e05..0bae0ec910 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -310,6 +310,7 @@ "sql-manual/sql-functions/array-functions/array_cum_sum", "sql-manual/sql-functions/array-functions/array_exists", "sql-manual/sql-functions/array-functions/array_first_index", + "sql-manual/sql-functions/array-functions/array_last", "sql-manual/sql-functions/array-functions/arrays_overlap", "sql-manual/sql-functions/array-functions/countequal", "sql-manual/sql-functions/array-functions/element_at" diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/array-functions/array_last.md b/docs/zh-CN/docs/sql-manual/sql-functions/array-functions/array_last.md new file mode 100644 index 0000000000..976430ecc4 --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/array-functions/array_last.md @@ -0,0 +1,71 @@ +--- +{ + "title": "array_last", + "language": "zh-CN" +} +--- + + + +## array_last + + + +array_last + + + +### description +返回数组中的最后一个func(arr1[i])值不为0的元素。当数组中所有元素进行func(arr1[i])都为0时,结果返回`NULL`值。 + +#### Syntax + +``` +T array_last(lambda, ARRAY) +``` + +使用一个lambda表达式和一个ARRAY作为输入参数,lambda表达式为布尔型,用于对ARRAY中的每个元素进行判断返回值。 + +### notice + +`仅支持向量化引擎中使用` + +### example + +``` +mysql> select array_last(x->x>2, [1,2,3,0]) ; ++------------------------------------------------------------------------------------------------+ +| array_last(array_filter(ARRAY(1, 2, 3, 0), array_map([x] -> x(0) > 2, ARRAY(1, 2, 3, 0))), -1) | ++------------------------------------------------------------------------------------------------+ +| 3 | ++------------------------------------------------------------------------------------------------+ + + +mysql> select array_last(x->x>4, [1,2,3,0]) ; ++------------------------------------------------------------------------------------------------+ +| array_last(array_filter(ARRAY(1, 2, 3, 0), array_map([x] -> x(0) > 4, ARRAY(1, 2, 3, 0))), -1) | ++------------------------------------------------------------------------------------------------+ +| NULL | ++------------------------------------------------------------------------------------------------+ + + +### keywords + +ARRAY, LAST, ARRAY_LAST diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/LambdaFunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/LambdaFunctionCallExpr.java index 546ef19822..d31e240b3d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LambdaFunctionCallExpr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LambdaFunctionCallExpr.java @@ -35,12 +35,14 @@ import java.util.List; public class LambdaFunctionCallExpr extends FunctionCallExpr { public static final ImmutableSet LAMBDA_FUNCTION_SET = new ImmutableSortedSet.Builder( String.CASE_INSENSITIVE_ORDER).add("array_map").add("array_filter").add("array_exists").add("array_sortby") - .add("array_first_index").build(); + .add("array_first_index").add("array_last").build(); // The functions in this set are all normal array functions when implemented initially. // and then wants add lambda expr as the input param, so we rewrite it to contains an array_map lambda function // rather than reimplementing a lambda function, this will be reused the implementation of normal array function public static final ImmutableSet LAMBDA_MAPPED_FUNCTION_SET = new ImmutableSortedSet.Builder( - String.CASE_INSENSITIVE_ORDER).add("array_exists").add("array_sortby").add("array_first_index").build(); + String.CASE_INSENSITIVE_ORDER).add("array_exists").add("array_sortby") + .add("array_first_index").add("array_last") + .build(); private static final Logger LOG = LogManager.getLogger(LambdaFunctionCallExpr.class); @@ -204,6 +206,31 @@ public class LambdaFunctionCallExpr extends FunctionCallExpr { throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes())); } fn.setReturnType(getChild(0).getType()); + } else if (fnName.getFunction().equalsIgnoreCase("array_last")) { + // array_last(lambda,array)--->array_last(array,lambda)--->element_at(array_filter,-1) + if (getChild(childSize - 1) instanceof LambdaFunctionExpr) { + List params = new ArrayList<>(); + for (int i = 0; i <= childSize - 1; ++i) { + params.add(getChild(i)); + } + LambdaFunctionCallExpr arrayFilterFunc = new LambdaFunctionCallExpr("array_filter", params); + arrayFilterFunc.analyzeImpl(analyzer); + IntLiteral indexParam = new IntLiteral(-1, Type.INT); + + argTypes = new Type[2]; + argTypes[0] = getChild(0).getType(); + argTypes[1] = indexParam.getType(); + this.children.clear(); + this.children.add(arrayFilterFunc); + this.children.add(indexParam); + } + fnName = new FunctionName(null, "element_at"); + fn = getBuiltinFunction(fnName.getFunction(), argTypes, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); + if (fn == null) { + LOG.warn("fn element_at not exists"); + throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes())); + } + fn.setReturnType(((ArrayType) argTypes[0]).getItemType()); } LOG.info("fn string: " + fn.signatureString() + ". return type: " + fn.getReturnType()); if (fn == null) { diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 6a302432c8..f019b4f97b 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -791,6 +791,7 @@ visible_functions = [ [['array_zip'], 'ARRAY', ['ARRAY', '...'], ''], + # reverse function for string builtin [['reverse'], 'VARCHAR', ['VARCHAR'], ''], # reverse function support the longtext diff --git a/regression-test/data/query_p0/sql_functions/array_functions/test_array_last.out b/regression-test/data/query_p0/sql_functions/array_functions/test_array_last.out new file mode 100644 index 0000000000..5905303382 --- /dev/null +++ b/regression-test/data/query_p0/sql_functions/array_functions/test_array_last.out @@ -0,0 +1,28 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !select_00 -- +5 + +-- !select_01 -- +\N + +-- !select_03 -- +5 + +-- !select_04 -- +c + +-- !select_05 -- +5.300000000 + +-- !select_06 -- +0 [2] ['123', '124', '125'] +1 [1, 2, 3, 4, 5] ['234', '124', '125'] +2 [1, 2, 10, 12, 10] ['345', '234', '123'] +3 [1, 3, 4, 2] ['222', '444', '555'] + +-- !select_07 -- +\N 125 +5 125 +10 234 +4 555 + diff --git a/regression-test/suites/query_p0/sql_functions/array_functions/test_array_last.groovy b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_last.groovy new file mode 100644 index 0000000000..fe5fdec9ff --- /dev/null +++ b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_last.groovy @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License 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. + +suite("test_array_last") { + + def tableName = "test_array_last" + sql "DROP TABLE IF EXISTS ${tableName}" + sql """ + CREATE TABLE IF NOT EXISTS `${tableName}` ( + `id` int(11) NULL, + `c_array1` array NULL, + `c_array2` array NULL + ) ENGINE=OLAP + DUPLICATE KEY(`id`) + DISTRIBUTED BY HASH(`id`) BUCKETS 1 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1", + "storage_format" = "V2" + ) + """ + + + sql """INSERT INTO ${tableName} values + (0, [2], ['123', '124', '125']), + (1, [1,2,3,4,5], ['234', '124', '125']), + (2, [1,2,10,12,10], ['345', '234', '123']), + (3, [1,3,4,2], ['222', '444', '555']) + """ + qt_select_00 " select array_last(x -> x>3, [1,2,3,4,5]);" + qt_select_01 " select array_last(x -> x<1, [1,2,3,4,5]);" + qt_select_03 " select array_last(x -> x>=5,[1,2,3,4,5]);" + qt_select_04 " select array_last(x -> x > 'abc', ['a','b','c']);" + qt_select_05 " select array_last(x -> x > 5.2 , [10.2, 5.3, 4]);" + + qt_select_06 "select * from ${tableName} order by id;" + + qt_select_07 " select array_last(x->x>3,c_array1), array_last(x-> x>'124',c_array2) from test_array_last order by id;" + sql "DROP TABLE IF EXISTS ${tableName}" +} \ No newline at end of file