銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

npm「list-it」― コンソールへ列を揃えてデータを表示

コンソールへの表形式でのデータ表示を支援するNode.jsのモジュール(npm)「list-it」のご紹介。

2次元配列的なデータの列幅を揃えてキレイに表示。

文字列は左寄せ、数値は右寄せ。同じ列内の数値の小数点の位置も揃えます。

npmは、便利なツールが揃っていて、気楽にコードが書けるのがヨロシイですね。

f:id:takamints:20160501161451p:plain



駆け足でご説明するざっくりした使い方

  1. require("list-it")する。
  2. buffer()メソッドで空のバッファを作成。
  3. d()メソッドで、現在の行へ列(セル)データを追加。複数指定も可。
  4. nl()メソッドで現在行への列の追加を終了し次の行へ。
  5. 追加できるデータの型は文字列か数値。配列は行の追加になる(行追加時はnl()不要)
  6. 複数の配列を与えると複数の行が出来上がります。2次元配列でも可。
  7. toString()で文字列化してconsole.log()

機能の概要

表形式のデータを放り込んでコンソールへそのまま表示できる文字列を生成します。列ごとに数値の小数点位置を揃えられます。なんとなく便利でしょ?

  • autoAlign(自動整列)モードON(デフォルト)で、数値は小数点位置を揃えて右寄せ、文字列は普通に左寄せ。
  • 文字列の色や表示スタイルにANSIエスケープシーケンスが使用できます。カーソル移動とかは無理ですけどね。エスケープシーケンスのリセットは自動では行わないので、追加文字列の最後にリセットシーケンスを入れておいてください。

サンプルコードと出力例

japanese-foods.js

const ListIt = require("list-it");//①
const buf = new ListIt();      //②
console.log(
    buf
        .d("1").d("Sushi")      //③
            .d("vinegared rice combined raw seafood")
            .d("Healthy").nl()  //④
        .d("2").d("Yakiniku")
            .d("Grilled meat on Japanese")
            .d("Juicy").nl()
        .d("3").d("Ramen")
            .d("Japanese noodle soup dish")
            .d("I like it").nl()
        .d("4").d("Tempura")
            .d("Deep fried seafood or vegetables")
            .d("Delicious").nl()
        .d("5").d("Sashimi")
            .d("Very fresh sliced fish")
            .d("Try it now, It's good").nl()
        .toString());

出力: 文字列は左寄せです

$ node sample/japanese-food.js
1 Sushi    vinegared rice combined raw seafood Healthy
2 Yakiniku Grilled meat on Japanese            Juicy
3 Ramen    Japanese noodle soup dish           I like it
4 Tempura  Deep fried seafood or vegetables    Delicious
5 Sashimi  Very fresh sliced fish              Try it now, It's good

autoAlign

autoAlignオプションをtrueにすれば(デフォルトでtrueです)、データの型を考慮してセルの整形を行います。

planets.js

const ListIt = require("list-it");
const buf = new ListIt({ "autoAlign" : true });
var PLANETS = [
    ["NAME", "Mass(10^24kg)", "Dia(km)", "Dens(kg/m3)",
                        "Grav(m/s2)", "EscV(km/s)", "Rot(hours)" ],
    ["MERCURY", 0.33,   4879,   5427,   3.7,    4.3,    1407.6  ],
    ["VENUS",   4.87,   12104,  5243,   8.9,    10.4,   -5832.5 ],
    ["EARTH",   5.97,   12756,  5514,   9.8,    11.2,   23.9    ],
    ["MOON",    0.0073, 3475,   3340,   1.6,    2.4,    655.7   ],
    ["MARS",    0.642,  6792,   3933,   3.7,    5.0,    24.6    ],
    ["JUPITER", 1898,   142984, 1326,   23.1,   59.5,   9.9     ],
    ["SATURN",  568,    120536, 687,    9.0,    35.5,   10.7    ],
    ["URANUS",  86.8,   51118,  1271,   8.7,    21.3,   -17.2   ],
    ["NEPTUNE", 102,    49528,  1638,   11.0,   23.5,   16.1    ],
    ["PLUTO",   0.0146, 2370,   2095,   0.7,    1.3,    -153.3  ]
];
console.log( buf.d( PLANETS ).toString() ); //⑤、⑥

