すべてのプロダクト
Search
ドキュメントセンター

MaxCompute:コード埋め込みUDF

最終更新日:Jan 06, 2025

このトピックでは、コード埋め込みユーザー定義関数 (UDF) を使用して、JavaまたはPythonコードをSQLスクリプトに埋め込む方法について説明します。

背景情報

MaxComputeのコード埋め込みUDFは、コードの実装とメンテナンスにおける次の問題を解決します。

  • 複雑なコードの実装: UDFを作成してコードを開発した後、Javaでコードをコンパイルし、リソースと関数を作成する必要があります。

  • 不便なコードメンテナンス: SQLスクリプトで参照されているUDFの実装ロジックを直接表示したり、JARパッケージのソースコードを取得したりすることはできません。

  • コードの読みやすさが悪い: ユーザー定義型 (UDT) を使用してJavaライブラリ関数を実装するには、Javaコードを長いコード行の式に変換する必要があります。 さらに、Javaコードを式として記述できない場合があります。 例:

    Foo f = new Foo();
    f.execute();
    f.getResult();

説明

コード埋め込みUDFを使用すると、JavaまたはPythonコードをSQLスクリプトに埋め込むことができます。 スクリプトをコンパイルすると、Janinoコンパイラは埋め込みコードを識別して抽出し、Javaでコードをコンパイルしてから、リソースを動的に生成し、一時関数を作成します。

SQLスクリプトとサードパーティのコード行を同じソースコードファイルに配置できます。 これにより、UDTまたはUDFの使用が簡単になり、日々の開発とメンテナンスが容易になります。

制限事項

組み込みJavaコードのコンパイルには、Janinoコンパイラのみを使用できます。 埋め込みJavaコードの構文は、標準JDK構文のサブセットでなければなりません。 埋め込みJavaコードには次の制限があります。

  • Lambda式はサポートされていません。

  • 1つのcatchブロックに複数の種類の例外を指定することはできません。 たとえば、catch(Exception1 | Exception2 e) は使用できません。

  • ジェネリック引数を自動的に推論することはできません。 たとえば、Map map = new HashMap<>(); はサポートされていません。

  • 型引数の推論の式は無視されます。 (String) myMap.get(key) などの引数型を指定するには、キャスト式を使用する必要があります。

  • Java仮想マシン (JVM) の -eaオプションが使用されている場合でも、アサーションは強制的に有効になります。

  • Java 8以降のバージョンでプログラムされているコードはサポートされていません。

UDTの参照埋め込みコード

次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。

SELECT 
  s, 
  com.mypackage.Foo.extractNumber(s) 
FROM VALUES ('abc123def'),('apple') AS t(s);

#CODE ('lang'='JAVA')
package com.mypackage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Foo {
  final static Pattern compile = Pattern.compile(".*?([0-9]+).*");
  public static String extractNumber(String input) {
    final Matcher m = compile.matcher(input);
    if (m.find()) {
      return m.group(1);
    }
    return null;
  }
}
#END CODE;
  • #CODEは、埋め込みコードブロックの先頭を示します。 #END CODEは、埋め込みコードブロックの終わりを示します。 この例では、埋め込みコードブロックはスクリプトの最後に配置され、スクリプト全体に適用されます。

  • 'lang'='JAVAは、埋め込みコードがJavaであることを示します。 PYTHONでコードをコンパイルする場合、JAVAはPythonに置き換えることができます。

  • SQLスクリプトでUDT構文を使用して、Foo.extractNumberを呼び出すことができます。

Javaコード埋め込みUDFの定義と呼び出し

次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。

CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING
#CODE ('lang'='JAVA')
package com.mypackage;
import com.aliyun.odps.udf.UDF;
public class Reverse extends UDF {
  public String evaluate(String input) {
    if (input == null) return null;
    StringBuilder ret = new StringBuilder();
    for (int i = input.toCharArray().length - 1; i >= 0; i--) {
      ret.append(input.toCharArray()[i]);
    }
    return ret.toString();
  }
}
#END CODE;

SELECT foo('abdc');
  • 埋め込みコードブロックは、USINGの後、またはスクリプトの最後に配置できます。 USINGの後に配置されたコードブロックは、CREATE TEMPORARY FUNCTIONステートメントにのみ適用されます。

  • CREATE TEMPORARY functionによって作成された関数は一時的な関数です。 この一時関数は、現在の実行プロセス中にのみ有効になり、MaxComputeメタデータシステムには保存されません。 永続関数を作成してMaxComputeメタデータシステムに保存する方法の詳細については、「create SQL function」をご参照ください。

Javaコード埋め込みUDTFの定義と呼び出し

次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。

CREATE TEMPORARY FUNCTION foo AS 'com.mypackage.Reverse' USING 
#CODE ('lang'='JAVA', 'filename'='embedded.jar')
package com.mypackage;

import com.aliyun.odps.udf.UDTF;
import com.aliyun.odps.udf.UDFException;
import com.aliyun.odps.udf.annotation.Resolve;

@Resolve({"string->string,string"})
public class Reverse extends UDTF {
  @Override
  public void process(Object[] objects) throws UDFException {
    String str = (String) objects[0];
    String[] split = str.split(",");
    forward(split[0], split[1]);
  }
}

#END CODE;

SELECT foo('ab,dc') AS (a,b);

@ Resolveの戻り値は、string[] 型である必要があります。 ただし、Janinoコンパイラは、"string->string,string"string[] として識別できません。 この問題に対処するには、@ Resolveによってコメントされたパラメーターを {} で囲む必要があります。 一般的な方法を使用してJava UDTFを作成する場合、中括弧 {} は必要ありません。

Pythonコード埋め込みUDFの定義と呼び出し

次のコードは例を提供します。 SQL文をスクリプトモードで送信する必要があります。 詳細については、「スクリプトモードのSQL」をご参照ください。

CREATE TEMPORARY FUNCTION foo AS 'embedded.UDFTest' USING
#CODE ('lang'='PYTHON', 'filename'='embedded')
from odps.udf import annotate
@annotate("bigint->bigint")
class UDFTest(object):
  def evaluate(self, a):
    return a * a
#END CODE;

SELECT foo(4);
  • Pythonコードのインデントは、Python言語の仕様に準拠している必要があります。

  • Python UDFを作成するときは、AS句に続くクラス名にPythonソースコードのファイル名を含める必要があります。 'filename'='embedded' を使用して、仮想ファイル名を指定できます。

  • さまざまなバージョンのPythonの開発と使用の詳細については、次のトピックを参照してください。