DynamoDBをSQLで操作するNodeモジュール
Amazon DynamoDBをSQL的な記述言語(SQL-ish)で操作できるNodeモジュールのご紹介。
目次
概要
このモジュールからはDynamoDBのテーブルをSQL的な文(SQL-ish)で操作できるステートメントを提供しています。
ステートメントを生成する関数は以下の4つ:
- awsNodeUtil.dynamodb.ScanStatement - テーブルをスキャンします。
- awsNodeUtil.dynamodb.QueryStatement - テーブルをクエリします。
- awsNodeUtil.dynamodb.PutItemStatement - 項目を追加、またはキーを指定して上書きできます。
- awsNodeUtil.dynamodb.DeleteItemStatement - キーを指定して項目を削除。
それぞれ、引数にSQL(によく似た)文を与えてステートメントを生成します。 各ステートメントは後からパラメータを与えて実行可能(※ただしDeleteItemStatementは現在パラメタライズが出来ません)。
このほか、ScanとQueryの結果の中の Itemsを(DynamoDBのマップ型の配列から)普通のオブジェクトの配列に変換する DynamoDbResultSet クラスの getItemsメソッドが使えます。
また、v0.9.6では、 PutItemStatement が返す DynamoDBPutItemStatementクラス の setValuesメソッドで、新たなVALUESを設定してステートメントを実行できます。
以下にScanとQueryの簡単なサンプルコードを掲載してます。 他のステートメントのサンプルやAPIの詳細はAPIリファレンスを参照してくださいね。
SQL-ish Statement クラスの使用例
実際に動作させるには、DynamoDBにstarsというテーブルがあり、HASHキーがString型のmainStar、RANGEキーがNumber型のorbitOrderとなっている必要があります。
"use strict"; const awsNodeUtil = require("aws-node-util"); const ScanStatement = awsNodeUtil.dynamodb.ScanStatement; const QueryStatement = awsNodeUtil.dynamodb.QueryStatement; const ResultSet = awsNodeUtil.dynamodb.ResultSet; // Connect (change each value for your account) awsNodeUtil.dynamodb.connect( // { accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-west-2' } ); // Prepare 'Scan' statement var scanStatement = ScanStatement( "FROM stars WHERE name=:name"); // Prepare 'Query' statement var queryStatement = QueryStatement( "SELECT mainStar, orbitOrder, name " + "FROM stars " + "WHERE mainStar=:mainStar"); // Run the statements ScanStatement("FROM stars").run({}, (err, resp) => { console.log("-----------------------"); console.log("SCAN all items of stars"); console.log("-----------------------"); printResult(err, resp); scanStatement.run({ ":name": "EARTH" }, (err, resp) => { console.log("--------------"); console.log("SCAN the EARTH"); console.log("--------------"); printResult(err, resp); queryStatement.run({ ":mainStar": "EARTH" }, (err, resp) => { console.log("------------------------------"); console.log("QUERY child stars of the EARTH"); console.log("------------------------------"); printResult(err, resp); }); }); }); // Handler to print result of scan / query function printResult(err, result) { if(err) { console.error("Error:", err.stack); } else { ResultSet.printScanResult(result); } }
実行時の出力例:
$ node sample/sqlish-sample.js ----------------------- SCAN all items of stars ----------------------- Count: 10 ROWNUM diameter rotation role mass gravity density escapeVelocity name orbitOrder mainStar 1 3475 655.7 satellite 0.0073 1.6 3340 2.4 MOON 1 EARTH 2 4879 1407.6 planet 0.33 3.7 5427 4.3 MERCURY 1 SUN 3 12104 -5832.0 planet 4.87 8.9 5243 10.4 VENUS 2 SUN 4 12756 23.9 planet 5.97 9.8 5514 11.2 EARTH 3 SUN 5 6792 24.6 planet 0.642 3.7 3933 5.0 MARS 4 SUN 6 142984 9.9 planet 1898.0 23.1 1326 59.5 JUPITER 5 SUN 7 120536 10.7 planet 568.0 9.0 687 35.5 SATURN 6 SUN 8 51118 -17.2 planet 86.8 8.7 1271 21.3 URANUS 7 SUN 9 49528 16.1 planet 102.0 11.0 1638 23.5 NEPTUNE 8 SUN 10 2370 -153.3 planet 0.0146 0.7 2095 1.3 PLUTO 9 SUN ScannedCount: 10 -------------- SCAN the EARTH -------------- Count: 10 ROWNUM diameter rotation role mass gravity density escapeVelocity name orbitOrder mainStar 1 12756 23.9 planet 5.97 9.8 5514 11.2 EARTH 3 SUN ScannedCount: 10 ------------------------------ QUERY child stars of the EARTH ------------------------------ Count: 1 ROWNUM name orbitOrder mainStar 1 MOON 1 EARTH ScannedCount: 1
SQL-ishの構文
各Statementの構文を以下にざっくり説明します。
[
~ ]
は省略可能を意味します。<
~ >
は後述します。
/* Scan */ [SELECT <projection-expression>] FROM <table-name> [WHERE <filter-expression>] [LIMIT <limit>] /*Query*/ [SELECT <projection-expression>] FROM <table-name> WHERE <key-condition-expression> [FILTER <filter-expression>] [LIMIT <limit>] /*PutItem*/ INSERT INTO <table-name> ( <attribute-list> ) VALUES ( <value-list> ) [WHERE <key-condition-expression>] /*DeleteItem*/ DELETE FROM <table-name> [WHERE <key-condition-expression>]
SELECT句
SELECT句には<projection-expression>
にカンマ区切りで選択する属性名を指定します。
通常のSQLと違ってSELECT句は省略可能です。省略時は全属性が選択されます。
(「属性」は一般的なDBでは列、またはカラムに相当します)
FROM句
FROM句は必須です。
<table-name>
に検索を行うテーブル名を指定します。JOINなどは出来ません。
WHERE句
WHERE句はScanとその他のステートメントでは意味が違います。
ScanStatementでは、フィルター条件式(<filter-expression>
)を指定します。これは省略可能です。省略すると全件取得。
フィルタ条件式にはキー属性を記述できません。これが必要な場合はQueryを使用します。
他のステートメントではキーの条件式(<key-condition-expression>
)を指定します。
QueryStatementでは必ず記述しなければなりません。
キーの条件式には、キー以外の属性を記述できません。
QueryStatementでキー以外の属性で絞り込みたい時はFILTER句を使用します。
その他、キーの条件式には、以下のような制限もあります。これらは全てDynamoDBのKeyConditionExpressionの制限です。
- パーティションキーの条件は必須で、
=
で一致させなくてはなりません。 - ソートキーの条件をANDで追加できます。この条件式で使用できる比較演算子は
=
,<
,<=
,>
,>=
,BETWEEN ~ AND
だけです。IN
は使用できません。関数はbegins_with
だけ使用できます。 - 詳細はQuery - Amazon DynamoDB(英文)に書いてあります。
FILTER句
FILTER句は、QueryStatementにフィルタを設定するために記述します。 省略可能。省略時はフィルタはかかりません。
ScanStatementにFILTER句は指定できません(WHERE句に指定)。
LIMIT句
LIMIT句は<limit>
にScanする件数(数値)を指定します。
省略可能ですが注意が必要。
LIMIT句を省略するとScanStatementではWHERE句(フィルタ)の指定に関わらず、全件をスキャンすることになります。 QueryStatementでは必ずパーティションキーの条件が入っていますのである程度件数は絞られます。 いずれにせよ項目数の多いテーブルでLIMIT句を省略すると処理速度や料金に影響しますので注意して下さい。 これはDynamoDBの仕様です。
VALUES句
PutItemStatementのVALUES句は、( <attribute-list> ) VALUES ( <attribute-list> )
という形式で、
追加・上書きする項目の属性(RDBでのカラム名に相当)と値をそれぞれカンマ区切りで並べたものです。
属性と値の数は一致しなければなりません。
各値の型は表記から自動的に判断されます。
プレースホルダーについて
DynamoDBを使う上でちょっと邪魔くさい「名前のプレースホルダー」、「値のプレースホルダー」について気にする必要はありません。 本モジュール内で構文解析時に自動的に識別して変換します。
ステートメントのパラメタライズ
上の例に示しているように、パラメータ化する箇所には半角コロン:
で始まる識別子を直接記述しておけば、実行時にパラメータを与えて処理できるようになります。
DynamoDBについての基本事項
DynamoDBはNoSQL
DynamoDBはAWS ― Amazon Web Service ― のNoSQL型データベース。 リレーショナルではありませんので複数テーブルの突き合わせ(JOIN)とかは出来ません。
DynamoDBのScanとQueryの違いについて
ScanもQueryもDynamoDBのテーブルから項目を読み込む機能ですが、読み込み方に違いがあります。
Scanはキーによる絞り込みが出来なくて、項目の並び順に順次読み込み(=スキャン)する感じ。 Queryはスキャンの前にキーの条件で絞り込めます。
どちらもスキャンしたあとに「フィルター」をかけられ、スキャンする項目数を指定できます。 また、続きから読むという操作も可能です。
あとがき
DQLというPython製ですが似たコンセプトのモジュールがあるらしいですね。 使ったことがないのでよくわからないですが、シェルから実行できる対話式のクライアントという感じでしょうか。
当npmをリリースした後「似たようなのって既にあるんじゃないの?」って検索して知ったのでワタシは無罪。 しかし、こちらが後発なのは明らかなので今後構文的にDQLに寄せていくのもありかなと思っています。BNFもドキュメントで規定されているし。