出力: 数値は小数点の位置を揃えて右寄せ

$ node sample/planets.js
NAME    Mass(10^24kg) Dia(km) Dens(kg/m3) Grav(m/s2) EscV(km/s) Rot(hours)
MERCURY        0.33      4879        5427        3.7        4.3     1407.6
VENUS          4.87     12104        5243        8.9       10.4    -5832.5
EARTH          5.97     12756        5514        9.8       11.2       23.9
MOON           0.0073    3475        3340        1.6        2.4      655.7
MARS           0.642     6792        3933        3.7        5.0       24.6
JUPITER     1898.0     142984        1326       23.1       59.5        9.9
SATURN       568.0     120536         687        9.0       35.5       10.7
URANUS        86.8      51118        1271        8.7       21.3      -17.2
NEPTUNE      102.0      49528        1638       11.0       23.5       16.1
PLUTO          0.0146    2370        2095        0.7        1.3     -153.3

日本語(ワイド文字)

japanese-foods-jp.js

const ListIt = require("list-it");
const buf = new ListIt();
console.log(
    buf
        .d("1").d("寿司")
            .d("酢とご飯とシーフード")
            .d("健康的だ").nl()
        .d("2").d("焼肉")
            .d("日本のグリルされたお肉")
            .d("ジューシー").nl()
        .d("3").d("ラーメン")
            .d("日本のスープに入った麺")
            .d("大好き").nl()
        .d("4").d("天ぷら")
            .d("シーフードや野菜に衣をつけて揚げたもの")
            .d("おいしー").nl()
        .d("5").d("刺身")
            .d("大変フレッシュな魚のスライス")
            .d("食べてみて!あご落ちるぜ").nl()
        .toString());

出力: HTMLでは何故か微妙にズレるけどコンソールではズレてません。

$ node sample/japanese-food-jp.js
1 寿司     酢とご飯とシーフード                   健康的だ
2 焼肉     日本のグリルされたお肉                 ジューシー
3 ラーメン 日本のスープに入った麺                 大好き
4 天ぷら   シーフードや野菜に衣をつけて揚げたもの おいしー
5 刺身     大変フレッシュな魚のスライス           食べてみて!あご落ちるぜ

未実装機能(将来追加する可能性はあり…ます…)

  • 文字列の折り返しができるようになればいいな。列幅指定してとか、コンソールの幅を判別して、自動的に列を折り返すとかできると超クール。
  • 見出し行とか見出しセルの概念を、我、欲す。行、列、セル単位でスタイル指定したい。
  • 表を転置(トランスポーズ:行列入れ替え)したいときがたまにある。
  • 演算機能があってもよいかなとか。あ、行列演算できてもよいか?いや、それはやりすぎか。

リポジトリ

メソッドの詳細などは、以下のリポジトリにあります。どう考えてもおかしな結果が得られたって時は、バンバンIssue入れてください。

GitHub

github.com

npm

www.npmjs.com

その他

これ、結構需要がある気がしていますが、プロジェクト単位でガチガチに力技で作ったりしがちですよね。 また、こういったモジュールは既にあるのかもしれませんが、軽く検索して見つけられなかったので作りました。

npmにはまる以前に作っていた別ソフトがベースになっています。 当時必要だと感じていた機能以上に、あれもこれもとドンドン実装したくなるから面白いもんですね。

npm 「hash-arg」 ― コマンドラインパラメータに名前でアクセス

