Todos os produtos
Search
Central de documentação

MaxCompute:Exemplo: Uso de tipos de dados complexos em UDFs

Última atualização: Jun 26, 2026

Ao desenvolver uma UDF para processar dados estruturados, mapeie os tipos complexos do MaxCompute — ARRAY, MAP e STRUCT — para os tipos correspondentes em Java ou Python no código do manipulador. Este tutorial demonstra como implementar, implantar e invocar uma UDF que converte timestamps nesses três tipos complexos.

UDFs em Java aceitam sobrecarga de métodos, permitindo que um único nome de UDF atenda aos três tipos. Já as UDFs em Python exigem uma função distinta para cada tipo.

Pré-requisitos

Antes de começar, verifique se você possui:

  • Um projeto do MaxCompute

  • Um ambiente de desenvolvimento Java com Maven ou um ambiente Python 3

Mapeamento de tipos

Cada tipo de dado complexo do MaxCompute corresponde a um tipo específico em Java ou Python no manipulador da UDF:

Tipo do MaxCompute

Tipo em Java

Tipo em Python

ARRAY

java.util.List

list

MAP

java.util.Map

dict

STRUCT

com.aliyun.odps.data.Struct

collections.namedtuple

O tipo STRUCT exige a anotação @Resolve em Java. Como a reflexão não lê nomes e tipos de campos diretamente de com.aliyun.odps.data.Struct , essa anotação fornece as informações necessárias durante a compilação. Ela afeta apenas métodos sobrecarregados cujos parâmetros de entrada ou valores de retorno incluam Struct .

Etapa 1: Escrever a UDF

UDF em Java

Os três métodos evaluate na classe Java compartilham o nome UDF_COMPLEX_DATA. O MaxCompute direciona as chamadas com base no tipo do argumento, de modo que cada método processe um tipo complexo diferente.

As assinaturas das UDFs em SQL correspondem diretamente às assinaturas dos métodos em Java:

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(struct<input_name:string,input_timestamp:bigint> st)

Adicione a seguinte dependência ao arquivo pom.xml:

<dependency>
  <groupId>com.aliyun.odps</groupId>
  <artifactId>odps-sdk-udf</artifactId>
  <version>0.29.10-public</version>
</dependency>
package com.aliyun; // Specify a package name.

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

    /**
     * Convert a list of timestamps into a list of time strings.
     * @param timestamps Enter a list of timestamps.
     * @return Obtain a list of time strings.
     */
    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;
    }

    /**
     * Convert timestamps of the MAP data type into time strings of the MAP data type.
     * @param timestamps Enter data of the MAP data type in which values are timestamps.
     * @return Obtain a list of time strings of the MAP data type.
     */
    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;
    }

    /**
     * Convert a timestamp into a time string.
     * @param input Enter a timestamp of the STRUCT data type.
     * @return Obtain a time string of the STRUCT data type.
     */
    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;
    }
}

Cada método evaluate retorna null quando sua entrada é null. O MaxCompute não garante a ordem de avaliação das subexpressões SQL; portanto, verificar valores nulos dentro da UDF é a maneira confiável de lidar com entradas nulas.

Para outros requisitos de código, consulte UDFs em Java.

UDF em Python

UDFs em Python não suportam sobrecarga de métodos, então cada tipo complexo requer uma UDF separada com seu próprio nome.

Projetos do MaxCompute executam Python 2 por padrão. Para usar Python 3, execute set odps.sql.python.version=cp37 no nível da sessão antes de chamar uma UDF em Python 3.

Para outros requisitos de UDF em Python 3, consulte UDFs em Python 3.

UDF_COMPLEX_DATA_ARRAY — processa entradas do tipo ARRAY<BIGINT>:

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

UDF_COMPLEX_DATA_MAP — processa entradas do tipo MAP<STRING, BIGINT>:

from odps.udf import annotate
import datetime
@annotate('map<string,bigint>->map<string,datetime>')
class MapExample:
    def evaluate(self, input_dict):
        output_dict = dict()
        for key in input_dict:
            value = input_dict[key]
            t = datetime.datetime.fromtimestamp(value)
            output_dict[key] = t
        return output_dict

UDF_COMPLEX_DATA_STRUCT — processa entradas do tipo STRUCT<input_name:STRING, input_timestamp:BIGINT>:

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

Etapa 2: Carregar recursos e criar a UDF

Após escrever e depurar o código da UDF, carregue-o no MaxCompute e registre a UDF.

Etapa 3: Invocar a UDF

ARRAY

-- Java UDF
SELECT UDF_COMPLEX_DATA(array(1554047999, 1554047989));

-- Python UDF (enable Python 3 first)
set odps.sql.python.version=cp37;
SELECT UDF_COMPLEX_DATA_ARRAY(array(1554047999, 1554047989));

Saída esperada:

+---------------------------------------------+
| _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 (enable Python 3 first)
set odps.sql.python.version=cp37;
SELECT UDF_COMPLEX_DATA_MAP(map('date1', 1554047989, 'date2', 1554047999));

Saída esperada:

+----------------------------------------------------------------+
| _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 (enable Python 3 first)
set odps.sql.python.version=cp37;
SELECT UDF_COMPLEX_DATA_STRUCT(struct('date', 1554047989));

Saída esperada:

+-------------------------------------------------------------+
| _c0                                                         |
+-------------------------------------------------------------+
| {"output_name":"date","output_time":"2019-03-31 23:59:49"}  |
+-------------------------------------------------------------+

Veja também

  • UDFs em Java — referência completa das especificações de UDFs em Java

  • UDFs em Python 3 — especificações e limitações de UDFs em Python 3