Amazon S3 Select で扱う JSON データの形式と Type 指定について

Amazon S3 Select で扱う JSON データの形式と Type 指定について

S3 Select で扱うのに適している JSON の形式とは?その形式の JSON を取得する時に指定する Type は?これを読めば今ベストな組み合わせがわかります!
Clock Icon2018.04.27 14:21

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

はじめに こんばんは、菅野です。 先日のブログエントリーで、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

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.