コマンドラインで指定されたパラメータに名前でアクセスする為の、シンプルなNode.jsのモジュール hash-argをnpmで公開したので御紹介します。

npm便利ですね。若干敷居が高い気がしていましたが、まずはいろんなモジュールを検索して使うところから始めるといいですね。

f:id:takamints:20160403163627p:plain

'hash-arg'コマンドラインパラメータに名前でアクセスするためのシンプルなnpmモジュールです。

www.npmjs.com

ソースはGitHibに置いています。

github.com


JavaScript 第6版
JavaScript 第6版
posted with amazlet at 16.04.03
David Flanagan
オライリージャパン
売り上げランキング: 16,569

はじめに

このモジュールは、コマンドラインオプションの処理をするものではありません

単純に、コマンドライン引数、または配列の各要素に、前から順に名前を付けてハッシュ(JavaScriptのobject)を返すモジュールです。 追加の機能として、各パラメータの型指定と、省略時の既定値の指定ができますが、基本的には単純なものです。

Node.jsのコマンドラインパラメータは、process.argv[2] 以降に文字列として格納されています([0][1]"node"と、スクリプト名)から、本モジュールにパラメータの定義(名前、型、既定値)だけを与えた場合は、process.argv[2]以降の要素を処理対象とします。 別の配列を与えた場合は、すべての要素を処理します。

オプションが含まれる場合

コマンドライン引数に'-'で始まるようなオプションを含ませたい場合は、先にnode-getoptのようなオプションのパーサーで処理しておいて、そのモジュールが(おそらく)提供している、「オプションではない引数の配列」を、当モジュールで処理させます。

少しだけ気楽になるモジュールです(多分)

オプションではない引数が配列に入っている場合、その要素数をチェックして、必要なパラメータが足りていなかったりすると、エラーにしたり、省略時の既定値を設定したりしますよね。これって、少し邪魔くさい。

本来ザクザク書きたいのはそういうところじゃないはずです。 どの位置に何が指定されていたのか?という情報は、その後、使わない情報ですし。

あと、最初のパラメータなのにprocess.argv[2]とか書いちゃうあの妙な違和感(笑)もどうにかできます。

使い方:単純に'process.argv'を使用する場合

唯一のメソッドgetの第一引数にパラメータ名の定義を与えて呼び出せば、process-argvから値を取り出し、オブジェクトの指定されたキーに値を格納して返します。 (第二引数は省略可能なパラメータの配列です)

simple.js

args = require("hash-arg").get(
        "inputFilePath outputFilePath");
 
console.log(JSON.stringify(args, null, "  "));

実行例:

$ node test/simple.js input.json output.json
{
  "inputFilePath": "input.json",
  "outputFilePath": "output.json"
}

使い方:node-getoptのようなコマンドラインパーサーと共に使用

省略可能な第二引数に、たとえば、node-getoptモジュールの argv プロパティを与えればOK。

with-node-argv.js

getopt = require("node-getopt").create([
    ['s', '', 'short option'],
    ['l', 'long', 'long option'],
    ['S', 'short-with-arg=ARG', 'option with argument']
]).parseSystem();
 
args = require("hash-arg").get([
        "inputFilePath",
        {
            "name":"outputFilePath",
            "default": "out.json"
        }
        ], getopt.argv);
 
console.log(JSON.stringify(args, null, "  "));

実行例:

$ node test/with-node-getopt.js -S DUMMY input.json -sl output.json
{
  "inputFilePath": "input.json",
  "outputFilePath": "output.json"
}

GET メソッド

呼び出し形式

HashArg.get(<argument-def> [, <argv-source-array>]);

argument-def

パラメータの定義を行う。

単一の文字列、または、文字列配列、または、パラメータ定義オブジェクトの配列を指定できる。

1) 文字列の場合

パラメータのキー名称をスペースで区切った文字列

例)

"inputFilePath outputFilePath"

2) Array of string

各要素がパラメーター名である文字列の配列。

