Athena-Expressを使ってAmazon Athenaへのクエリの実行と結果取得をシンプルにする

2022.10.21

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

こんにちは、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さんに教えて頂きました。ありがとうございました!

参考

以上