Athenaのstruct型でオブジェクトの要素が増減したときの読み込みの様子を調べた

2022.06.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部の鈴木です。

Athenaのstruct型でテーブルのカラムを定義していて、読み込み元のファイル内で要素が増減していたとき、検索結果はどのような影響を受けるのか気になったので確認してみました。

結果としては、オブジェクトの要素が定義から増減していても検索は行うことができ、未定義の要素は検索結果には表示されず、テーブルには定義されているけれどファイルに存在しない要素はnullになることが分かりました。

準備

読み込み用のテーブルの準備

読み込み用のテーブルを作成しておきました。

DDLは以下になります。データを置いておくバケットデータを置いておくパスは必要なものに変えておきます。

CREATE EXTERNAL TABLE `struct_sample`(
  `id` int,
  `value` struct <
              value1: int,
              value2: int
          >
)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
  's3://データを置いておくバケット/データを置いておくパス/'

読み込むファイルのフォーマットは、parquetで検証しました。整数値のidカラムと、JSONが入っているvalueカラムがある想定です。

valueカラムは挙動を確認したいstruct型にしておきました。

Glueデータカタログから確認すると、以下のようなスキーマになりました。

スキーマ1

valueカラムの詳細はこのようになっています。

スキーマの詳細

データの作成

JSON文字列を含むCSVを3種類作成し、parquetにしておきました。 CSVは以下の3つになります。

パターン1: 定義通りのJSONの文字列を含むもの

"id","value"
"1","{\"value1\": 11, \"value2\": 12}"
"2","{\"value1\": 21, \"value2\": 22}"
"3","{\"value1\": 31, \"value2\": 32}"

パターン2: 定義より1つ要素が多いJSONの文字列を含むもの

"id","value"
"4","{\"value1\": 41, \"value2\": 42, \"value3\": 43}"
"5","{\"value1\": 51, \"value2\": 52, \"value3\": 53}"
"6","{\"value1\": 61, \"value2\": 62, \"value3\": 63}"

パターン3: 定義より1つ要素が少ないJSONの文字列を含むもの

"id","value"
"7","{\"value1\": 71}"
"8","{\"value1\": 81}"
"9","{\"value1\": 91}"

以下のPythonスクリプトで、parquetに変換しておきました。

import json
import os

import pandas as pd


def convert_csv2parquet(filepath: str, pandas_dtype: dict):
    """ 指定したファイルを読み込み、parquetに変換する。
    """
    base_filename = os.path.splitext(os.path.basename(filepath))[0]
    df = pd.read_csv(
            filepath,
            dtype=pandas_dtype,
            escapechar = "\\"
         )
    df["value"] = df["value"].map(lambda x: json.loads(x))
    df.to_parquet(f"./{base_filename}.parquet")

# 読み込み時の型を指定する
pandas_dtype = {
        'id': int,
        'value': str
    }

# 変換を実行する
convert_csv2parquet("./sample1.csv", pandas_dtype)
convert_csv2parquet("./sample2.csv", pandas_dtype)
convert_csv2parquet("./sample3.csv", pandas_dtype)

作成したファイルはテーブルから参照できる箇所にアップロードしておきました。

ファイルアップロード後

やってみた

以下のクエリを実行して、まとめて値を参照してみました。

select 
    *
from struct_sample
order by id

結果は以下のようになりました。

クエリの実行結果

オブジェクトの要素が定義から増減していても検索は行えていることが分かります。

テーブル定義内で未定義の要素value3は結果として表示されていません。一方で、JSONにvalue2が存在しないパターン3の場合、value2nullになることが分かりました。

最後に

今回は、Athenaのstruct型でテーブルのカラムを定義していて、読み込み元で要素が変更されたとき、クエリ実行時にどのような挙動になるのかを確認してみました。

定義していないカラムは読めなかったり、存在しないカラムはnullになったりはするものの、クエリ自体が失敗したりファイルが読み飛ばされたりしないことが分かったので安心しました。

参考になりましたら幸いです。

参考