ユーザー定義関数を使用したクエリ
Amazon Athena のユーザー定義関数 (UDF) を使用すると、レコードまたはレコードのグループを処理するためのカスタム関数を作成できます。UDF は、パラメータを受け入れ、作業を実行し、結果を返します。
Athena で UDF を使用するには、SQL クエリの USING EXTERNAL FUNCTION
ステートメントの前に SELECT
句を記述します。この SELECT
ステートメントは UDF を参照し、クエリの実行時に、UDF に渡される変数を定義します。SQL クエリは UDF を呼び出すときに、Java ランタイムを使用して Lambda 関数を呼び出します。UDF は、Java デプロイパッケージのメソッドとして Lambda 関数内で定義されます。Lambda 関数の同じ Java デプロイパッケージで複数の UDF を定義できます。また、USING EXTERNAL FUNCTION
句で Lambda 関数の名前も指定します。
Athena UDF 用の Lambda 関数のデプロイには 2 つのオプションがあり、Lambda を使用して直接関数をデプロイすることも、AWS Serverless Application Repository を使用することもできます。UDF の既存の Lambda 関数を検索するには、パブリック AWS Serverless Application Repository またはプライベートリポジトリを検索し、Lambda にデプロイします。また、Java ソースコードを作成または変更して、JAR ファイルにパッケージ化し、Lambda または AWS Serverless Application Repository を使用してデプロイすることもできます。これを開始するための Java ソースコードとパッケージの例については、「Lambda 使用した UDF の作成とデプロイ」を参照してください。Lambda の詳細については、AWS Lambda デベロッパーガイドを参照してください。AWS Serverless Application Repository の詳細については、AWS Serverless Application Repository 開発者ガイドを参照してください。
Athena で UDF を使用してテキストを翻訳および分析する例については、AWS Machine Learning ブログ記事「Amazon Athena、Amazon Translate、および Amazon Comprehend で SQL 関数を使用してテキストを翻訳および分析する
考慮事項と制約事項
-
利用可能なリージョン – Athena UDF 機能は、Athena エンジンバージョン 2 以降がサポートされている AWS リージョン で使用できます。Athena エンジンバージョン 2 をサポートする AWS リージョンのリストについては、「Athena エンジンバージョン 2」を参照してください。
-
組み込み Lambda 関数 – Athena の組み込み Presto 関数は、高い性能を発揮するように設計されています。可能な場合は、UDF よりも組み込み関数を使用することをお勧めします。組み込み関数の詳細については、「Amazon Athena の関数」を参照してください。
-
スカラー UDF 限定 – Athena は、一度に 1 行を処理し、単一の列値を返すスカラー UDF のみをサポートします。Athena は、Lambda を呼び出すたびに行のバッチを UDF に渡しましますが、これは並行的に行われる可能性があります。UDF とクエリを設計するときは、この処理がネットワークトラフィックに及ぼす可能性がある影響に注意する必要があります。
-
UDF ハンドラ関数は省略形式を使用 – UDF 関数には、省略形式 (フルフォーマットではない) を使用します (例:
package.Class::method
の代わりにpackage.Class
)。 -
UDF メソッドは小文字を使用する必要がある – UDF メソッドは小文字にする必要があります。キャメルケースは使用できません。
-
Java ランタイムサポート – 現在、Athena UDF は Lambda について Java 8 と Java 11 ランタイムをサポートしています。詳細については、AWS Lambda デベロッパーガイドの「Java による Lambda 関数のビルド」を参照してください。
-
IAM 許可 – Athena で UDF クエリステートメントを作成して実行するには、クエリを実行する IAM プリンシパルに、Athena 関数以外のアクションを実行することが許可されている必要があります。詳細については、「Amazon Athena ユーザー定義関数 (UDF) を許可する IAM 許可ポリシーの例」を参照してください。
-
Lambda のクォータ – UDF には Lambda のクォータが適用されます。詳細については、AWS Lambda デベロッパーガイドの「Lambda のクォータ」を参照してください。
-
ビュー – UDF でビューを使用することはできません。
-
既知の問題 – 既知の問題に関する最新のリストについては、GitHub の awslabs/aws-athena-query-federation セクションにある「Limitations and Issues
」(制限と問題) を参照してください。
動画
Athena での UDF の使用に関する詳細については、以下の動画をご覧ください。
動画: Amazon Athena のユーザー定義関数 (UDF) の概要
以下の動画は、Amazon Athena で UDF を使用して、機密情報のリダクションを行う方法を紹介しています。
この動画で示されている構文はリリース前のものですが、概念は同じです。AmazonAthenaPreviewFunctionality
ワークグループなしで、Athena エンジンバージョン 2 を使用します。
動画:Amazon Athena で SQL クエリを使用したテキストフィールドの翻訳、分析、およびリダクション
以下の動画は、その他の AWS のサービス と共に Amazon Athena の UDF を使用して、テキストを翻訳し、分析する方法を紹介しています。
この動画で示されている構文はリリース前のものですが、概念は同じです。正しい構文については、AWS Machine Learning ブログの関連するブログ記事「Amazon Athena、Amazon Translate、および Amazon Comprehend による SQL 関数を使用したテキストの翻訳、修正、分析
UDF クエリ構文
USING EXTERNAL FUNCTION
句は、クエリで後続の SELECT
ステートメントで参照できる UDF または複数の UDF を指定します。UDF のメソッド名と UDF をホストする Lambda 関数の名前が必要です。
概要
USING EXTERNAL FUNCTION UDF_name
(variable1
data_type
[, variable2
data_type
][,...])
RETURNS data_type
LAMBDA 'lambda_function
'
SELECT [...] UDF_name
(expression
) [...]
パラメータ
- USING EXTERNAL FUNCTION
UDF_name
(variable1
data_type
[,variable2
data_type
][,...]) -
UDF_name
は UDF の名前を指定します。この名前は、参照される Lambda 関数内の Java メソッドに対応している必要があります。各variable data_type
は、UDF が入力として受け入れる名前付きの変数とそれに対応するデータ型を指定します。data_type
は、以下の表に記載されているサポート対象の Athena データ型の 1 つであり、対応する Java データ型にマップする必要があります。Athena データ型 Java データ型 TIMESTAMP
java.time.LocalDateTime (UTC)
DATE
java.time.LocalDate (UTC)
TINYINT
java.lang.Byte
SMALLINT
java.lang.Short
REAL
java.lang.Float
DOUBLE
java.lang.Double
DECIMAL (「
RETURNS
ノート」を参照)java.math.BigDecimal
BIGINT
java.lang.Long
INTEGER
java.lang.Int
VARCHAR
java.lang.String
VARBINARY
byte[]
BOOLEAN
java.lang.Boolean
ARRAY
java.util.List
ROW
java.util.Map<文字列、オブジェクト>
- RETURNS
data_type
-
data_type
は、UDF が出力として返す SQL データ型を指定します。上記の表にリストされている Athena データ型がサポートされます。DECIMAL
データ型の場合、この構文RETURNS DECIMAL(
を使います。precision
,scale
)精度
とスケール
は整数です。 - LAMBDA '
lambda_function
' -
lambda_function
は、UDF の実行時に呼び出される Lambda 関数の名前を指定します。 - SELECT [...]
UDF_name
(expression
) [...] -
UDF に値を渡し、結果を返す
SELECT
クエリです。UDF_Name
は使用する UDF を指定し、値を渡すために評価されるexpression
がその後に続きます。渡される値と返される値は、USING EXTERNAL FUNCTION
句で UDF に指定された対応するデータ型と一致する必要があります。
例
GitHub にある AthenaUDFHandler.java
Lambda 使用した UDF の作成とデプロイ
カスタム UDF を作成するには、UserDefinedFunctionHandler
クラスを拡張して新しい Java クラスを作成します。SDK の UserDefinedFunctionHandler.java
このセクションの手順では、コマンドラインとデプロイから Apache Maven
Maven を使用して Athena にカスタム UDF を作成する手順
SDK のクローン作成と開発環境の準備
始める前に、sudo
yum install git -y
を使用して git がシステムにインストールされていることを確認してください。
AWS クエリフェデレーション SDK をインストールするには
-
コマンドラインで次のコマンドを入力して、SDK リポジトリのクローンを作成します。このリポジトリには、SDK、例、データソースコネクタのスイートが含まれています。データソースコネクタの詳細については、「Amazon Athena 横串検索の使用」を参照してください。
git clone https://github.com/awslabs/aws-athena-query-federation.git
この手順の前提条件をインストールする
Apache Maven、AWS CLI、および AWS Serverless Application Model ビルドツールが既にインストールされている開発マシンで作業している場合は、このステップをスキップできます。
-
クローン作成時に作成した
aws-athena-query-federation
ディレクトリのルートから、開発環境を準備する prepare_dev_env.shスクリプトを実行します。 -
インストールプロセスによって作成された新しい変数を調達するようにシェルを更新するか、ターミナルセッションを再起動します。
source ~/.profile
重要 このステップをスキップすると、後で AWS CLI または AWS SAM ビルドツールが Lambda 関数を公開できないというエラーが表示されます。
Maven プロジェクトの作成
次のコマンドを実行して、Maven プロジェクトを作成します。groupId
を組織の一意の ID に置き換え、my-athena-udf
をアプリケーション名に置き換えます。詳細については、Apache Maven ドキュメントの「How do I make my first Maven project?
mvn -B archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DgroupId=
groupId
\ -DartifactId=my-athena-udfs
Maven プロジェクトへの依存関係とプラグインの追加
Maven プロジェクト pom.xml
ファイルに次の設定を追加します。例については、GitHub の pom.xml
<properties> <aws-athena-federation-sdk.version>2021.6.1</aws-athena-federation-sdk.version> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-athena-federation-sdk</artifactId> <version>${aws-athena-federation-sdk.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
UDF に Java コードを記述
UserDefinedFunctionHandler.java
次の例では、UDF の 2 つの Java メソッド、compress()
および decompress()
がクラス MyUserDefinedFunctions
内に作成されます。
*package *com.mycompany.athena.udfs; public class MyUserDefinedFunctions extends UserDefinedFunctionHandler { private static final String SOURCE_TYPE = "MyCompany"; public MyUserDefinedFunctions() { super(SOURCE_TYPE); } /** * Compresses a valid UTF-8 String using the zlib compression library. * Encodes bytes with Base64 encoding scheme. * * @param input the String to be compressed * @return the compressed String */ public String compress(String input) { byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8); // create compressor Deflater compressor = new Deflater(); compressor.setInput(inputBytes); compressor.finish(); // compress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); while (!compressor.finished()) { int bytes = compressor.deflate(buffer); byteArrayOutputStream.write(buffer, 0, bytes); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return encoded string byte[] compressedBytes = byteArrayOutputStream.toByteArray(); return Base64.getEncoder().encodeToString(compressedBytes); } /** * Decompresses a valid String that has been compressed using the zlib compression library. * Decodes bytes with Base64 decoding scheme. * * @param input the String to be decompressed * @return the decompressed String */ public String decompress(String input) { byte[] inputBytes = Base64.getDecoder().decode((input)); // create decompressor Inflater decompressor = new Inflater(); decompressor.setInput(inputBytes, 0, inputBytes.length); // decompress bytes to output stream byte[] buffer = new byte[4096]; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(inputBytes.length); try { while (!decompressor.finished()) { int bytes = decompressor.inflate(buffer); if (bytes == 0 && decompressor.needsInput()) { throw new DataFormatException("Input is truncated"); } byteArrayOutputStream.write(buffer, 0, bytes); } } catch (DataFormatException e) { throw new RuntimeException("Failed to decompress string", e); } try { byteArrayOutputStream.close(); } catch (IOException e) { throw new RuntimeException("Failed to close ByteArrayOutputStream", e); } // return decoded string byte[] decompressedBytes = byteArrayOutputStream.toByteArray(); return new String(decompressedBytes, StandardCharsets.UTF_8); } }
JAR ファイルのビルド
mvn clean install
を実行して、プロジェクトをビルドします。正常にビルドされると、JAR ファイルが target
という名前のプロジェクトの
フォルダに作成されます。ここで、artifactId
-version
.jarartifactId
は Maven プロジェクトで指定した名前(例: my-athena-udfs
)です。
JAR を AWS Lambda にデプロイする
コードを Lambda にデプロイする、次の 2 つの方法があります。
-
AWS Serverless Application Repository を使用してデプロイする(推奨)
-
JAR ファイルから Lambda 関数を作成する
オプション 1: AWS Serverless Application Repository にデプロイする
JAR ファイルを AWS Serverless Application Repository にデプロイするときは、アプリケーションのアーキテクチャを表す AWS SAM テンプレート YAML ファイルを作成します。次に、この YAML ファイルと、アプリケーションのアーティファクトがアップロードされて AWS Serverless Application Repository に対して利用可能になる Simple Storage Service (Amazon S3) バケットを指定します。以下の手順では、先ほどクローンした Athena Query Federation SDK の athena-query-federation/tools
ディレクトリにある publish.sh
詳細と要件については、「AWS Serverless Application Repository デベロッパーガイド」の「アプリケーションの公開」、「AWS Serverless Application Model デベロッパーガイド」の「AWS SAM テンプレートの概念」、および「AWS SAM CLI を使用してサーバーレスアプリケーションを公開する」を参照してください。
次の例は、YAML ファイルのパラメータを示しています。同様のパラメータを YAML ファイルに追加し、プロジェクトディレクトリに保存します。完全な例については、GitHub の athena-udf.yaml
Transform: 'AWS::Serverless-2016-10-31' Metadata: 'AWS::ServerlessRepo::Application': Name:
MyApplicationName
Description: 'The description I write for my application
' Author: 'Author Name
' Labels: - athena-federation SemanticVersion: 1.0.0 Parameters: LambdaFunctionName: Description: 'The name of the Lambda function that will contain your UDFs.
' Type: String LambdaTimeout: Description: 'Maximum Lambda invocation runtime in seconds. (min 1 - 900 max)' Default: 900 Type: Number LambdaMemory: Description: 'Lambda memory in MB (min 128 - 3008 max).' Default: 3008 Type: Number Resources: ConnectorConfig: Type: 'AWS::Serverless::Function' Properties: FunctionName: !Ref LambdaFunctionName Handler: "full.path.to.your.handler. For example, com.amazonaws.athena.connectors.udfs.MyUDFHandler
" CodeUri: "Relative path to your JAR file. For example, ./target/athena-udfs-1.0.jar
" Description: "My description of the UDFs that this Lambda function enables.
" Runtime: java8 Timeout: !Ref LambdaTimeout MemorySize: !Ref LambdaMemory
YAML ファイルを保存したプロジェクトディレクトリに publish.sh
スクリプトをコピーし、次のコマンドを実行します。
./publish.sh
MyS3Location
MyYamlFile
たとえば、バケットの場所が s3://mybucket/mysarapps/athenaudf
で、YAML ファイルが my-athena-udfs.yaml
として保存された場合:
./publish.sh mybucket/mysarapps/athenaudf my-athena-udfs
Lambda 関数を作成するには
-
https://console.aws.amazon.com/lambda/
で Lambda コンソールを開いて [Create function] (関数の作成) をクリックし、[Browse serverless app repository] (Serverless Application Repository の参照) を選択します。 -
[プライベートアプリケーション] を選択し、リストでアプリケーションを見つけるか、キーワードを使用してアプリケーションを検索して選択します。
-
アプリケーションの詳細を確認して指定し、[デプロイ] を選択します。
これで、Lambda 関数の JAR ファイルで定義されたメソッド名を Athena で UDF として使用できるようになりました。
オプション 2: Lambda 関数を直接作成する
コンソールまたは AWS CLI を使用して、Lambda 関数を直接作成することもできます。以下の例は、Lambda create-function
CLI コマンドを使用する方法を示しています。
aws lambda create-function \ --function-name
MyLambdaFunctionName
\ --runtime java8 \ --role arn:aws:iam::1234567890123:role/my_lambda_role
\ --handlercom.mycompany.athena.udfs.MyUserDefinedFunctions
\ --timeout 900 \ --zip-file fileb://./target/my-athena-udfs-1.0-SNAPSHOT.jar