Tous les produits
Search
Centre de documentation

MaxCompute:Exemple : Utiliser des types de données complexes dans les UDF

Dernière mise à jour :Aug 10, 2026

Lorsque vous écrivez une UDF qui traite des données structurées, vous devez faire correspondre les types de données complexes MaxCompute — ARRAY, MAP et STRUCT — aux types Java ou Python équivalents dans votre code de gestionnaire. Ce tutoriel explique comment implémenter, déployer et appeler une UDF qui convertit des horodatages pour ces trois types de données complexes.

Les UDF Java prennent en charge la surcharge de méthodes : une seule UDF peut donc gérer les trois types. Les UDF Python nécessitent en revanche une fonction distincte pour chaque type.

Prérequis

Avant de commencer, assurez-vous de disposer des éléments suivants :

  • Un projet MaxCompute

  • Un environnement de développement Java avec Maven, ou un environnement Python 3

Correspondances de types

Chaque type de données complexe MaxCompute correspond à un type Java ou Python spécifique dans le gestionnaire de votre UDF :

Type MaxCompute

Type Java

Type Python

ARRAY

java.util.List

list

MAP

java.util.Map

dict

STRUCT

com.aliyun.odps.data.Struct

collections.namedtuple

Le type STRUCT requiert l'annotation @Resolve en Java. La réflexion ne permettant pas de lire les noms et les types de champs depuis com.aliyun.odps.data.Struct , cette annotation fournit les informations nécessaires au moment de la compilation. L'annotation n'affecte que les méthodes surchargées dont les paramètres d'entrée ou la valeur de retour incluent Struct .

Étape 1 : Rédiger l'UDF

UDF Java

Les trois méthodes evaluate de la classe Java partagent le même nom UDF_COMPLEX_DATA. MaxCompute distribue les appels en fonction du type d'argument, ce qui permet à chaque méthode de traiter un type complexe spécifique.

Les signatures UDF en SQL correspondent directement aux signatures de méthode 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)

Ajoutez la dépendance suivante au fichier 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;
    }
}

Chaque méthode evaluate renvoie null lorsque son entrée est null. MaxCompute ne garantissant pas l'ordre d'évaluation des sous-expressions SQL, les vérifications de nullité au sein de l'UDF constituent le moyen fiable de gérer les entrées nulles.

Pour connaître les autres exigences liées au code, consultez la rubrique UDF Java.

UDF Python

Les UDF Python ne prenant pas en charge la surcharge de méthodes, chaque type complexe nécessite une UDF distincte portant son propre nom.

Les projets MaxCompute exécutent Python 2 par défaut. Pour utiliser Python 3, exécutez la commande set odps.sql.python.version=cp37 au niveau de la session avant d'appeler une UDF Python 3.

Pour plus d'informations sur les autres exigences relatives aux UDF Python 3, consultez la rubrique UDF Python 3.

UDF_COMPLEX_DATA_ARRAY — gère les entrées de type 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 — gère les entrées de type 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 — gère les entrées de type 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

Étape 2 : Télécharger les ressources et créer l'UDF

Une fois le code de votre UDF rédigé et débogué, téléchargez-le sur MaxCompute et enregistrez l'UDF.

Étape 3 : Appeler l'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));

Résultat attendu :

+---------------------------------------------+
| _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));

Résultat attendu :

+----------------------------------------------------------------+
| _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));

Résultat attendu :

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

Voir aussi

  • UDF Java — Référence complète des spécifications des UDF Java

  • UDF Python 3 — Spécifications et limites des UDF Python 3