本文為您介紹如何在Java UDF和Python UDF中使用複雜資料類型。
命令說明
本樣本將註冊一個名稱為UDF_COMPLEX_DATA的自訂函數。
本樣本將介紹array、map、struct三種複雜資料類型的使用。Java UDF通過重載的方式使用同一個自訂函數名。Python UDF需要分別註冊UDF_COMPLEX_DATA_ARRAY、UDF_COMPLEX_DATA_MAP和UDF_COMPLEX_DATA_STRUCT實現上述三種資料類型的使用。
命令格式:
array<string> UDF_COMPLEX_DATA(array<bigint> <as>) map<string, string> UDF_COMPLEX_DATA(map<string,bigint> <ms>) struct<output_name:string,output_time:string> UDF_COMPLEX_DATA(<input_name:string,input_timestamp:bigint> <st>)
命令功能:
將輸入的時間戳記轉換成yyyy-MM-dd HH:mm:ss格式的時間字串。其中入參分別使用到array、map、struct複雜資料類型。
參數說明:
as:ARRAY<BIGINT>類型,時間戳記列表,必填。
ms:MAP<STRING, BIGINT>類型,其中value是時間戳記,必填。
st:STRUCT類型,其中FIELD為input_timestamp的值是時間戳記。
開發和使用步驟
1. 代碼開發
Java UDF 程式碼範例
package com.aliyun; // package名稱,可以根據您的情況定義
import com.aliyun.odps.data.Struct;
import com.aliyun.odps.udf.UDF;
import com.aliyun.odps.udf.annotation.Resolve;
import java.text.SimpleDateFormat;
import java.util.*;
@Resolve("struct<input_name:string, input_timestamp:bigint>->map<string,string>")
public class ComplexDataTypeExample extends UDF{
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 將時間戳記列錶轉換為時間字串列表
* @param timestamps 時間戳記列表
* @return 時間字串列表
*/
public List<String> evaluate(List<Long> timestamps) {
if (timestamps == null) {
return null;
}
List<String> result = new ArrayList<>();
SimpleDateFormat formatter = new SimpleDateFormat(PATTERN);
for (Long timestamp : timestamps) {
Date date = new Date(timestamp < 9999999999L ? timestamp * 1000 : timestamp);
String dateString = formatter.format(date);
result.add(dateString);
}
return result;
}
/**
* 將時間戳記map轉換為時間字串map
* @param timestamps 時間戳記map,其中value為時間戳記
* @return 時間字串map
*/
public Map<String, String> evaluate(Map<String, Long> timestamps) {
if (timestamps == null) {
return null;
}
Map<String, String> result = new HashMap<>(timestamps.size());
SimpleDateFormat formatter = new SimpleDateFormat(PATTERN);
for (String key : timestamps.keySet()) {
Long timestamp = timestamps.get(key);
Date date = new Date(timestamp < 9999999999L ? timestamp * 1000 : timestamp);
String dateString = formatter.format(date);
result.put(key, dateString);
}
return result;
}
/**
* 將時間戳記轉換為時間字串
* @param input 時間戳記Struct
* @return 時間字串Struct
*/
public Map<String, String> evaluate(Struct input) {
if (input == null) {
return null;
}
SimpleDateFormat formatter = new SimpleDateFormat(PATTERN);
String nameValue = (String) input.getFieldValue("input_name");
Long timestampValue = (Long) input.getFieldValue("input_timestamp");
Date date = new Date(timestampValue < 9999999999L ? timestampValue * 1000 : timestampValue);
String dateString = formatter.format(date);
Map<String, String> result = new HashMap<>(8);
result.put("output_name", nameValue);
result.put("output_time", dateString);
return result;
}
}pom.xml需要引入的依賴如下:
<dependency>
<groupId>com.aliyun.odps</groupId>
<artifactId>odps-sdk-udf</artifactId>
<version>0.29.10-public</version>
</dependency>範例程式碼中,定義了3個重載的evaluate方法。其中:
第一個用ARRAY作為參數,ARRAY對應java.util.List。
第二個用MAP作為參數,MAP對應java.util.Map。
第三個用STRUCT作為參數,STRUCT對應com.aliyun.odps.data.Struct。
說明com.aliyun.odps.data.Struct無法通過反射分析擷取到field name和field type,需要輔助使用
@Resolve annotation,即如果您需要在UDF中使用STRUCT,要求在UDF class上也標註上@Resolve註解,該註解只會影響參數或傳回值中包含com.aliyun.odps.data.Struct的重載。
使用Java語言編寫UDF代碼必須繼承UDF類,本例中evaluate方法定義了三個入參,類型分別為List、Map和Struct,且其對應的傳回值類型也分別為List、Map和Map。輸入參數和傳回值的資料類型將作為SQL語句中UDF的函數簽名Signature,其他代碼規範和要求請參考:UDF開發規範與通用流程(Java)。
Python3 UDF 程式碼範例
下面樣本中輸入資料類型為map<string,bigint>,該範例程式碼在本文中將註冊為自訂函數:UDF_COMPLEX_DATA_MAP。
from odps.udf import annotate import datetime @annotate('map<string,bigint>->map<string,datetime>') class MapExample: def evaluate(self, intput_dict): output_dict = dict() for key in intput_dict: value = intput_dict[key] t = datetime.datetime.fromtimestamp(value) output_dict[key] = t return output_dict下面樣本中輸入資料類型為array<bigint>,該範例程式碼在本文中將註冊為自訂函數:UDF_COMPLEX_DATA_ARRAY。
from odps.udf import annotate import datetime @annotate('array<bigint>->array<datetime>') class ArrayExample: def evaluate(self, input_list): output_list = list() for item in input_list: t = datetime.datetime.fromtimestamp(item) output_list.append(t) return output_list下面樣本中輸入資料類型為struct<input_name:string,input_timestamp:bigint>,該範例程式碼在本文中將註冊為自訂函數:UDF_COMPLEX_DATA_STRUCT。
from odps.udf import annotate import datetime, collections @annotate('struct<input_name:string,input_timestamp:bigint>->struct<output_name:string,output_time:datetime>') class StructExample: def evaluate(self, input_namedtuple): OutputNamedTuple = collections.namedtuple('output_namedtuple', ['output_name', 'output_time']) name_val = input_namedtuple.input_name time_val = datetime.datetime.fromtimestamp(input_namedtuple.input_timestamp) output_namedtuple = OutputNamedTuple(name_val, time_val) return output_namedtuple
MaxCompute預設使用Python 2,可以在Session層級使用命令set odps.sql.python.version=cp37開啟Python 3。更多python3 UDF規範請參考:UDF開發規範與通用流程(Python3)。
2. 上傳資源和註冊函數
完成UDF代碼開發和調試之後,將資源上傳至MaxCompute並註冊函數,本樣本Java UDF註冊函數名:UDF_COMPLEX_DATA (Python UDF需要分別註冊函數名:UDF_COMPLEX_DATA_ARRAY、UDF_COMPLEX_DATA_MAP和UDF_COMPLEX_DATA_STRUCT)。Java UDF上傳資源與註冊函數詳情步驟請參見:打包、上傳及註冊,Python UDF請參見:上傳及註冊。
3. 使用樣本
將時間戳記(Array類型)轉換為時間字串,命令如下:
-- Java UDF調用 SELECT UDF_COMPLEX_DATA(array(1554047999, 1554047989)); -- Python UDF調用 set odps.sql.python.version=cp37; -- python3 UDF需要使用該命令開啟python3 SELECT UDF_COMPLEX_DATA_ARRAY(array(1554047999, 1554047989));執行結果如下:
+---------------------------------------------+ | _c0 | +---------------------------------------------+ | [2019-03-31 23:59:59, 2019-03-31 23:59:49] | +---------------------------------------------+將時間戳記(Map類型)轉換為時間字串,命令如下:
-- Java UDF調用 SELECT UDF_COMPLEX_DATA(map('date1', 1554047989, 'date2', 1554047999)); -- Python UDF調用 set odps.sql.python.version=cp37; -- python3 UDF需要使用該命令開啟python3 SELECT UDF_COMPLEX_DATA_MAP(map('date1', 1554047989, 'date2', 1554047999));執行結果如下:
+----------------------------------------------------------------+ | _c0 | +----------------------------------------------------------------+ | {"date1":"2019-03-31 23:59:49","date2":"2019-03-31 23:59:59"} | +----------------------------------------------------------------+將時間戳記(Struct類型)轉換為時間字串,命令如下
-- Java UDF調用 SELECT UDF_COMPLEX_DATA(struct('date', 1554047989)); -- Python UDF調用 set odps.sql.python.version=cp37; -- python3 UDF需要使用該命令開啟python3 SELECT UDF_COMPLEX_DATA_STRUCT(struct('date', 1554047989));執行結果如下:
+-------------------------------------------------------------+ | _c0 | +-------------------------------------------------------------+ | {"output_name":"date","output_time":"2019-03-31 23:59:49"} | +-------------------------------------------------------------+