You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ro...@apache.org on 2022/05/08 15:41:42 UTC

[iotdb] branch master updated: [IOTDB-2992] JEXL UDF: support multi input columns (#5810)

This is an automated email from the ASF dual-hosted git repository.

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new c446c692f0 [IOTDB-2992] JEXL UDF: support multi input columns (#5810)
c446c692f0 is described below

commit c446c692f062aac854f49532ca44b9aa1ee86614
Author: AACEPT <34...@users.noreply.github.com>
AuthorDate: Sun May 8 23:41:37 2022 +0800

    [IOTDB-2992] JEXL UDF: support multi input columns (#5810)
---
 docs/UserGuide/Query-Data/Select-Expression.md     |  74 +++----
 docs/zh/UserGuide/Query-Data/Select-Expression.md  |  72 +++----
 .../db/integration/IoTDBUDTFBuiltinFunctionIT.java |  39 +++-
 .../iotdb/db/query/udf/builtin/UDTFJexl.java       | 224 +++++++++++++--------
 4 files changed, 252 insertions(+), 157 deletions(-)

diff --git a/docs/UserGuide/Query-Data/Select-Expression.md b/docs/UserGuide/Query-Data/Select-Expression.md
index 7d9170750b..d97fc85622 100644
--- a/docs/UserGuide/Query-Data/Select-Expression.md
+++ b/docs/UserGuide/Query-Data/Select-Expression.md
@@ -752,53 +752,53 @@ Java Expression Language (JEXL) is an expression language engine. We use JEXL to
 
 | Function Name | Allowed Input Series Data Types | Required Attributes                           | Output Series Data Type | Series Data Type  Description                 |
 |----------|--------------------------------|---------------------------------------|------------|--------------------------------------------------|
-| JEXL   | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | `expr` is a standard lambda expression with only one argument, conforming to the format `x -> {...}`, for example `x -> {x * 2}` | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | Returns the input time series transformed by a lambda expression |
+| JEXL   | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | `expr` is a lambda expression that supports standard one or multi arguments in the form `x -> {...}` or `(x, y, z) -> {...}`, e.g. ` x -> {x * 2}`, `(x, y, z) -> {x + y * z}` | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | Returns the input time series transformed by a lambda expression |
 
 #### Demonstrate
-Example data: `root.ln.wf01.wt01.temperature` has a total of `11` ordered data from `0.0-10.0`.
-
-```
-IoTDB> select temperature from root.ln.wf01.wt01;
-+-----------------------------+-----------------------------+
-|                         Time|root.ln.wf01.wt01.temperature|
-+-----------------------------+-----------------------------+
-|1970-01-01T08:00:00.000+08:00|                          0.0|
-|1970-01-01T08:00:00.001+08:00|                          1.0|
-|1970-01-01T08:00:00.002+08:00|                          2.0|
-|1970-01-01T08:00:00.003+08:00|                          3.0|
-|1970-01-01T08:00:00.004+08:00|                          4.0|
-|1970-01-01T08:00:00.005+08:00|                          5.0|
-|1970-01-01T08:00:00.006+08:00|                          6.0|
-|1970-01-01T08:00:00.007+08:00|                          7.0|
-|1970-01-01T08:00:00.008+08:00|                          8.0|
-|1970-01-01T08:00:00.009+08:00|                          9.0|
-|1970-01-01T08:00:00.010+08:00|                         10.0|
-+-----------------------------+-----------------------------+
+Example data: `root.ln.wf01.wt01.temperature`, `root.ln.wf01.wt01.st`, `root.ln.wf01.wt01.str` a total of `11` data.
+
+```
+IoTDB> select * from root.ln.wf01.wt01;
++-----------------------------+---------------------+--------------------+-----------------------------+
+|                         Time|root.ln.wf01.wt01.str|root.ln.wf01.wt01.st|root.ln.wf01.wt01.temperature|
++-----------------------------+---------------------+--------------------+-----------------------------+
+|1970-01-01T08:00:00.000+08:00|                  str|                10.0|                          0.0|
+|1970-01-01T08:00:00.001+08:00|                  str|                20.0|                          1.0|
+|1970-01-01T08:00:00.002+08:00|                  str|                30.0|                          2.0|
+|1970-01-01T08:00:00.003+08:00|                  str|                40.0|                          3.0|
+|1970-01-01T08:00:00.004+08:00|                  str|                50.0|                          4.0|
+|1970-01-01T08:00:00.005+08:00|                  str|                60.0|                          5.0|
+|1970-01-01T08:00:00.006+08:00|                  str|                70.0|                          6.0|
+|1970-01-01T08:00:00.007+08:00|                  str|                80.0|                          7.0|
+|1970-01-01T08:00:00.008+08:00|                  str|                90.0|                          8.0|
+|1970-01-01T08:00:00.009+08:00|                  str|               100.0|                          9.0|
+|1970-01-01T08:00:00.010+08:00|                  str|               110.0|                         10.0|
++-----------------------------+---------------------+--------------------+-----------------------------+
 ```
 Sql:
 ```sql
-select jexl(temperature, 'expr'='x -> {x + x}') as jexl1, jexl(temperature, 'expr'='x -> {x * 3}') as jexl2, jexl(temperature, 'expr'='x -> {x * x}') as jexl3, jexl(temperature, 'expr'='x -> {multiply(x, 100)}') as jexl4 from root.ln.wf01.wt01;```
+select jexl(temperature, 'expr'='x -> {x + x}') as jexl1, jexl(temperature, 'expr'='x -> {x * 3}') as jexl2, jexl(temperature, 'expr'='x -> {x * x}') as jexl3, jexl(temperature, 'expr'='x -> {multiply(x, 100)}') as jexl4, jexl(temperature, st, 'expr'='(x, y) -> {x + y}') as jexl5, jexl(temperature, st, str, 'expr'='(x, y, z) -> {x + y + z}') as jexl6 from root.ln.wf01.wt01;```
 ```
 
 Result:
 ```
-+-----------------------------+-----+-----+-----+------+
-|                         Time|jexl1|jexl2|jexl3| jexl4|
-+-----------------------------+-----+-----+-----+------+
-|1970-01-01T08:00:00.000+08:00|  0.0|  0.0|  0.0|   0.0|
-|1970-01-01T08:00:00.001+08:00|  2.0|  3.0|  1.0| 100.0|
-|1970-01-01T08:00:00.002+08:00|  4.0|  6.0|  4.0| 200.0|
-|1970-01-01T08:00:00.003+08:00|  6.0|  9.0|  9.0| 300.0|
-|1970-01-01T08:00:00.004+08:00|  8.0| 12.0| 16.0| 400.0|
-|1970-01-01T08:00:00.005+08:00| 10.0| 15.0| 25.0| 500.0|
-|1970-01-01T08:00:00.006+08:00| 12.0| 18.0| 36.0| 600.0|
-|1970-01-01T08:00:00.007+08:00| 14.0| 21.0| 49.0| 700.0|
-|1970-01-01T08:00:00.008+08:00| 16.0| 24.0| 64.0| 800.0|
-|1970-01-01T08:00:00.009+08:00| 18.0| 27.0| 81.0| 900.0|
-|1970-01-01T08:00:00.010+08:00| 20.0| 30.0|100.0|1000.0|
-+-----------------------------+-----+-----+-----+------+
++-----------------------------+-----+-----+-----+------+-----+--------+
+|                         Time|jexl1|jexl2|jexl3| jexl4|jexl5|   jexl6|
++-----------------------------+-----+-----+-----+------+-----+--------+
+|1970-01-01T08:00:00.000+08:00|  0.0|  0.0|  0.0|   0.0| 10.0| 10.0str|
+|1970-01-01T08:00:00.001+08:00|  2.0|  3.0|  1.0| 100.0| 21.0| 21.0str|
+|1970-01-01T08:00:00.002+08:00|  4.0|  6.0|  4.0| 200.0| 32.0| 32.0str|
+|1970-01-01T08:00:00.003+08:00|  6.0|  9.0|  9.0| 300.0| 43.0| 43.0str|
+|1970-01-01T08:00:00.004+08:00|  8.0| 12.0| 16.0| 400.0| 54.0| 54.0str|
+|1970-01-01T08:00:00.005+08:00| 10.0| 15.0| 25.0| 500.0| 65.0| 65.0str|
+|1970-01-01T08:00:00.006+08:00| 12.0| 18.0| 36.0| 600.0| 76.0| 76.0str|
+|1970-01-01T08:00:00.007+08:00| 14.0| 21.0| 49.0| 700.0| 87.0| 87.0str|
+|1970-01-01T08:00:00.008+08:00| 16.0| 24.0| 64.0| 800.0| 98.0| 98.0str|
+|1970-01-01T08:00:00.009+08:00| 18.0| 27.0| 81.0| 900.0|109.0|109.0str|
+|1970-01-01T08:00:00.010+08:00| 20.0| 30.0|100.0|1000.0|120.0|120.0str|
++-----------------------------+-----+-----+-----+------+-----+--------+
 Total line number = 11
-It costs 0.055s
+It costs 0.118s
 ```
 
 ### User Defined Timeseries Generating Functions
diff --git a/docs/zh/UserGuide/Query-Data/Select-Expression.md b/docs/zh/UserGuide/Query-Data/Select-Expression.md
index fe6374d347..5758f601fc 100644
--- a/docs/zh/UserGuide/Query-Data/Select-Expression.md
+++ b/docs/zh/UserGuide/Query-Data/Select-Expression.md
@@ -756,51 +756,51 @@ Java Expression Language (JEXL) 是一个表达式语言引擎。我们使用JEX
 
 | 函数名      | 可接收的输入序列类型                     | 必要的属性参数                               | 输出序列类型     | 功能类型                                             |
 |----------|--------------------------------|---------------------------------------|------------|--------------------------------------------------|
-| JEXL   | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | `expr`是一个标准的只有一个参数的lambda表达式,符合`x -> {...}`的格式,例如`x -> {x * 2}`| INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | 返回将输入的时间序列通过lambda表达式变换的序列             |
+| JEXL   | INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | `expr`是一个支持标准的一元或多元参数的lambda表达式,符合`x -> {...}`或`(x, y, z) -> {...}`的格式,例如`x -> {x * 2}`, `(x, y, z) -> {x + y * z}`| INT32 / INT64 / FLOAT / DOUBLE / TEXT / BOOLEAN | 返回将输入的时间序列通过lambda表达式变换的序列             |
 
 #### 演示
-测试数据:`root.ln.wf01.wt01.temperature`从`0.0-10.0`共`11`条数据。
-```
-IoTDB> select temperature from root.ln.wf01.wt01;
-+-----------------------------+-----------------------------+
-|                         Time|root.ln.wf01.wt01.temperature|
-+-----------------------------+-----------------------------+
-|1970-01-01T08:00:00.000+08:00|                          0.0|
-|1970-01-01T08:00:00.001+08:00|                          1.0|
-|1970-01-01T08:00:00.002+08:00|                          2.0|
-|1970-01-01T08:00:00.003+08:00|                          3.0|
-|1970-01-01T08:00:00.004+08:00|                          4.0|
-|1970-01-01T08:00:00.005+08:00|                          5.0|
-|1970-01-01T08:00:00.006+08:00|                          6.0|
-|1970-01-01T08:00:00.007+08:00|                          7.0|
-|1970-01-01T08:00:00.008+08:00|                          8.0|
-|1970-01-01T08:00:00.009+08:00|                          9.0|
-|1970-01-01T08:00:00.010+08:00|                         10.0|
-+-----------------------------+-----------------------------+
+测试数据:`root.ln.wf01.wt01.temperature`, `root.ln.wf01.wt01.st`, `root.ln.wf01.wt01.str`共`11`条数据。
+```
+IoTDB> select * from root.ln.wf01.wt01;
++-----------------------------+---------------------+--------------------+-----------------------------+
+|                         Time|root.ln.wf01.wt01.str|root.ln.wf01.wt01.st|root.ln.wf01.wt01.temperature|
++-----------------------------+---------------------+--------------------+-----------------------------+
+|1970-01-01T08:00:00.000+08:00|                  str|                10.0|                          0.0|
+|1970-01-01T08:00:00.001+08:00|                  str|                20.0|                          1.0|
+|1970-01-01T08:00:00.002+08:00|                  str|                30.0|                          2.0|
+|1970-01-01T08:00:00.003+08:00|                  str|                40.0|                          3.0|
+|1970-01-01T08:00:00.004+08:00|                  str|                50.0|                          4.0|
+|1970-01-01T08:00:00.005+08:00|                  str|                60.0|                          5.0|
+|1970-01-01T08:00:00.006+08:00|                  str|                70.0|                          6.0|
+|1970-01-01T08:00:00.007+08:00|                  str|                80.0|                          7.0|
+|1970-01-01T08:00:00.008+08:00|                  str|                90.0|                          8.0|
+|1970-01-01T08:00:00.009+08:00|                  str|               100.0|                          9.0|
+|1970-01-01T08:00:00.010+08:00|                  str|               110.0|                         10.0|
++-----------------------------+---------------------+--------------------+-----------------------------+
 ```
 sql:
 ```sql
-select jexl(temperature, 'expr'='x -> {x + x}') as jexl1, jexl(temperature, 'expr'='x -> {x * 3}') as jexl2, jexl(temperature, 'expr'='x -> {x * x}') as jexl3, jexl(temperature, 'expr'='x -> {multiply(x, 100)}') as jexl4 from root.ln.wf01.wt01;```
+select jexl(temperature, 'expr'='x -> {x + x}') as jexl1, jexl(temperature, 'expr'='x -> {x * 3}') as jexl2, jexl(temperature, 'expr'='x -> {x * x}') as jexl3, jexl(temperature, 'expr'='x -> {multiply(x, 100)}') as jexl4, jexl(temperature, st, 'expr'='(x, y) -> {x + y}') as jexl5, jexl(temperature, st, str, 'expr'='(x, y, z) -> {x + y + z}') as jexl6 from root.ln.wf01.wt01;```
 ```
 结果:
 ```
-+-----------------------------+-----+-----+-----+------+
-|                         Time|jexl1|jexl2|jexl3| jexl4|
-+-----------------------------+-----+-----+-----+------+
-|1970-01-01T08:00:00.000+08:00|  0.0|  0.0|  0.0|   0.0|
-|1970-01-01T08:00:00.001+08:00|  2.0|  3.0|  1.0| 100.0|
-|1970-01-01T08:00:00.002+08:00|  4.0|  6.0|  4.0| 200.0|
-|1970-01-01T08:00:00.003+08:00|  6.0|  9.0|  9.0| 300.0|
-|1970-01-01T08:00:00.004+08:00|  8.0| 12.0| 16.0| 400.0|
-|1970-01-01T08:00:00.005+08:00| 10.0| 15.0| 25.0| 500.0|
-|1970-01-01T08:00:00.006+08:00| 12.0| 18.0| 36.0| 600.0|
-|1970-01-01T08:00:00.007+08:00| 14.0| 21.0| 49.0| 700.0|
-|1970-01-01T08:00:00.008+08:00| 16.0| 24.0| 64.0| 800.0|
-|1970-01-01T08:00:00.009+08:00| 18.0| 27.0| 81.0| 900.0|
-|1970-01-01T08:00:00.010+08:00| 20.0| 30.0|100.0|1000.0|
-+-----------------------------+-----+-----+-----+------+
++-----------------------------+-----+-----+-----+------+-----+--------+
+|                         Time|jexl1|jexl2|jexl3| jexl4|jexl5|   jexl6|
++-----------------------------+-----+-----+-----+------+-----+--------+
+|1970-01-01T08:00:00.000+08:00|  0.0|  0.0|  0.0|   0.0| 10.0| 10.0str|
+|1970-01-01T08:00:00.001+08:00|  2.0|  3.0|  1.0| 100.0| 21.0| 21.0str|
+|1970-01-01T08:00:00.002+08:00|  4.0|  6.0|  4.0| 200.0| 32.0| 32.0str|
+|1970-01-01T08:00:00.003+08:00|  6.0|  9.0|  9.0| 300.0| 43.0| 43.0str|
+|1970-01-01T08:00:00.004+08:00|  8.0| 12.0| 16.0| 400.0| 54.0| 54.0str|
+|1970-01-01T08:00:00.005+08:00| 10.0| 15.0| 25.0| 500.0| 65.0| 65.0str|
+|1970-01-01T08:00:00.006+08:00| 12.0| 18.0| 36.0| 600.0| 76.0| 76.0str|
+|1970-01-01T08:00:00.007+08:00| 14.0| 21.0| 49.0| 700.0| 87.0| 87.0str|
+|1970-01-01T08:00:00.008+08:00| 16.0| 24.0| 64.0| 800.0| 98.0| 98.0str|
+|1970-01-01T08:00:00.009+08:00| 18.0| 27.0| 81.0| 900.0|109.0|109.0str|
+|1970-01-01T08:00:00.010+08:00| 20.0| 30.0|100.0|1000.0|120.0|120.0str|
++-----------------------------+-----+-----+-----+------+-----+--------+
 Total line number = 11
-It costs 0.055s
+It costs 0.118s
 ```
 
 ### 自定义时间序列生成函数
diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDTFBuiltinFunctionIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDTFBuiltinFunctionIT.java
index 7b1d66f395..e2bab16409 100644
--- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDTFBuiltinFunctionIT.java
+++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBUDTFBuiltinFunctionIT.java
@@ -916,6 +916,9 @@ public class IoTDBUDTFBuiltinFunctionIT {
       statement.execute("CREATE TIMESERIES root.sg.d7.s4 with datatype=TEXT,encoding=PLAIN");
       statement.execute("CREATE TIMESERIES root.sg.d7.s5 with datatype=BOOLEAN,encoding=PLAIN");
       statement.execute("CREATE TIMESERIES root.sg.d7.s6 with datatype=INT64,encoding=PLAIN");
+      statement.execute("CREATE TIMESERIES root.sg.d7.s7 with datatype=INT64,encoding=PLAIN");
+      statement.execute("CREATE TIMESERIES root.sg.d7.s8 with datatype=FLOAT,encoding=PLAIN");
+      statement.execute("CREATE TIMESERIES root.sg.d7.s9 with datatype=TEXT,encoding=PLAIN");
     } catch (SQLException throwable) {
       fail(throwable.getMessage());
     }
@@ -925,6 +928,9 @@ public class IoTDBUDTFBuiltinFunctionIT {
     String[] SQL_FOR_SAMPLE_4 = new String[5];
     String[] SQL_FOR_SAMPLE_5 = new String[5];
     String[] SQL_FOR_SAMPLE_6 = new String[5];
+    String[] SQL_FOR_SAMPLE_7 = new String[5];
+    String[] SQL_FOR_SAMPLE_8 = new String[5];
+    String[] SQL_FOR_SAMPLE_9 = new String[5];
     for (int i = 0; i < 5; i++) {
       SQL_FOR_SAMPLE_1[i] =
           String.format("insert into root.sg.d7(time, s1) values (%d, %d)", i, i + 1);
@@ -937,11 +943,20 @@ public class IoTDBUDTFBuiltinFunctionIT {
       SQL_FOR_SAMPLE_5[i] = String.format("insert into root.sg.d7(time, s5) values (%d, true)", i);
       SQL_FOR_SAMPLE_6[i] =
           String.format("insert into root.sg.d7(time, s6) values (%d, %d)", i, i + 8);
+      SQL_FOR_SAMPLE_7[i] =
+          String.format("insert into root.sg.d7(time, s7) values (%d, %d)", i, i + 1);
+      SQL_FOR_SAMPLE_8[i] =
+          String.format("insert into root.sg.d7(time, s8) values (%d, %f)", i, i + 1.0);
+      SQL_FOR_SAMPLE_9[i] =
+          String.format("insert into root.sg.d7(time, s9) values (%d, '%s')", i, "string");
     }
     double[] ANSWER1 = new double[] {2, 4, 6, 8, 10};
     double[] ANSWER2 = new double[] {2, 4, 6, 8, 10};
     double[] ANSWER3 = new double[] {4, 7, 10, 13, 16};
     String[] ANSWER4 = new String[] {"string2", "string2", "string2", "string2", "string2"};
+    double[] ANSWER7 = new double[] {1, 4, 9, 16, 25};
+    String[] ANSWER8 = new String[] {"string1", "string4", "string9", "string16", "string25"};
+    double[] ANSWER9 = new double[] {2, 9, 28, 65, 126};
     try (Connection connection = EnvFactory.getEnv().getConnection();
         Statement statement = connection.createStatement()) {
       for (int i = 0; i < 5; i++) {
@@ -951,6 +966,9 @@ public class IoTDBUDTFBuiltinFunctionIT {
         statement.execute(SQL_FOR_SAMPLE_4[i]);
         statement.execute(SQL_FOR_SAMPLE_5[i]);
         statement.execute(SQL_FOR_SAMPLE_6[i]);
+        statement.execute(SQL_FOR_SAMPLE_7[i]);
+        statement.execute(SQL_FOR_SAMPLE_8[i]);
+        statement.execute(SQL_FOR_SAMPLE_9[i]);
       }
     } catch (SQLException throwable) {
       fail(throwable.getMessage());
@@ -965,6 +983,9 @@ public class IoTDBUDTFBuiltinFunctionIT {
       String expr4 = "x -> {x + 2}";
       String expr5 = "x -> {x == true}";
       String expr6 = "x -> {x == x}";
+      String expr7 = "(x, y) -> {x * y}";
+      String expr8 = "(x, y, z) -> {x + y * z}";
+      String expr9 = "(x, y, z, a) -> {x * y * z + (a ? 1 : -1)}";
       ResultSet resultSet =
           statement.executeQuery(
               String.format(
@@ -973,7 +994,10 @@ public class IoTDBUDTFBuiltinFunctionIT {
                       + "%s(s3, 'expr'='%s'), "
                       + "%s(s4, 'expr'='%s'), "
                       + "%s(s5, 'expr'='%s'), "
-                      + "%s(s6, 'expr'='%s') "
+                      + "%s(s6, 'expr'='%s'), "
+                      + "%s(s7, s8, 'expr'='%s'), "
+                      + "%s(s4, s7, s1, 'expr'='%s'), "
+                      + "%s(s1, s7, s8, s5, 'expr'='%s') "
                       + "from root.sg.d7",
                   functionName,
                   expr1,
@@ -986,9 +1010,15 @@ public class IoTDBUDTFBuiltinFunctionIT {
                   functionName,
                   expr5,
                   functionName,
-                  expr6));
+                  expr6,
+                  functionName,
+                  expr7,
+                  functionName,
+                  expr8,
+                  functionName,
+                  expr9));
       int columnCount = resultSet.getMetaData().getColumnCount();
-      assertEquals(1 + 6, columnCount);
+      assertEquals(1 + 9, columnCount);
       for (int i = 0; i < 5; i++) {
         resultSet.next();
         assertEquals(ANSWER1[i], resultSet.getDouble(2), 0.01);
@@ -997,6 +1027,9 @@ public class IoTDBUDTFBuiltinFunctionIT {
         assertEquals(ANSWER4[i], resultSet.getString(5));
         assertTrue(resultSet.getBoolean(6));
         assertTrue(resultSet.getBoolean(7));
+        assertEquals(ANSWER7[i], resultSet.getDouble(8), 0.01);
+        assertEquals(ANSWER8[i], resultSet.getString(9));
+        assertEquals(ANSWER9[i], resultSet.getDouble(10), 0.01);
       }
     } catch (Exception e) {
       e.printStackTrace();
diff --git a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java
index af05530ec0..20a529e31d 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/udf/builtin/UDTFJexl.java
@@ -38,26 +38,30 @@ import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlScript;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 public class UDTFJexl implements UDTF {
 
+  private int inputSeriesNumber;
+  private TSDataType[] inputDataType;
   private TSDataType outputDataType;
   private JexlScript script;
   private Evaluator evaluator;
 
   @Override
   public void validate(UDFParameterValidator validator) throws UDFException {
-    validator
-        .validateInputSeriesNumber(1)
-        .validateInputSeriesDataType(
-            0,
-            TSDataType.INT32,
-            TSDataType.INT64,
-            TSDataType.FLOAT,
-            TSDataType.DOUBLE,
-            TSDataType.TEXT,
-            TSDataType.BOOLEAN)
-        .validateRequiredAttribute("expr");
+    inputSeriesNumber = validator.getParameters().getPaths().size();
+    for (int i = 0; i < inputSeriesNumber; i++) {
+      validator.validateInputSeriesDataType(
+          i,
+          TSDataType.INT32,
+          TSDataType.INT64,
+          TSDataType.FLOAT,
+          TSDataType.DOUBLE,
+          TSDataType.TEXT,
+          TSDataType.BOOLEAN);
+    }
+    validator.validateRequiredAttribute("expr");
   }
 
   @Override
@@ -68,38 +72,45 @@ public class UDTFJexl implements UDTF {
     JexlEngine jexl = new JexlBuilder().create();
     script = jexl.createScript(expr);
 
-    TSDataType inputDataType = parameters.getDataType(0);
-    outputDataType = probeOutputDataType(inputDataType);
-
-    switch (inputDataType) {
-      case INT32:
-        evaluator = new EvaluatorIntInput();
-        break;
-      case INT64:
-        evaluator = new EvaluatorLongInput();
-        break;
-      case FLOAT:
-        evaluator = new EvaluatorFloatInput();
-        break;
-      case DOUBLE:
-        evaluator = new EvaluatorDoubleInput();
-        break;
-      case TEXT:
-        evaluator = new EvaluatorStringInput();
-        break;
-      case BOOLEAN:
-        evaluator = new EvaluatorBooleanInput();
-        break;
-      default:
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            inputDataType,
-            TSDataType.INT32,
-            TSDataType.INT64,
-            TSDataType.FLOAT,
-            TSDataType.DOUBLE,
-            TSDataType.TEXT,
-            TSDataType.BOOLEAN);
+    inputDataType = new TSDataType[inputSeriesNumber];
+    for (int i = 0; i < inputSeriesNumber; i++) {
+      inputDataType[i] = parameters.getDataType(i);
+    }
+    outputDataType = probeOutputDataType();
+
+    if (inputSeriesNumber == 1) {
+      switch (inputDataType[0]) {
+        case INT32:
+          evaluator = new EvaluatorIntInput();
+          break;
+        case INT64:
+          evaluator = new EvaluatorLongInput();
+          break;
+        case FLOAT:
+          evaluator = new EvaluatorFloatInput();
+          break;
+        case DOUBLE:
+          evaluator = new EvaluatorDoubleInput();
+          break;
+        case TEXT:
+          evaluator = new EvaluatorStringInput();
+          break;
+        case BOOLEAN:
+          evaluator = new EvaluatorBooleanInput();
+          break;
+        default:
+          throw new UDFInputSeriesDataTypeNotValidException(
+              0,
+              inputDataType[0],
+              TSDataType.INT32,
+              TSDataType.INT64,
+              TSDataType.FLOAT,
+              TSDataType.DOUBLE,
+              TSDataType.TEXT,
+              TSDataType.BOOLEAN);
+      }
+    } else {
+      evaluator = new EvaluatorMulInput();
     }
 
     configurations
@@ -107,42 +118,28 @@ public class UDTFJexl implements UDTF {
         .setOutputDataType(outputDataType);
   }
 
-  private TSDataType probeOutputDataType(TSDataType inputDataType)
-      throws UDFInputSeriesDataTypeNotValidException, UDFOutputSeriesDataTypeNotValidException {
-    Object o;
-    // 23, 23L, 23f, 23d, "string", true are hard codes for probing
-    switch (inputDataType) {
-      case INT32:
-        o = script.execute(null, 23);
-        break;
-      case INT64:
-        o = script.execute(null, 23L);
-        break;
-      case FLOAT:
-        o = script.execute(null, 23f);
-        break;
-      case DOUBLE:
-        o = script.execute(null, 23d);
-        break;
-      case TEXT:
-        o = script.execute(null, "string");
-        break;
-      case BOOLEAN:
-        o = script.execute(null, true);
-        break;
-      default:
-        // This will not happen.
-        throw new UDFInputSeriesDataTypeNotValidException(
-            0,
-            inputDataType,
-            TSDataType.INT32,
-            TSDataType.INT64,
-            TSDataType.FLOAT,
-            TSDataType.DOUBLE,
-            TSDataType.TEXT,
-            TSDataType.BOOLEAN);
+  // 23, 23L, 23f, 23d, "string", true are hard codes for probing
+  private HashMap<TSDataType, Object> initialMap() {
+    HashMap<TSDataType, Object> map = new HashMap<TSDataType, Object>();
+    map.put(TSDataType.INT32, 23);
+    map.put(TSDataType.INT64, 23L);
+    map.put(TSDataType.FLOAT, 23f);
+    map.put(TSDataType.DOUBLE, 23d);
+    map.put(TSDataType.TEXT, "string");
+    map.put(TSDataType.BOOLEAN, true);
+    return map;
+  }
+
+  private TSDataType probeOutputDataType() throws UDFOutputSeriesDataTypeNotValidException {
+    // initial inputHardCodes to probe OutputDataType
+    HashMap<TSDataType, Object> map = initialMap();
+    Object[] inputHardCodes = new Object[inputSeriesNumber];
+    for (int i = 0; i < inputSeriesNumber; i++) {
+      inputHardCodes[i] = map.get(inputDataType[i]);
     }
 
+    Object o = script.execute(null, inputHardCodes);
+
     if (o instanceof Number) {
       return TSDataType.DOUBLE;
     } else if (o instanceof String) {
@@ -156,7 +153,8 @@ public class UDTFJexl implements UDTF {
 
   @Override
   public void transform(Row row, PointCollector collector)
-      throws IOException, UDFOutputSeriesDataTypeNotValidException, QueryProcessException {
+      throws IOException, UDFOutputSeriesDataTypeNotValidException, QueryProcessException,
+          UDFInputSeriesDataTypeNotValidException {
     switch (outputDataType) {
       case DOUBLE:
         evaluator.evaluateDouble(row, collector);
@@ -174,11 +172,14 @@ public class UDTFJexl implements UDTF {
   }
 
   private interface Evaluator {
-    void evaluateDouble(Row row, PointCollector collector) throws IOException;
+    void evaluateDouble(Row row, PointCollector collector)
+        throws IOException, UDFInputSeriesDataTypeNotValidException;
 
-    void evaluateText(Row row, PointCollector collector) throws IOException, QueryProcessException;
+    void evaluateText(Row row, PointCollector collector)
+        throws IOException, QueryProcessException, UDFInputSeriesDataTypeNotValidException;
 
-    void evaluateBoolean(Row row, PointCollector collector) throws IOException;
+    void evaluateBoolean(Row row, PointCollector collector)
+        throws IOException, UDFInputSeriesDataTypeNotValidException;
   }
 
   private class EvaluatorIntInput implements Evaluator {
@@ -294,4 +295,65 @@ public class UDTFJexl implements UDTF {
       collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getBoolean(0)));
     }
   }
+
+  private class EvaluatorMulInput implements Evaluator {
+
+    Object[] values = new Object[inputSeriesNumber];
+
+    @Override
+    public void evaluateDouble(Row row, PointCollector collector)
+        throws IOException, UDFInputSeriesDataTypeNotValidException {
+      getValues(row);
+      collector.putDouble(row.getTime(), ((Number) script.execute(null, values)).doubleValue());
+    }
+
+    @Override
+    public void evaluateText(Row row, PointCollector collector)
+        throws IOException, QueryProcessException, UDFInputSeriesDataTypeNotValidException {
+      getValues(row);
+      collector.putString(row.getTime(), (String) script.execute(null, values));
+    }
+
+    @Override
+    public void evaluateBoolean(Row row, PointCollector collector)
+        throws IOException, UDFInputSeriesDataTypeNotValidException {
+      getValues(row);
+      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, values));
+    }
+
+    public void getValues(Row row) throws IOException, UDFInputSeriesDataTypeNotValidException {
+      for (int i = 0; i < inputSeriesNumber; i++) {
+        switch (inputDataType[i]) {
+          case INT32:
+            values[i] = row.getInt(i);
+            break;
+          case INT64:
+            values[i] = row.getLong(i);
+            break;
+          case FLOAT:
+            values[i] = row.getFloat(i);
+            break;
+          case DOUBLE:
+            values[i] = row.getDouble(i);
+            break;
+          case TEXT:
+            values[i] = row.getString(i);
+            break;
+          case BOOLEAN:
+            values[i] = row.getBoolean(i);
+            break;
+          default:
+            throw new UDFInputSeriesDataTypeNotValidException(
+                i,
+                inputDataType[i],
+                TSDataType.INT32,
+                TSDataType.INT64,
+                TSDataType.FLOAT,
+                TSDataType.DOUBLE,
+                TSDataType.TEXT,
+                TSDataType.BOOLEAN);
+        }
+      }
+    }
+  }
 }