web-dev-qa-db-ja.com

AWS Glue:さまざまなスキーマでネストされたJSONを処理する方法

目的: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にヒットする前に、この最初のレベルの解析を実行したいと思います。

これまでに試した/参照したこと:

  • AWS Glue CrawlerをS3バケットにポイントすると、一貫したトップレベルスキーマ(上記の属性)を持つ数百のテーブルが作成されますが、STRUCT要素のより深いレベルではスキーマが異なります。これらのすべてのテーブルから読み取り、単一のテーブルにロードするGlue ETLジョブを作成する方法は見つかりませんでした。
  • テーブルを手動で作成することは有益ではありませんでした。各列をSTRINGデータ型に設定しようとしましたが、ジョブはデータのロードに成功しませんでした(おそらくこれにはSTRUCTからSTRINGへの変換が必要になるためです)。列をSTRUCTに設定する場合、定義済みのスキーマが必要ですが、これはレコードごとに異なるため、問題のすべてのレコードで機能する汎用のSTRUCTスキーマを提供することはできません。
  • AWS Glue Relationalize transform は興味をそそりますが、このシナリオで探しているものではありません(JSONの一部を完全に平坦化するのではなく、そのままにしておきたいためです)。 Redshift Spectrumは、数週間前の スカラーJSON データをサポートしていますが、これは、処理しているネストされたJSONでは機能しません。これらのどちらも、グルークローラーによって作成された数百のテーブルの処理には役立ちません。

質問:Glue(または他の方法)を使用して、これらのレコードの最初のレベルだけを解析できるようにする方法-以下のさまざまなスキーマを無視する最上位の要素-Spectrumからアクセスしたり、Redshiftに物理的にロードしたりできるようにしますか?

接着剤は初めてです。私はGlueのドキュメントにかなりの時間を費やし、フォーラムで(ややまばらな)情報を調べてきました。私は明白な何かを見逃している可能性があります-または、これは現在の形の接着剤の制限です。どんな提案も歓迎します。

ありがとう!

11
Airik55082

テーブル定義でこれを実行できるかどうかはわかりませんが、マッピング関数を使用して最上位の値を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に直接エクスポートするオプションがあります。

2
x1084

2018年12月20日の時点で、第1レベルのjsonフィールドを持つテーブルをタイプSTRINGの列として手動で定義することができました。グルースクリプトでは、ダイナミックフレームの列が文字列になっています。そこから、フィールドに対してUnbox型のjson操作を実行できます。これにより、フィールドがJSONで解析され、実際のスキーマが導出されます。 UnboxFilterと組み合わせると、スキーマのリストをループできる場合、同じ入力から異種jsonスキーマをループして処理できます。

ただし、警告の1つの言葉、これは非常に遅いです。ループの各反復中に接着剤がs3からソースファイルをダウンロードしていると思います。最初のソースデータを保持する方法を探していましたが、.toDFは、接着剤StringTypeとして指定した場合でも、文字列jsonフィールドのスキーマを導出します。パフォーマンスが向上したソリューションを見つけられる場合は、ここにコメントを追加します。

1
Zambonilli

できれば$ [*]のグルー分類器を追加する必要があります

S3でjsonファイルをクロールすると、ファイルの最初の行が読み取られます。

このJSONファイルのデータカタログテーブルをredshiftにロードするために、グルージョブを作成できます。

ここでの唯一の問題は、Redshift Spectrumがデータカタログのjsonテーブルの読み取りに問題があることです。

解決策を見つけたら教えてください

0
beni

浅いネストされたjsonに役立つとわかった手順:

  1. _datasource0_としての最初のレベルのApplyMapping

  2. structまたはarrayオブジェクトを分解して、要素レベルdf1 = datasource0.toDF().select(id,col1,col2,...,explode(coln).alias(coln)を削除します。ここで、explodeには_from pyspark.sql.functions import explode_が必要です。

  3. intact_json = df1.select(id, itct1, itct2,..., itctm);により、そのまま保持するJSONオブジェクトを選択します。

  4. _df1_をdynamicFrameに変換し、dynamicFrameをRelationalizeし、dataframe.drop_fields(itct1, itct2,..., itctm);によって無傷の列をドロップします。

  5. 'id'列に基づいて、リレーショナル化されたテーブルを元のテーブルに結合します。

0
LinZ

これは、現時点でのGlueの制限です。 Glue Classifiersをご覧になりましたか?まだ使っていない唯一の作品ですが、あなたのニーズに合うかもしれません。フィールドなどのJSONパスを定義できます。

それ以外-接着剤の仕事は行く方法です。バックグラウンドでSparkなので、ほとんどすべてを行うことができます。開発エンドポイントを設定して試してみてください。過去3週間、さまざまな障害に立ち向かい、完全にすべてのGlue機能を廃止し、Sparkのみを使用することで、移植性と実際の動作の両方を実現できます。

開発エンドポイントを設定する際に留意する必要があることの1つは、IAMロールに「/」のパスが必要であるため、このパスを持つ別のロールを手動で作成する必要があることです。自動的に作成されたものには、「/ service-role /」のパスがあります。

0
LauriK