例)

["inputFilePath", "outputFilePath"]

3) Array of definition object

このフォーマットでは、省略時のデフォルト値を指定できます。

デフォルト値が指定されていない場合は、nullが使用されます。

[
    {"name":"inputFilePath"},
    {
        "name"      : "outputFilePath",
        "default"   : "out.json"
    }
]

argv-source-array (省略可能)

コマンドラインパラメータを保持する文字列配列。

省略時はprocess.argvが使われます。

※ 注意:v0.0.2以前では、getの第二引数に、process.argvを与えると誤動作します。

定義より多くパラメータが指定されたら

v0.0.3以上では、パラメータ定義の数よりもコマンドラインで指定されたパラメーターが多い場合、未定義のパラメータが、返されるオブジェクトの空文字のキーに配列として保存されます(v0.0.2以前ではそれらは失われていました)。

args = require("hash-arg").get("inputFilePath outputFilePath");
console.log(JSON.stringify(args, null, "  "));

実行例:

$ node test/with-node-getopt.js input.json output.json foo bar
{
  "inputFilePath": "input.json",
  "outputFilePath": "output.json",
  "": ["foo", "bar"]
}

ライセンス

MIT

その他

単に利用するだけなら、npm install hash-argでOKですよ。


Node.jsで正常終了するとき process.exit(0) を呼んではいけない

f:id:takamints:20160402113544p:plain

Node.jsで正常終了するとき、 process.exit(0) を明示的に呼び出すべきではないってことを書いています。



関連記事:

takamints.hatenablog.jp

勢い余って呼んじゃった

Node.jsでコマンドラインのツールを書くとき、特に他の人に使ってもらうようなものだと特に、エラー処理をキチンと書くように努めています。

エラーメッセージを分かりやすく表示して、process.exit(-1) で、OSに対して異常終了を示すわけですが、勢い余って正常終了時に、ついつい process.exit(0) と書いてしまったのです。

その結果、「正常終了しているけれど、処理が正しくできてない」というよくわからない状況に困惑しました。

以下のようなコードです。

var fs = require("fs");
if(process.argv.length < 2) {
    console.error("エラー:ファイル名なし");
    process.exit(1);
}
fs.readFile(process.argv, "utf-8", function(err, data) {
    //
    //ファイルを読み込んであれこれする
    //
});
process.exit(0); //ここで正常終了ですよ

process.exitは「非同期処理を待ってくれない」

もうお気づきでしょうね最後の行です。

これだと readFileのコールバックが呼ばれる前に、プロセスが正常終了しちゃいます。

「nodeのプロセスは、全てのコールバックが終わってから終了する」けど、明示的にprocess.exitを呼び出したら、その時点で終了してしまいます。

これ当たり前。そうでなきゃ困るんだけど、意識していなかった。

呼び出さないほうがよい

ということで早速結論

Nodeのスクリプトで正常に処理を終了するときは、process.exitを呼び出さないほうが良い。

呼び出さなければ全部の処理を確実に実行してから、自動的に終了コード=0で終了してくれますが、 呼び出していると「これ、本当に全部の処理が行われてから正常終了しているの?」と疑うことが出来てしまいます。 コードレビューで指摘されると正統性を示すのが結構大変になりそうですし。

よく「呼び出す必要が無い」と説明されているのですが、もうチョット強めのニュアンスの方が良いのではないかと思います。 「呼び出さないほうがよい」ぐらいのほうが適当かと。

おまけ:0以外の終了コードで正常終了する方法

じゃあ0以外の終了コードで正常終了を表したい場合は、どうすればいいの?って思いますよね。

以下のようにすると良いですよ。

process.on("exit", function() {
    process.exit(1);
});

確認してみる。

$ node -e "process.on('exit', function(){process.exit(255);});"

$ echo $?
255

$

OKですね。


