Cette rubrique explique comment développer, enregistrer et utiliser des fonctions utilisateur définies renvoyant des tables (UDTF) en Python.
Définition
Une fonction utilisateur définie renvoyant des tables (UDTF) accepte zéro, une ou plusieurs valeurs scalaires en tant que paramètres d'entrée, qui peuvent être de longueur variable. Contrairement à une fonction scalaire, une UDTF peut retourner un nombre quelconque de lignes au lieu d'une seule valeur. Chaque ligne retournée peut contenir une ou plusieurs colonnes, et chaque appel à une UDTF peut générer plusieurs lignes ou colonnes.
Limites
En raison des environnements de déploiement et réseau de Realtime Compute for Apache Flink, tenez compte des limites suivantes lors du développement de fonctions utilisateur définies en Python :
Seules les versions open source de Flink 1.12 et ultérieures sont prises en charge.
Python est préinstallé dans l'espace de travail Flink. Développez votre code avec la version de Python préinstallée.
Remarque
Le moteur Realtime Compute VVR antérieur à la version 8.0.11 est livré avec Python 3.7.9 préinstallé, tandis que VVR 8.0.11 et les versions ultérieures incluent Python 3.9.21. Si vous mettez à niveau une version antérieure vers VVR 8.0.11 ou une version ultérieure, vous devez retester, déployer et exécuter les jobs PyFlink développés dans la version précédente.
L'environnement d'exécution Flink prend uniquement en charge JDK 8 et JDK 11. Si un job Python dépend d'un package JAR tiers, assurez-vous que le package JAR est compatible.
Seule la version open source Scala 2.11 est prise en charge. Si un job Python dépend d'un package JAR tiers, utilisez le package JAR correspondant à Scala 2.11.
Les fonctions inline ne sont prises en charge qu'à partir de VVR 11.9.0.
Développer une UDTF
Vous pouvez développer une fonction Python de l'une des manières suivantes :
Empaqueter et télécharger le code Python, puis enregistrer le code sur la plateforme.
Déclarer la logique du code en tant que fonction inline dans un script SQL.
Si la logique de l'UDTF est simple, développez la fonction Python en tant que fonction inline.
Développer une UDTF à l'aide d'un package de code
Développer une UDTF
Remarque
Flink fournit un exemple de projet d'extensions utilisateur définies (UDX) en Python pour vous aider à développer des UDX. L'exemple inclut les implémentations de fonctions scalaires utilisateur définies, de fonctions d'agrégation utilisateur définies (UDAF) et d'UDTF. Cet exemple utilise le système d'exploitation Windows pour expliquer comment développer une UDTF.
Important
Un nom de UDF enregistré ne peut contenir que des lettres minuscules, des chiffres et des traits d'union (-). Les traits de soulignement (\_) et autres caractères spéciaux ne sont pas pris en charge.
Le nom de la fonction ne doit pas être identique à celui d'une fonction intégrée. L'environnement géré de Realtime Compute for Apache Flink comprend certaines fonctions intégrées préinstallées, telles que split. Si une fonction personnalisée porte le même nom qu'une fonction intégrée, l'enregistrement échoue ou la validation SQL signale une erreur. Utilisez un préfixe personnalisé pour le nom de la fonction, par exemple my-split.
Téléchargez et décompressez le package d'exemple python\_demo-master sur votre ordinateur.
Dans PyCharm, choisissez file > open pour ouvrir le dossier python\_demo-master décompressé.
Double-cliquez sur \python\_demo-master\udx\udtfs.py et modifiez le contenu du fichier udtfs.py en fonction de vos besoins métier.
Dans cet exemple, my\_split divise la chaîne de chaque ligne en plusieurs colonnes à l'aide de barres verticales (|).
from pyflink.table import DataTypes
from pyflink.table.udf import udtf
@udtf(result_types=[DataTypes.STRING(), DataTypes.STRING()])
def my_split(s: str):
splits = s.split("|")
yield splits[0], splits[1]
Dans le répertoire contenant le dossier udx du package téléchargé (\python\_demo), exécutez la commande suivante pour empaqueter les fichiers.
zip -r python_demo.zip udx
Le package python\_demo.zip est généré dans le répertoire \python\_demo\, ce qui indique que l'UDTF est développée.
Enregistrer une UDTF
Pour plus d'informations sur l'enregistrement d'une UDTF, consultez Gérer les UDF.
Utiliser une UDTF
Une fois l'UDTF enregistrée, vous pouvez l'utiliser. Procédez comme suit :
Développez un job Flink SQL. Pour plus d'informations, consultez Présentation du développement de jobs.
La valeur du champ message de chaque ligne de la table ASI\_UDTF\_Source est concaténée avec la chaîne aa à l'aide d'une barre verticale (|), puis divisée en plusieurs colonnes à l'aide de barres verticales (|). Le code suivant illustre cette opération :
CREATE TEMPORARY TABLE ASI_UDTF_Source (
`message` VARCHAR
) WITH (
'connector'='datagen'
);
CREATE TEMPORARY TABLE ASI_UDTF_Sink (
name VARCHAR,
place VARCHAR
) WITH (
'connector' = 'blackhole'
);
INSERT INTO ASI_UDTF_Sink
SELECT name,place
FROM ASI_UDTF_Source,lateral table(my_split(concat_ws('|', `message`, 'aa'))) as T(name,place);
Sur la page O&M > Deployments, cliquez sur Start dans la colonne Actions du job cible.
Une fois le job démarré, la table ASI\_UDTF\_Sink contient deux colonnes. Les données sont générées en concaténant les champs message et aa de chaque ligne de la table ASI\_UDTF\_Source avec une barre verticale (|), puis en divisant le résultat à l'aide de barres verticales (|).
Fonctions de table inline Python
Une fonction inline intègre l'implémentation de la fonction directement dans une instruction CREATE FUNCTION. La définition de la fonction et la déclaration d'enregistrement sont effectuées dans la même instruction SQL. L'exemple suivant définit une fonction inline qui masque les adresses e-mail. Vous devez déclarer la logique complète du code entre $$.
CREATE TEMPORARY FUNCTION parse_order_items(items_json STRING)
RETURNS TABLE (
sku STRING,
quantity INT
)
HANDLER 'ParseOrderItems'
AS $$
import json
class ParseOrderItems:
def eval(self, items_json):
if not items_json:
return
try:
items = json.loads(items_json)
except (TypeError, ValueError):
return
if not isinstance(items, list):
return
for item in items:
if not isinstance(item, dict):
continue
sku = str(item.get("sku", "")).strip().upper()
try:
quantity = int(item.get("quantity"))
except (TypeError, ValueError):
continue
if sku and quantity > 0:
yield sku, quantity
$$
LANGUAGE PYTHON;
Python est sensible à l'indentation. Écrivez le premier niveau du corps de la fonction au début d'une ligne et assurez-vous que l'indentation relative à l'intérieur du bloc de code est correcte.
Les fonctions inline ne prennent pas en charge l'exécution vectorisée et ne peuvent pas être déclarées comme des fonctions non déterministes.
Seules les fonctions TEMPORARY peuvent être créées.