Amazon AthenaでAmazon Ion形式データのクエリがサポートされました

2022.04.19

いわさです。

先日のアップデートでAmazon AthenaでAmazon Ion形式のデータをクエリ出来るようになりました。

Amazon Ion はAmazonで内部的に開発されているデータフォーマットですが、AWSのサービスではQLDBで利用されています。
以前、QLDBのジャーナルデータをJSON形式でエクスポート出来るようになった際にAthenaで操作する方法をご紹介したのですが、今後はAthenaでクエリする観点で見るとJSON形式でエクスポートしなくても良くなります。

Ion形式はJSON形式を拡張したものであり、上位互換となっています。
QLDBジャーナルのJSONエクスポート機能では、IonからJSONへの変換時に一部データがダウンコンバートされてしまうのですが、今回Ionを直接クエリ出来るようになるのでそれが回避出来ます。

実際にどういったものがダウンコンバートの対象となるのかについては以下に記述があります。

QLDBのジャーナルをJSONとIONで比較

Athenaで試してみたいところですが、せっかくなのでQLDBのジャーナルでJSONとIonを比較してみましょう。

{
    "blockAddress": {
        "strandId": "294jgIvicl39p2SvfmPRJK",
        "sequenceNo": 0
    },
    "transactionId": "8fcbL1p5NSW6QbLgBh4C0b",
    "blockTimestamp": "2022-01-07T20:02:59.421Z",
    "blockHash": "FSGrXi6bWc5LaKI6cYCOj+doyFed1ZRD9YJhgY13Ujw=",
    "entriesHash": "FSGrXi6bWc5LaKI6cYCOj+doyFed1ZRD9YJhgY13Ujw=",
    "previousBlockHash": "",
    "entriesHashList": [
        "jtMwLm8JNJpWajXYp5Zw4fPQP2lyDC2UxkLScA+UBXs=",
        "fHDFFjhC6OpK4+k+neriLuqW5NPa9dePB5iN58tKZ/U=",
        "NAH5hYT0YwM3JOg0mCQmh9QCvrh64p02O8R+2mkYsuk=",
        "g2em3Vcy7q2NGC1ZNPsYZewYqAcbdEjwlGmZnev2J3E="
    ],
    "transactionInfo": {
        "statements": [
            {
                "statement": "SELECT * FROM information_schema.user_tables",
                "startTime": "2022-01-07T20:02:59.168Z",
                "statementDigest": "vrvWiTj+6MEi8QmaEflsEgf4p3SBk7HREoAXijhK9vc="
            }
        ]
    },
    "revisions": [
        {
            "hash": "fHDFFjhC6OpK4+k+neriLuqW5NPa9dePB5iN58tKZ/U="
        }
    ]
}

Ion形式

{
    blockAddress: {
        strandId: "294jgIvicl39p2SvfmPRJK",
        sequenceNo: 0
    },
    transactionId: "8fcbL1p5NSW6QbLgBh4C0b",
    blockTimestamp: 2022-01-07T20: 02: 59.421Z,
    blockHash: {
        {
            FSGrXi6bWc5LaKI6cYCOj+doyFed1ZRD9YJhgY13Ujw=
        }
    },
    entriesHash: {
        {
            FSGrXi6bWc5LaKI6cYCOj+doyFed1ZRD9YJhgY13Ujw=
        }
    },
    previousBlockHash: {
        {}
    },
    entriesHashList: [
        {
            {
                jtMwLm8JNJpWajXYp5Zw4fPQP2lyDC2UxkLScA+UBXs=
            }
        },
        {
            {
                fHDFFjhC6OpK4+k+neriLuqW5NPa9dePB5iN58tKZ/U=
            }
        },
        {
            {
                NAH5hYT0YwM3JOg0mCQmh9QCvrh64p02O8R+2mkYsuk=
            }
        },
        {
            {
                g2em3Vcy7q2NGC1ZNPsYZewYqAcbdEjwlGmZnev2J3E=
            }
        }
    ],
    transactionInfo: {
        statements: [
            {
                statement: "SELECT * FROM information_schema.user_tables",
                startTime: 2022-01-07T20: 02: 59.168Z,
                statementDigest: {
                    {
                        vrvWiTj+6MEi8QmaEflsEgf4p3SBk7HREoAXijhK9vc=
                    }
                }
            }
        ]
    },
    revisions: [
        {
            hash: {
                {
                    fHDFFjhC6OpK4+k+neriLuqW5NPa9dePB5iN58tKZ/U=
                }
            }
        }
    ]
}

上位互換なだけあってだいぶ似ていますね。
上記はQLDBのトランザクションジャーナルデータなのですが、確認出来る範囲ではダウンコンバートされているものはなさそうです。数値の精度や独自のnull形式は注意が必要ですね。

Athenaで使ってみる

ではAthenaで使ってみましょう。

まず、Athenaで新しいシリアライザーAmazon Ion Hive Serdeが提供されるようになりました。これを使ってCREATE TABLEします。

S3バケット以下のIon形式のデータを格納しておきます。

hoge.ion

{
    hoge1: {
        hogeAAA: "111",
        hogeBBB: "222"
    },
    hoge2: "value1"
}
{
    hoge1: {
        hogeAAA: "333",
        hogeBBB: "444"
    },
    hoge2: "value2"
}
{
    hoge1: {
        hogeAAA: "111",
        hogeBBB: "444"
    },
    hoge2: "value3"
}

単純にマッピングさせるだけの場合は以下の構文を使うことが出来ます。

CREATE EXTERNAL TABLE sample1 (
    hoge1 MAP<STRING, STRING>,
    hoge2 STRING
)
STORED AS ION
LOCATION 's3://20220419ledge/ionsample/sample1/'

構造データに対してアクセス、あるいはフィルタリングする場合は以下のように指定が出来ます。

フラット化

path_extractorを使うことで列にマッピングし、フラットな列形式でテーブルを作成することが出来ます。
以下ではhoge1の中のIon値をhogeaaahogebbbの列にマッピングしています。

CREATE EXTERNAL TABLE sample2 (
    hogeaaa STRING,
    hogebbb STRING,
    hoge2 STRING
)
ROW FORMAT SERDE
 'com.amazon.ionhiveserde.IonHiveSerDe'
WITH SERDEPROPERTIES (
    'ion.hogeaaa.path_extractor'='(hoge1 hogeaaa)',
    'ion.hogebbb.path_extractor'='(hoge1 hogebbb)')
STORED AS ION
LOCATION 's3://20220419ledge/ionsample/sample1/'

列マッピング

Ionの列マッピングの方法は以下に一覧でまとまっています。

変換時の制限事項が少しあるのでそのあたりをよく読んでおいたほうが良いです。MAPSTRUCT周りが最初うまくいかなくて少し苦労しました。

さいごに

わかりやすいのがQLDBのジャーナルなのですが、Athena以外で直接参照する場合は引き続きJSONでエクスポートすることになるかなと思います。
Athenaを使う場合はダウンコンバートなしでIonをそのまま使えるのでそういうシーンでは利用価値がありそうですね。