Nodeクックブック
Nodeクックブック
posted with amazlet at 16.04.02
David Mark Clements
オライリージャパン
売り上げランキング: 277,422

AWS Lambdaの関数アップロードをお手軽に

Lambda関数を、ローカルPCからアップロードするコマンドをご紹介。ダウンロードも出来ますよっと。

Lambda関数はAWSのWEBコンソールで編集できますが、数が増えるとやってらんない。そして外部モジュールを使う場合はWEBでは無理ですし。

でき得るならばローカルで、編集したのをアップロードしたいのだけど、その都度ZIPしてアップしてとか、それはそれで手間がかかってイライラします…

f:id:takamints:20160309213632p:plain
photo credit: 150120-N-ZZ999-002 via photopin (license)

てなことで、怒りに任せて(笑)取り急ぎ、勢いで3つばかり作ってしまいました。

その後、いろいろ更新してnpmとして公開してます。 最新版では、SQLライクな構文でDynamoDBを操作できるようになりました。 これについては以下の記事で説明してます。

takamints.hatenablog.jp


目次

  1. コマンド
    1. aws-lambda-get - Lambda関数をダウンロード
    2. aws-lambda-upload - Lambda関数を更新(上書き)
    3. aws-lambda-create - Lambda関数を新規作成
  2. 機能説明
    1. ローカルのLambda関数のディレクトリ構造
    2. アップロード前に特定の処理を実行する
  3. セットアップ
    1. 実行環境と必要なツール等
    2. インストール方法
    3. リポジトリ


aws-node-utilDynamoDB関連記事はこちらです:

takamints.hatenablog.jp


1. コマンド

1-1. aws-lambda-get - Lambda関数をダウンロード

AWSのLambda関数をダウンロードします。

$ aws-lambda-get <function-name>

パラメータ

  • function-name - Lambda関数名。

ちゃんとダウンロードできたら、カレントディレクトリに関数名のディレクトリが作られ、全てのファイルが展開されます。

安心してください、上書きしませんよ

既に同名のサブディレクトリがカレントディレクトリ直下にある場合はダウンロードしません。 ディレクトリの名前を変更して再実行してください。

1-2. aws-lambda-upload - Lambda関数を更新(上書き)

AWSのLambda関数をローカルファイルで更新します。

カレントディレクトリ直下の関数名のディレクトリをZIPファイルにまとめてAWSへアップロードして上書きます。

$ aws-lambda-upload <function-name>

パラメータ

  • function-name - Lambda関数名。カレントディレクトリ直下に同名のサブディレクトリが必要です。それをZIPしてアップします。

1-3. aws-lambda-create - Lambda関数を新規作成

ローカルのLambda関数をAWSへアップロードし、新規作成します。

カレントディレクトリ直下の関数名のディレクトリをZIPファイルにまとめてAWSへアップロードします。

$ aws-lambda-create <function-name> <role-arn>

パラメータ

  • function-name - Lambda関数名です。カレントディレクトリ直下に同名のサブディレクトリが必要です。それをZIPしてアップします。
  • role-arn - Lamdaに付けられるRoleのarnを指定してください。RoleはAWS IAMで作成出来ます。

Node.js限定です

新規作成に関しては、言語はnode.jsだけで、ハンドラーは index.handlerに固定されています。

生成だけなら別途awsコマンドでやってもたいした手間ではありません。その後、uploadすれば大丈夫なんじゃないかな?(試してないのでスミマセン)

2. 機能説明

2-1. ローカルのLambda関数のディレクトリ構造

AWSからダウンロードするとカレントディレクトリ直下に関数名のサブディレクトリが作成されます。 このディレクトリは、そのままアップロードできる状態になっています。

たとえば、以下のようにHelloLambdaという関数をダウンロードすると、 カレントディレクトリ直下にディレクトHelloLambdaが作成され、全ファイルが展開されます。

$ aws-lambda-get HelloLambda

