こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、Athena-Expressを使ってAmazon Athenaへのクエリの実行と結果取得をシンプルにする方法のご紹介です。
Athena-Expressはどう便利なのか
Athena-Expressは、Athenaのクエリ実行と結果取得の処理をシンプルにすることができるAWS SDKのラッパーです。
AWS SDKのみでAthenaのクエリ実行し結果取得する場合、次のようなAthenaのAPI仕様によりクライアント側の処理の記述が煩雑になりがちでした。
- クエリ実行の開始(StartQueryExecution)した上で実行の完了を待機し結果取得(GetQueryResults)する必要があるため、ポーリング処理を合わせて実装する必要がある。
- GetQueryResultsで取得した結果の構文はデータとヘッダーが別れた形式となっており、1レコード=1Jsonオブジェクトのシンプルな形式に変換したい場合は変換処理を合わせて実装する必要がある。
しかしAthena-Expressを使うことにより、SDKの中でポーリング処理や取得結果の変換処理を行ってくれるため、シンプルな記述とすることができます。
3年ほど前ですが、Athena-ExpressはAWS公式ブログでも紹介されていました。
使ってみた
インストール
npm i athena-express
使ってみる
使い方は次のようになります。
test/athena-query.test.ts
import { AthenaExpress } from 'athena-express';
import * as AWS from 'aws-sdk';
AWS.config.update({ region: 'ap-northeast-1' });
const athenaExpressConfig = {
aws: AWS, //必須
workgroup: 'workGroupVersionSpecified', //オプション。既定はdefault
};
const athenaExpress = new AthenaExpress(athenaExpressConfig);
const main = async () => {
const sqlQuery = 'SELECT * FROM gluedatabase.source_glue_table';
const results = await athenaExpress.query(sqlQuery);
console.log(results);
};
main();
実行するとシンプルな形式でクエリ結果を取得することができました。
$ npx ts-node athena-express-query.ts
{
Items: [
{
deviceid: 'd001',
maxtemperature: 19.8,
year: 2022,
month: 11,
day: 1
},
{
deviceid: 'd002',
maxtemperature: 21,
year: 2022,
month: 11,
day: 1
},
{
deviceid: 'd001',
maxtemperature: 22.7,
year: 2022,
month: 10,
day: 15
},
{
deviceid: 'd002',
maxtemperature: 20.5,
year: 2022,
month: 10,
day: 15
},
{
deviceid: 'd001',
maxtemperature: 19.8,
year: 2022,
month: 10,
day: 30
},
{
deviceid: 'd002',
maxtemperature: 21,
year: 2022,
month: 10,
day: 30
},
{
deviceid: 'd001',
maxtemperature: 19.1,
year: 2022,
month: 10,
day: 1
},
{
deviceid: 'd002',
maxtemperature: 22.9,
year: 2022,
month: 10,
day: 1
}
]
}
ConfigErrorとなる場合
次のようにConfigError: Missing region in config
というエラーとなる場合は、AWS SDKのリージョン指定を忘れている可能性があります。AWS.config.update({ region: 'リージョン' })
という記述をしているか確認しましょう。
ConfigError: Missing region in config
11 | test('Query test', async () => {
12 | const sqlQuery = 'SELECT * FROM gluedatabase.source_glue_table';
> 13 | const results = await athenaExpress.query(sqlQuery);
| ^
Athena-Expressを使わない場合のクエリ実行結果
Athena-ExpressでなくAWS SDKのGetQueryResultsを使用してクエリの実行結果を取得した場合は、次のようにレコードのデータとカラムなどのメタ情報が分かれた冗長な形式のレスポンスとなり、大抵の場合はシンプルな形式のJsonへの変換処理が必要となります。
$ aws athena get-query-results --query-execution-id 9555b376-e987-4f49-a3fc-3a998ce5dfbc
{
"ResultSet": {
"Rows": [
{
"Data": [
{
"VarCharValue": "deviceid"
},
{
"VarCharValue": "maxtemperature"
},
{
"VarCharValue": "year"
},
{
"VarCharValue": "month"
},
{
"VarCharValue": "day"
}
]
},
{
"Data": [
{
"VarCharValue": "d001"
},
{
"VarCharValue": "22.7"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "15"
}
]
},
{
"Data": [
{
"VarCharValue": "d002"
},
{
"VarCharValue": "20.5"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "15"
}
]
},
{
"Data": [
{
"VarCharValue": "d001"
},
{
"VarCharValue": "19.1"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "1"
}
]
},
{
"Data": [
{
"VarCharValue": "d002"
},
{
"VarCharValue": "22.9"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "1"
}
]
},
{
"Data": [
{
"VarCharValue": "d001"
},
{
"VarCharValue": "19.8"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "30"
}
]
},
{
"Data": [
{
"VarCharValue": "d002"
},
{
"VarCharValue": "21.0"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "10"
},
{
"VarCharValue": "30"
}
]
},
{
"Data": [
{
"VarCharValue": "d001"
},
{
"VarCharValue": "19.8"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "11"
},
{
"VarCharValue": "1"
}
]
},
{
"Data": [
{
"VarCharValue": "d002"
},
{
"VarCharValue": "21.0"
},
{
"VarCharValue": "2022"
},
{
"VarCharValue": "11"
},
{
"VarCharValue": "1"
}
]
}
],
"ResultSetMetadata": {
"ColumnInfo": [
{
"CatalogName": "hive",
"SchemaName": "",
"TableName": "",
"Name": "deviceid",
"Label": "deviceid",
"Type": "varchar",
"Precision": 2147483647,
"Scale": 0,
"Nullable": "UNKNOWN",
"CaseSensitive": true
},
{
"CatalogName": "hive",
"SchemaName": "",
"TableName": "",
"Name": "maxtemperature",
"Label": "maxtemperature",
"Type": "float",
"Precision": 17,
"Scale": 0,
"Nullable": "UNKNOWN",
"CaseSensitive": false
},
{
"CatalogName": "hive",
"SchemaName": "",
"TableName": "",
"Name": "year",
"Label": "year",
"Type": "integer",
"Precision": 10,
"Scale": 0,
"Nullable": "UNKNOWN",
"CaseSensitive": false
},
{
"CatalogName": "hive",
"SchemaName": "",
"TableName": "",
"Name": "month",
"Label": "month",
"Type": "integer",
"Precision": 10,
"Scale": 0,
"Nullable": "UNKNOWN",
"CaseSensitive": false
},
{
"CatalogName": "hive",
"SchemaName": "",
"TableName": "",
"Name": "day",
"Label": "day",
"Type": "integer",
"Precision": 10,
"Scale": 0,
"Nullable": "UNKNOWN",
"CaseSensitive": false
}
]
}
},
"UpdateCount": 0
}
おわりに
Athena-Expressを使ってAmazon Athenaへのクエリの実行と結果取得をシンプルにする方法の紹介でした。
今までは下記エントリで紹介していたようにわざわざポーリング処理を設けたりしていましたが、今後はAthena-Expressを積極的に採用していきたいと思います。
この方法は弊社のtakahashi-yudaiさんに教えて頂きました。ありがとうございました!
参考
以上