Node.jsドライバ for Snowflakeを試してみる

2021.02.27

はじめに

データアナリティクス事業本部のkobayashiです。

Snowflakeへ接続してSQLを実行する際にSnowflakeではJDBC、ODBCなど様々なドライバを用意してくれています。今回Snowflakeを操作する際にNode.jsドライバを調査する機会が有りましたのでその内容をまとめます。

環境

  • node.js 12.8.0
  • snowflake-sdk 1.5.3

Node.jsドライバ for Snowflake

SnowflakeのNode.jsドライバはjavascriptで記述されており、Snowflakeへのネイティブで非同期な接続を行えます。

( Node.js Driver — Snowflake Documentation より引用)

Written in pure JavaScript, the Node.js driver provides a native asynchronous Node.js interface to Snowflake.

GitHub - snowflakedb/snowflake-connector-nodejs: NodeJS driver

Node.jsドライバのインストール

対応しているNode.jsのバージョンはv10,v12ですのでその点のみご注意ください。

インストールは簡単でNode.jsの環境があれば以下のコマンドでインストールできます。

npm install snowflake-sdk

それでは実際にSnowflakeに接続してSQLを実行してみます。

Snowflakeからデータを取得する

Node.jsドライバの使い方は他のデータベースへの接続と同じ様な手順で行います。

  1. Snowflakeと接続を確立する
  2. クエリを実行する
  3. 結果を取得する

一つ一つを観るよりもコード全体を見たほうがわかりやすいと思います。 以下が実行するコードになります。

export default async function snowflakeSample() {
    // Load the Snowflake Node.js driver.
    const snowflake = require('snowflake-sdk');

    // Create a Connection object that we can use later to connect.
    const connection = snowflake.createConnection({
            account: '{Snowflakeのアカウント名}',
            username: '{ユーザー名}',
            password: '{パスワード}',
            // option
            warehouse: '{ウェアハウス名}',
            database: '{データベース名}',
        },
    );

    // Try to connect to Snowflake, and check whether the connection was successful.
    const connectSF = () => {
        return new Promise((resolve, reject) => {
            connection.connect(
                (err, conn) => {
                    if (err) {
                        console.error('Unable to connect: ' + err.message)
                        reject(err)
                    } else {
                        console.log('Successfully connected to Snowflake.')
                        resolve(conn)
                    }
                });
        });
    }

    // Execute Query
    const execQuery = (query, bind: (string | number)[] = [], fetchasstring: string[] = []) => {
        return new Promise((resolve, reject) => {
            connection.execute({
                sqlText: query,
                binds: bind,
                fetchAsString: fetchasstring,
                complete: (err, stmt, rows) => {
                    if (err) {
                        console.error('Failed to execute statement due to the following error: ' + err.message)
                        reject(err)
                    } else {
                        console.log('Successfully executed statement: ' + stmt.getSqlText())
                        resolve(rows)
                    }
                }
            });
        });
    }

    try {
        // Snowflakeへ接続確立
        await connectSF()

        // クエリの実行
        const ret1 = await execQuery('SELECT t.* FROM PUBLIC.SF_IRIS t limit 3')
        console.log(ret1)
    } catch (err) {
        console.error("エラー発生");
    }
}

snowflakeSample()

Snowflakeへの接続確立とクエリの実行ではCallbackで結果が返ってくるのでPromise化して使い勝手を良くし、メイン処理ではPromise化したものをasync/awaitを使って呼び出して同期処理的な書き方にして見通しを良くしています。

上記の実行結果は以下になります。

Successfully connected to Snowflake.
Successfully executed statement: SELECT t.* FROM PUBLIC.SF_IRIS t limit 3
[
  {
    SEPAL_LENGTH: 5.1,
    SEPAL_WIDTH: 2.5,
    PETAL_LENGTH: 3,
    PETAL_WIDTH: 1.1,
    SPECIES: 'versicolor'
  },
  {
    SEPAL_LENGTH: 5.8,
    SEPAL_WIDTH: 2.6,
    PETAL_LENGTH: 4,
    PETAL_WIDTH: 1.2,
    SPECIES: 'versicolor'
  },
  {
    SEPAL_LENGTH: 6.4,
    SEPAL_WIDTH: 3.2,
    PETAL_LENGTH: 4.5,
    PETAL_WIDTH: 1.5,
    SPECIES: 'versicolor'
  }
]

次にconnection.executeメソッドにはいくつかパラメータがあるので使ってみます。

Bind変数を使う(bindsパラメータ)

SnowflakeのNode.jsドライバでもクエリにBind変数が扱えます。

例)

select 111 as a, 'bbb' as b, 3333 as c

上の様なクエリはBindのパラメータを使った形で記述すると

?シンタックスを使って

const ret1 = await execQuery(
    'select ? as a, ? as b, ? as c',
    [111, 'bbb', 3333]
)

もしくはインデックスを使って

const ret1 = await execQuery(
    'select :1 as a, :2 as b, :3 as c',
    [111, 'bbb', 3333]
)

と書けます。結果は以下の通りです。

[ { A: 111, B: 'bbb', C: 3333 } ]

データ型をStringにする(fetchAsStringパラメータ)

Snowflakeからデータを取得した際にSnowflakeのデータ型に応じで自動的にJavascriptのデータ型にマップされます。変換される型は Data Type Casting | Using the Node.js Driver — Snowflake Documentation に詳しく書かれていますので必要に応じてご確認ください。

この機能は便利なのですが、 SnowflakeのNUMBER(p,s)型がJavascriptのNumber型だと桁溢れしてしまう場合や日付型をそのまま文字列として扱いたい場合などいくつか使い勝手の悪いシーンもあります。

その様な場合にはfetchAsStringパラメータを使って、指定したJavascriptのデータ型を自動変換せずに文字列型で取得することができます。

例)

const ret1 = await execQuery('select 123456789123456789.123456789 as a,current_date() as b,current_timestamp() as c')
console.lot(ret1)

結果

[
  {
    A: 123456789123456780,
    B: 2021-02-09T00:00:00.000Z {
      getEpochSeconds: [Function],
      getNanoSeconds: [Function],
      getScale: [Function],
      getTimezone: [Function],
      getFormat: [Function],
      toJSON: [Function]
    },
    C: 2021-02-10T03:05:08.127Z {
      getEpochSeconds: [Function],
      getNanoSeconds: [Function],
      getScale: [Function],
      getTimezone: [Function],
      getFormat: [Function],
      toJSON: [Function]
    }
  }
]

fetchAsStringパラメータを使った例)

const ret1 = await execQuery('select 123456789123456789.123456789 as a,current_date() as b,current_timestamp() as c', [], ['Number', 'Date'])
console.log(ret1)

結果

[
  {
    A: '123456789123456789.123456789',
    B: '2021-02-09',
    C: '2021-02-09 19:13:32.195 -0800'
  }
]

この様に数値が桁落ちすることもなく、日付も文字列のまま取得できています。

まとめ

Node.jsドライバ for Snowflakeを使ってみました。変わったクセもなくイメージ通りに使えてかなり使い勝手は良い感じたため、簡単にSnowflakeへ接続してデータを取り扱えます。

最後まで読んで頂いてありがとうございました。