Amazon S3 Select で扱う JSON データの形式と Type 指定について
はじめに こんばんは、菅野です。 先日のブログエントリーで、Amazon S3 Select を使って CSV ファイルを取得してみました。 今回は JSON データを対象にしてみたいと思います。
データが JSON の場合に指定するパラメータ
データが CSV の場合には存在しなかったパラメータとして「InputSerialization.JSON.Type」が必要となります。
Type:"Lines"
- 1行が1レコードになっているタイプのデータを扱います
- このタイプのデータを「JSON Lines」と呼びます(http://jsonlines.org/)
{"id":1,"name":"りんご","price":90} {"id":2,"name":"みかん","price":30} {"id":3,"name":"柿","price":60} {"id":4,"name":"キウイ","price":70} {"id":5,"name":"梨","price":120}
Type:"Document"
- JSON Lines 以外の JSON を扱う時はこちらを指定します
- 以下のようにトップレベルがオブジェクトになっている JSON ファイルを扱う事ができます
{ "Records": [ {"id":1,"name":"りんご","price":90}, {"id":2,"name":"みかん","price":30}, {"id":3,"name":"柿","price":60}, {"id":4,"name":"キウイ","price":70}, {"id":5,"name":"梨","price":120} ] }
トップレベルが配列になっている JSON ファイルについて
- 以下のような JSON ファイルはどちらのタイプを使っても結果が空となります(2018年4月27日現在)
[ {"id":1,"name":"りんご","price":90}, {"id":2,"name":"みかん","price":30}, {"id":3,"name":"柿","price":60}, {"id":4,"name":"キウイ","price":70}, {"id":5,"name":"梨","price":120} ]
どちらのタイプを使うべきか(2018年4月27日現在)
「JSON Lines」を対象とし、Type:"Lines" で使いましょう。 JSON Lines 形式以外のデータに対して Type:"Document" でアクセスすると、以下のように全体で1レコードとして扱われますので where による絞り込みができません。
{"Records":[{"id":1,"name":"りんご","price":90},{"id":2,"name":"みかん","price":30},{"id":3,"name":"柿","price":60},{"id":4,"name":"キウイ","price":70},{"id":5,"name":"梨","price":120}]}
また、Type:"Lines" だと CSV 形式での出力も可能です。
取得用のスクリプト
準備については先日のブログエントリーを読んでください。 今回は「JSON Lines」に対して S3 Select を実行する python スクリプトを用意しました。
#!/usr/bin/env python # -*- coding: utf-8 -*- import boto3 s3 = boto3.client( 's3', 'ap-northeast-1' ) response_s3select = s3.select_object_content( Bucket = 'sugano-test-2', Key = 'test-data/price-list-1.json', ExpressionType = 'SQL', Expression = 'Select * from S3Object', # Expression = 'Select s.id, s.name, s.price from S3Object s', # Expression = 'Select count(*) as data_count from S3Object s', # Expression = 'Select s.id, s.name, s.price from S3Object s where cast(s.id as int) > 3', # Expression = 'Select sum(cast(s.price as int)) as price_sum from S3Object s', # Expression = 'Select max(cast(s.price as int)) as price_max, cast(avg(cast(s.price as int)) as int) as price_ave, min(cast(s.price as int)) as price_min from S3Object s', InputSerialization = { 'CompressionType': 'NONE', 'JSON' : { 'Type' : 'Lines' } }, OutputSerialization = { 'CSV' : { 'RecordDelimiter' : '\n', 'FieldDelimiter' : ',' } } # OutputSerialization = { # 'JSON' : { # 'RecordDelimiter' : '\n' # } # } ) for event in response_s3select[ 'Payload' ]: if 'Records' in event: records = event[ 'Records' ][ 'Payload' ].decode( 'utf-8' ) print( records )
結果(CSV出力)
1,りんご,90 2,みかん,30 3,柿,60 4,キウイ,70 5,梨,120
JSON で出力する場合
ソースコードの中に JSON 形式で出力するための OutputSerialization もコメントアウトで埋めてあります。 以下のように CSV 用と切り替えてみましょう。
# OutputSerialization = { # 'CSV' : { # 'RecordDelimiter' : '\n', # 'FieldDelimiter' : ',' # } # } OutputSerialization = { 'JSON' : { 'RecordDelimiter' : '\n' } }
結果は以下のようになります。
{"id":1,"name":"りんご","price":90} {"id":2,"name":"みかん","price":30} {"id":3,"name":"柿","price":60} {"id":4,"name":"キウイ","price":70} {"id":5,"name":"梨","price":120}
他の SQL 文も試してみる
- Select count(*) as data_count from S3Object s
{"data_count":5}
- Select s.id, s.name, s.price from S3Object s where cast(s.id as int) > 3
{"id":4,"name":"キウイ","price":70} {"id":5,"name":"梨","price":120}
- Select sum(cast(s.price as int)) as price_sum from S3Object s
{"price_sum":370}
- Select max(cast(s.price as int)) as price_max, cast(avg(cast(s.price as int)) as int) as price_ave, min(cast(s.price as int)) as price_min from S3Object s
{"price_max":120,"price_ave":74,"price_min":30}
- 以下の SQL 文のように平均を int に cast していない場合は指数表記となるようです
- Select max(cast(s.price as int)) as price_max, avg(cast(s.price as int)) as price_ave, min(cast(s.price as int)) as price_min from S3Object s
{"price_max":120,"price_ave":74e0,"price_min":30}
まとめ
JSON Lines 形式のデータも、CSV 形式と同様に S3 Select で扱える事が確認できました。 Type:"Document" の場合の動作は 2018年4月27日現在だと残念な結果となりましたが、いつか改善されると思いますので待ちましょう。
さいごに
いかがでしたでしょうか。 少しのソース修正だけで JSON Lines 形式のデータに対応することができました。 本エントリーがを見た皆様が S3 Select を活用するための参考になれば幸いです。
参考ページ
以下のページを参考にさせていただきました。 ありがとうございました。 Amazon S3 Select および Amazon Glacier Select の SQL リファレンス - Amazon Simple Storage Service