以下は、HelloLambdaがモジュールを使用している場合の例です。

./HelloLambda/
    index.js
    node_modules/
      node-uuid/
        ....

2-2. アップロード前に特定の処理を実行する

アップロードする前に、Lambda関数が利用しているモジュールを更新したり、エディタのテンポラリファイルを削除したくなると思います。

各関数のディレクトリ直下に、.onupload.sh というファイルがあれば、アップロード前(正確にはZIPで固める前)に、これをシェルスクリプトとして実行します。

このファイル自体はAWSへはアップロードされません。

#node-uuidをインストール
if [ ! -d node_modules ]; then
    npm install node-uuid
else
    npm update node-uuid
fi
#VIMのバックアップファイルを消す
rm *~ .*~ 

3. セットアップ

3-1. 実行環境と必要なツール等

利用するには、以下のものが必要です。

  • AWSのアカウント - これがないと始まらない。今なら登録から1年間は基本的に無料。
  • AWSCLI - AWSコマンドラインインターフェースです。AWSからダウンロードしてインストールし、AWSアカウントで接続できるようにしておいてください。各コマンドから利用しています。
  • Bash - コマンドがシェルスクリプトなので必要です。
  • Node.js - JSONの処理等に利用しています。将来的には全部Nodeでやりたいです。
  • Zip/Unzipコマンド - Lambda関数のアップロード、ダウンロードで必要になります。(Bashで最初から利用できるのかも?)

3-2. インストール方法

npmにpublishしたので、インストールは npm install -g aws-node-utilでOKです。

インストールスクリプトINSTALL.sh

$ sh INSTALL.sh

実行すると、各スクリプト~/binへコピーし、node_modules以下のファイルを ~/node_modules へコピーしています。

かなりいい加減なインストールスクリプトで申し訳ないです。当方MinGW使ってるのでこれで済んでいます。

可能ならば、Forkして適切に修正してください。そしてプルリク下さい。よろしくお願いいたします。

3-3. リポジトリ

ソースは全て以下のGitHubに置いています。 ご自由に利用なさってください(MIT license)。 他にも雑多なスクリプトが入っていますが、それはそれでまたおいおい。

github.com

AWS LambdaからDynamoDBをQueryする

f:id:takamints:20160223124534p:plain
photo credit: Violet electric lightning versus turquoise storm - 09072012 via photopin (license)

JavaScript(Node.js)で記述した AWS Lambda から AWS-SDK を使って DynamoDBのテーブルをQueryするサンプルコードをご紹介。

ところでAWSAPIのリファレンスって検索しにくいですよねえ。 Lambdaがいろんな言語に対応しているため、REST APIと各言語向けの説明が別れていてチュートリアルも検索結果にたくさん出てきます。

※ 本記事ではDynamoDBのQueryするときのパラメータを説明します。 関連する記事として、検索条件を入力してQueryパラメータを生成するWebアプリは以下になります。合わせて参照してみてください。

takamints.hatenablog.jp

さらに、 DynamoDBをSQL(的な文)で扱う方法はコチラに書いています。

takamints.hatenablog.jp

DynamoDBのAPIは「Welcome Amazon DynamoDB」で説明されていますが、こちらはREST APIの説明で、各言語から呼び出せるメソッドについて直接記述されているわけではありません。だから定型的な読み替えと試行錯誤と推測が必要です。

日本語に翻訳されている文書もありますが、おおかた機械翻訳のようでして、かなり不可解な表現があったりします。 はなから英語で読んだほうが理解しやすいところもあったり。

などと、愚痴ってばかりでは始まらないので、ここはひとつ怒りに任せて(笑)Node.jsのLambda関数からDynamoDBを操作するサンプルコードをご紹介。

※ ここではAmazon DynamoDBAWS Lambdaについて、ひと通り基本的な知識を持っているって前提で書いてます。ワタシもAWS初心者なんですけどエラそうで申し訳ない。
※ フロントエンドは Amazon API Gatewayを利用している前提です。このため、実行結果はcontext.doneでJSON文字列を出力しています。


