目的:AWS Glueデータカタログを使用して、S3バケットにあるJSONデータ用の単一のテーブルを作成し、クエリを実行することを望んでいますRedshift Spectrumを介して解析します。
Background:JSONデータはDynamoDB Streamsからのもので、深くネストされています。 JSONの最初のレベルには、キー、NewImage、OldImage、SequenceNumber、ApproximateCreationDateTime、SizeBytes、EventNameの一貫した要素セットがあります。唯一の違いは、NewImageを持たないレコードとOldImageを持たないレコードがあることです。ただし、この最初のレベルより下では、スキーマは大きく異なります。
理想的には、Glueを使用してこの最初のレベルのJSONのみを解析し、基本的に下位レベルを大きなSTRINGオブジェクトとして扱います(その後、必要に応じてRedshift Spectrumで解析します)。現在、レコード全体をRedshiftの単一のVARCHAR列に読み込んでいますが、レコードはRedshiftのデータ型の最大サイズに近づいています(VARCHARの最大長は65535です)。その結果、レコードがRedshiftにヒットする前に、この最初のレベルの解析を実行したいと思います。
これまでに試した/参照したこと:
質問:Glue(または他の方法)を使用して、これらのレコードの最初のレベルだけを解析できるようにする方法-以下のさまざまなスキーマを無視する最上位の要素-Spectrumからアクセスしたり、Redshiftに物理的にロードしたりできるようにしますか?
接着剤は初めてです。私はGlueのドキュメントにかなりの時間を費やし、フォーラムで(ややまばらな)情報を調べてきました。私は明白な何かを見逃している可能性があります-または、これは現在の形の接着剤の制限です。どんな提案も歓迎します。
ありがとう!
テーブル定義でこれを実行できるかどうかはわかりませんが、マッピング関数を使用して最上位の値をJSON文字列としてキャストすることにより、ETLジョブでこれを実現できます。ドキュメント:[ link ]
import json
# Your mapping function
def flatten(rec):
for key in rec:
rec[key] = json.dumps(rec[key])
return rec
old_df = glueContext.create_dynamic_frame.from_options(
's3',
{"paths": ['s3://...']},
"json")
# Apply mapping function f to all DynamicRecords in DynamicFrame
new_df = Map.apply(frame=old_df, f=flatten)
ここから、S3(おそらくクエリのために最適化するためのParquetまたは他のカラムナー形式)にエクスポートするか、私の理解からRedshiftに直接エクスポートするオプションがあります。
2018年12月20日の時点で、第1レベルのjsonフィールドを持つテーブルをタイプSTRINGの列として手動で定義することができました。グルースクリプトでは、ダイナミックフレームの列が文字列になっています。そこから、フィールドに対してUnbox
型のjson
操作を実行できます。これにより、フィールドがJSONで解析され、実際のスキーマが導出されます。 Unbox
をFilter
と組み合わせると、スキーマのリストをループできる場合、同じ入力から異種jsonスキーマをループして処理できます。
ただし、警告の1つの言葉、これは非常に遅いです。ループの各反復中に接着剤がs3からソースファイルをダウンロードしていると思います。最初のソースデータを保持する方法を探していましたが、.toDF
は、接着剤StringTypeとして指定した場合でも、文字列jsonフィールドのスキーマを導出します。パフォーマンスが向上したソリューションを見つけられる場合は、ここにコメントを追加します。
できれば$ [*]のグルー分類器を追加する必要があります
S3でjsonファイルをクロールすると、ファイルの最初の行が読み取られます。
このJSONファイルのデータカタログテーブルをredshiftにロードするために、グルージョブを作成できます。
ここでの唯一の問題は、Redshift Spectrumがデータカタログのjsonテーブルの読み取りに問題があることです。
解決策を見つけたら教えてください
浅いネストされたjsonに役立つとわかった手順:
_datasource0
_としての最初のレベルのApplyMapping
struct
またはarray
オブジェクトを分解して、要素レベルdf1 = datasource0.toDF().select(id,col1,col2,...,explode(coln).alias(coln)
を削除します。ここで、explode
には_from pyspark.sql.functions import explode
_が必要です。
intact_json = df1.select(id, itct1, itct2,..., itctm)
;により、そのまま保持するJSONオブジェクトを選択します。
_df1
_をdynamicFrameに変換し、dynamicFrameをRelationalizeし、dataframe.drop_fields(itct1, itct2,..., itctm)
;によって無傷の列をドロップします。
'id'列に基づいて、リレーショナル化されたテーブルを元のテーブルに結合します。
これは、現時点でのGlueの制限です。 Glue Classifiersをご覧になりましたか?まだ使っていない唯一の作品ですが、あなたのニーズに合うかもしれません。フィールドなどのJSONパスを定義できます。
それ以外-接着剤の仕事は行く方法です。バックグラウンドでSparkなので、ほとんどすべてを行うことができます。開発エンドポイントを設定して試してみてください。過去3週間、さまざまな障害に立ち向かい、完全にすべてのGlue機能を廃止し、Sparkのみを使用することで、移植性と実際の動作の両方を実現できます。
開発エンドポイントを設定する際に留意する必要があることの1つは、IAMロールに「/」のパスが必要であるため、このパスを持つ別のロールを手動で作成する必要があることです。自動的に作成されたものには、「/ service-role /」のパスがあります。