[feature](array) Add array_last lambda function (#18388)

Add array_last lambda function
This commit is contained in:
herry2038
2023-05-11 13:15:54 +08:00
committed by GitHub
parent 5167dc1251
commit 834bf2eab7
8 changed files with 255 additions and 3 deletions

View File

@ -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

View File

@ -0,0 +1,71 @@
---
{
"title": "array_last",
"language": "en"
}
---
<!--
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.
-->
## array_last
<version since="2.0">
array_last
</version>
### description
Returns the last element in the array for which func(arr1[i]) returns something other than 0.
#### Syntax
```
T array_last(lambda, ARRAY<T>)
```
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

View File

@ -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"

View File

@ -0,0 +1,71 @@
---
{
"title": "array_last",
"language": "zh-CN"
}
---
<!--
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.
-->
## array_last
<version since="2.0">
array_last
</version>
### description
返回数组中的最后一个func(arr1[i])值不为0的元素。当数组中所有元素进行func(arr1[i])都为0时,结果返回`NULL`值。
#### Syntax
```
T array_last(lambda, ARRAY<T>)
```
使用一个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

View File

@ -35,12 +35,14 @@ import java.util.List;
public class LambdaFunctionCallExpr extends FunctionCallExpr {
public static final ImmutableSet<String> 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<String> 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<Expr> 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) {

View File

@ -791,6 +791,7 @@ visible_functions = [
[['array_zip'], 'ARRAY', ['ARRAY', '...'], ''],
# reverse function for string builtin
[['reverse'], 'VARCHAR', ['VARCHAR'], ''],
# reverse function support the longtext

View File

@ -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

View File

@ -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<int(11)> NULL,
`c_array2` array<varchar(20)> 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}"
}