AWSエキスパート養成読本[Amazon Web Servicesに最適化されたアーキテクチャを手に入れる! ] (Software Design plus)
吉田 真吾 今井 智明 大瀧 隆太 松井 基勝 冨永 善視 藤原 吉規 大栗 宗
技術評論社
売り上げランキング: 1,300

DynamoDB API Query - キー属性で項目を検索

DynamoDBのテーブルから検索条件に合致した項目を問い合わせるためには、Query を使用します。条件無しで全項目を得るには、Queryではなく Scan を使用します。

Queryの検索条件には、少なくともプライマリーキーの条件が指定されなくてはならず、これに加えて、プライマリーソートキーやセカンダリーインデックスの条件も AND / OR で連結して記述できます。

さらに、プライマリーキー以外の項目によるフィルター条件も指定できますが、これは、あくまでも上記のプライマリーキーによる検索結果を得た後に対して絞りこむものですから、クエリ自体のパフォーマンスには、ほぼ無関係でしょう。(レスポンスが少なくなるので、通信速度には影響すると思われます)

サンプルコード(Node.js)

以下のように少しややこしい。初めて見たら軽くめまいを覚えそうです。

var AWS = require('aws-sdk');
var dynamo = new AWS.DynamoDB({
    region: 'ap-northeast-1'
});
var tableName = "myTable";
exports.handler = function(event, context) {

    //パラメータ定義
    var params = {
        "TableName": tableName,
        //キー、インデックスによる検索の定義
        "KeyConditionExpression":
            "id = :Id AND #Ts BETWEEN :T0 AND :T1",
        //プライマリーキー以外の属性でのフィルタ
        "FilterExpression":
            "#Fn &gt; :Fn",
        //属性名のプレースホルダの定義
        "ExpressionAttributeNames": {
            "#Ts": "timestamp",
            "#Fn": "value.floor"
        },
        //検索値のプレースホルダの定義
        "ExpressionAttributeValues": {
            ":Id" : { "S" : event.id },
            ":T0" : { "S" : event.T0 },
            ":T1" : { "S" : event.T1 }
            ":Fn" : { "S" : event.Fn }
        }
    };
    //クエリ実行
    dynamo.query(params, function(err, data) {
        if (err) {
            context.fail(
                new Error("Fail. err:" + err));
        } else {
            var response = JSON.stringify(data);
            context.done(null, response);
        }
    });
};

ポイントは、params。 ここではテーブルの指定以外に以下の3つのことをやっています。

  1. プライマリーキーによる検索を検索値のプレースホルダを使用して定義する
  2. プライマリーキー以外の属性で、キーの検索結果をフィルタする
  3. 属性名が予約語とかち合った場合は属性名のプレースホルダーで解決する

以降で、これらパラメータについて、詳細に説明します。

パラメータ

パラメータは第一引数に、objectとして与えられます。 下表に、ここで使用しているパラメータの概要です。

No. キー 説明
1 TableName string テーブル名を指定する。
2 KeyConditionExpression string キー属性での検索定義
3 ExpressionAttributeValues object 検索値のプレースホルダを定義する。
4 ExpressionAttributeNames object 属性名のプレースホルダを定義する。
5 FilterExpression string 検索後のフィルター条件を記述します。
  • 1~3は必須のはずです。 APIの説明ではTableNameだけ必須と書いてありますが、QUERYでは少なくともKeyConditionExpressionExpressionAttributeValuesを指定しないといけません。
  • 「4. ExpressionAttributeNames」は、検索する属性名に予約語が使われている場合に、読み替えを行うため必要となるパラメータ。
  • 他のパラメータについては、Query - Amazon DynamoDBで御確認ください。

KeyConditionExpression - キー、インデックスによる検索を定義する

KeyConditionExpressionではテーブル作成時に指定したキーによる検索条件を記述します。キー以外の条件は使えません。キー以外で絞り込みたい場合は、フィルターを使用する必要があります(フィルターはキーに関する制約を除けばKeyConditionExpressionと同じように条件を記述します)。

DynamoDBのテーブルのキーはパーティションキーとレンジキーの二種類があり、このうちパーティションキーは必ず検索条件に指定しなければなりません。レンジキーはオプションです。

検索条件の値は直接記述できません。値は検索値のプレースホルダを使って記述しなければなりません。 検索値のプレースホルダは、:(コロン)で始まる名称です。値に対する仮引数みたいなものですね。 具体的な値をプレースホルダに紐づけるには、次項で説明する ExpressionAttributeValues を使用します。

以下の :placeholderName が検索値のプレースホルダ

{
    "KeyConditionExpression":
        "keyAttrName = :placeholderName",
}

KeyConditionExpression についてまとめると以下のようになります。

ExpressionAttributeValues - 検索値のプレースホルダを定義する

ExpressionAttributeValuesには、KeyConditionExpressionやFilterExpressionに記述されているプレースホルダの値を型とともに指定します。

{
    "KeyConditionExpression":
        "keyAttrName = :placeholderName",
    "FilterExpression":
            "attrName > :placeholderName2",
    "ExpressionAttributeValues": {
        ":placeholderName":  { "S" : "11" },
        ":placeholderName2": { "N" : "12" }
    }
}

チュートリアルでは値だけが指定されていますが「structureじゃないとダメですよ」という例外が投入されます。

ExpressionAttributeNames - 属性名のプレースホルダを定義する

DynamoDBのテーブルの属性名に予約語が使われている場合、検索条件を記述できません(「属性名がキーワードだ」との例外投入) ので、ExpressionAttributeNamesで置き換えます。

以下の例では、予約語timestampと同じ属性名を、読み替えています。(#Tmの部分)

        "KeyConditionExpression": "#Tm = :T0",
        "ExpressionAttributeNames": {
            "#Tm": "timestamp"
        },
        "ExpressionAttributeValues": {
            ":T0" : { "S" : "" + event.t0 }
        }

他のAPI

テーブルの項目を操作するAPIは以下の様なものがあります。

  • 項目の問い合わせ - Query
  • 全項目取得 - Scan
  • 特定項目を取得 - GetItem
  • 項目上書き - PutItem
  • 項目変更 - UpdateItem
  • 項目削除 - DeleteItem
  • バッチ項目取得 - BatchGetItem
  • バッチ項目更新 - BatchWriteItem

  • PutItemとUpdateItemは紛らわしいけど、上書きしてしまうか、特定の属性の変更が行えるかの違いらしい(詳細不明)

  • バッチ系はちょっと使い勝手が違うかもしれませんが、一度にやっちゃう感じでしょう(詳細不明)
  • その他、テーブルを操作するAPIDynamoDBのAPIの説明ページ に書いてあります。

AWS関連記事

Node.js / npm 関連記事



その他、一般事項

Node.jsから呼び出すAPIのメソッド名

DynamoDBのAPIの説明ページ に掲載されている API の名称をキャメルケースに変換すれば、Node.jsのメソッド名になると思います(多分)。

DynamoDB APIの基本形

Node.jsでのDynamoDB APIは、以下のような形式で呼び出します。

dynamo.apiName(params, function(err, data) {
    if(err) {
        //エラー;
    } else {

        //dataにAPIの実行結果が入っています。

    }
});

(↑)apiNameをAPIの名前に書き換え。


RDB技術者のためのNoSQLガイド
渡部 徹太郎 河村 康爾 北沢 匠 佐伯 嘉康 佐藤 直生 原沢 滋 平山 毅 李昌 桓
秀和システム
売り上げランキング: 40,238