まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:javascript

IBM Bluemix からも提供されている IBM の DBaaS サービスである dashDB に Node.js からアクセスする方法を紹介します。実際には dashDB だけでなく、DB2 のサービスやオンプレミスデータベースへも同様に応用できますが、今回は Bluemix 上の DB2/dashDB 関連サービスを例に紹介します:
2017063002


dashDB は行指向/列指向型のテーブルをどちらも作成することができるリレーショナル・データベースのサービスですが、そのデータベースシステムとしての実体は IBM DB2 です。というわけで、このライブラリを使ってアクセスします:
https://www.npmjs.com/package/ibm_db

2017063001


まず以下のコマンドを実行して ibm_db をインストールします(このコマンドだけで DB2 ODBC Driver ごとインストールされます):
$ npm install ibm_db


そして以下のようなコードを用意して dashDB にアクセスします:

(settings.js)
exports.db_host = 'dashdb-entry-yp-XXXXXXXX.services.dal.bluemix.net';
exports.db_port = 50000;
exports.db_name = 'BLUDB';
exports.db_username = 'dashNNNN';
exports.db_password = 'PASSWORD';

(sample.js)
var ibm_db = require( 'ibm_db' );
var settings = require( './settings' );

var db_str = "DATABASE=" + settings.db_name
  + ";HOSTNAME=" + settings.db_host
  + ";UID=" + settings.db_username
  + ";PWD=" + settings.db_password
  + ";PORT=" + settings.db_port
  + ";PROTOCOL=TCPIP";
var sql = "select OBJECTID, NAME from SAMPLES.GEO_CUSTOMER limit 10";

ibm_db.open( db_str, function( err, conn ){
  if( err ) return console.log( err );

  conn.query( sql, function( err, data ){
    if( err ) console.log( err );
    else console.log( data );

    conn.close( function(){
      console.log( 'done.' );
    });
  });
});

settings.js の中身はユーザー名やパスワードといった dashDB に接続するためのサービス資格情報です。IBM Bluemix の画面から取得できる値を使って、実際の値で書き換えて使ってください:

2017063003


アプリケーションの実体は sample.js です。今回の例ではシンプルに接続して、サンプルデータとして GEO_CUSTOMER テーブルから OBJECTID と NAME の値を 10 件だけ取得する、という SQL (青字部分)を実行しました。また settings.js で定義した情報を取り出して接続文字列(赤字部分)を生成しています。

node コマンドで sample.js を実行して、以下のような結果が表示されれば成功です:
$ node sample.js
[ { OBJECTID: 1322, NAME: 'Kami Labarbera' },
  { OBJECTID: 1323, NAME: 'Johnathon Tunney' },
     :
  { OBJECTID: 1587, NAME: 'Althea Alcazar' } ]
done.







 

Node.js の処理内で unzip を実現する方法を紹介します。アップロードなどで zip ファイルを受取って、それをダイナミックに展開して特定のファイルを取り出す、といった仕組みを Node.js で実現する場合に必要な実装の例です。

この仕組みを実現するために、node-unzip という便利なライブラリがあるので、これを使うことにします:
https://www.npmjs.com/package/unzip

2017070601


fs ライブラリと併用して、こんな感じで使います(zip ファイル内の全ファイルを展開する例):
var fs = require( 'fs' );
var unzip = require( 'unzip' );

  :
  :

fs.createReadStream( './uploads/archive.zip' )
    .pipe( unzip.Extract( { path: './tmp/' } ) );

特定のファイルだけ(以下の例では拡張子が ".xml" のものだけ)を展開する場合は以下のようにします:
var fs = require( 'fs' );
var unzip = require( 'unzip' );

  :
  :

fs.createReadStream( './uploads/archive.zip' )
    .pipe( unzip.Parse() )
    .on( 'entry', function( entry ){
      var filename = entry.path;  //. ファイル名
      var type = entry.type;  //. 'Directory' または 'File'
      var size = entry.size;   //. ファイルサイズ

      if( filename.toLowerCase().endsWith( ".xml" ) ){
//. ".xml" で終わるファイル名だった場合のみ展開 entry.pipe( fs.createWriteStream( './tmp/' + filename ) ); }else{ entry.autodrain(); } });

そもそもの元ファイルが zip 圧縮されていたり、大量のファイルデータをアップロードして登録したい場合などは、目的のファイルを zip して、1回でまとめてアップロードできると便利なのですが、この方法であれば受け取った zip を展開して・・・という処理が実現できます。


ウェブブラウザである Chrome の機能を拡張する Chrome extension を作ってみます。今回は試しに
 Chrome でアマゾンの商品ページを見ている時に、そのページ内の ASIN コードを取り出して表示する
というシンプルな機能を実装してみます。

この Chrome 拡張機能の開発方法に関してはこちらに公式ドキュメントがあります。が、Chrome 自身のバージョンアップにともなって機能変更が加わり、それが混乱を招いてしまっている側面はあると思っています。今回紹介する内容はとりあえず 2016/Jun/23 時点では動くことを確認していますが、将来的な保証はないことを最初にお断りしておきます。

今回用意するファイルは次の3つです:
ファイル名用途・コメント
jquery-3.0.0.min.jsjQuery ライブラリ。ここから最新版をダウンロードし、manifest.json 内にファイル名を記述する
manifest.json拡張機能の定義ファイル
script.js拡張機能の実装ファイル


まず jQuery の最新版ライブラリを使います。jQuery のダウンロードページから最新バージョン(今回の例では 3.0.0)をダウンロードします。

次に定義ファイルである manifest.json です。Chrome 拡張機能を作る際に必須の JSON ファイルで、Chrome はこのファイルの中に記述されている通りに拡張されます。今回は以下の様な内容にします:
{
  "name": "AmazonASIN",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": "Amazon商品ページからASINコードを取り出す拡張",
  "content_scripts": [
    {
      "matches": ["http://www.amazon.co.jp/*","https://www.amazon.co.jp/*"],
      "js": ["jquery-3.0.0.min.js","script.js"]
    }
  ]
}

この中では以下の様な指定を行っています:
  • 拡張の名前(name)は "AmazonASIN"
  • 拡張のバージョン(versoin)は 0.0.1
  • マニフェストのバージョン(manifest_version)は2(固定)
  • description に拡張の説明を記述
  • content_script 内にスクリプトの条件を指定
  • このスクリプトは http(s)://www.amazon.co.jp/ 内のページを参照している時だけ動く
  • 動くファイルは前述の jquery-3.0.0.min.js と後述の script.js

簡単に言うと、アマゾン(www.amazon.co.jp)のページを参照している時に script.js が動く、という指定をしています(jquery-3.0.0.min.js は script.js 内で利用しているのでここに記述しています)。

そして今回紹介する拡張機能の本体となるのが script.js です。この内容は以下のとおりです:
$(function(){
 $(".col2 .pdTab tr:first").each(function(){
  var tr = $(this);
  var td1 = tr.children('td:first');
  if( td1.html() == "ASIN" ){
    var td2 = tr.children('td:nth-child(2)');
    var asin = td2.html();
    window.prompt( "ASIN", asin );
  }
 });
});

jQuery にある程度詳しい人であれば簡単に理解できそうなほどシンプルな内容ですが、中身を一応紹介します。

まず最初に、自動化したい作業はこんな感じです:
  1. アマゾンの商品ページを開いたら、
  2. 「登録情報」の「ASIN」と書かれた箇所(下図参照)を探して、
  3. その横の ASIN 番号を取り出して表示する

2016062301

↑この機能、アフィリエイターに需要ありますかね・・・


そしてこのアマゾン商品ページの HTML ソースを見ると、『「登録情報」の「ASIN」と書かれた箇所』というのが、『col2 クラスを持った div パート内の、pdTab クラスを持った div 内のテーブルの、最初の tr パート』であることがわかります:

2016062302


この tr の2番目の td に目的の ASIN 番号が書かれているのでこれを取り出すのが目的です。というわけで、上記の script.js 内では以下の様な処理を記述しています:
  • Amazon のページでのみ以下を実行する(manifest.json で指定済み)
  • ページが全て読み込まれた後に以下を実行
  • col2 クラス以下の pdTab クラスの更に下にあるテーブルの最初の1行(最初のtr)を取り出す
  • 取り出した tr 内の1つ目の td を取り出し、その中に "ASIN" と書かれていることを確認する
  • 確認できたら tr の2つ目の td (ここに ASIN 番号がある)を取り出して、その HTML 文字列を取り出す
  • 取り出した ASIN 番号を画面に表示する

これで処理の記述はできたので、上記3つのファイルを全て同じフォルダ(下図では c:\tmp\amazon_extension)内に保存します:
2016062303


作成した Chrome 拡張はデベロッパーモードの Chrome 内に読み込ませて利用することができます。Chrome を起動後、右上のメニューボタンから 「その他のツール」-「拡張機能」 を選択します:
2016062304


Chrome 拡張機能のメニューが表示されたら、右上の「デベロッパーモード」にチェックを入れます。これでデベロッパーモードになったので、更に「パッケージ化されていない拡張機能を読み込む」ボタンをクリックします:
2016062305


先程作成した3つのファイルを格納したフォルダ(上記の例では C:\tmp\amazon_extension\)を指定して拡張機能を読み込みます:
2016062306


拡張機能にエラーがなければ正しく読み込まれ、拡張機能一覧に表示されます。「有効」にチェックが入っていることを確認してください(チェックを外すと、この拡張機能は動きません。またその右のゴミ箱アイコンをクリックすることで拡張機能を Chrome から削除することもできます):
2016062307


では改めてアマゾンの商品ページを見てみましょう。例えばこのページを参照してみると、、
https://www.amazon.co.jp/gp/product/B014CGROJW/


商品ページがロードされた後に以下の様なダイアログが表示され、ASIN コードが取り出されたことが確認できるはずです(必要であれば、ここからコピーできます):
2016062308


Chrome 拡張機能のごくシンプルな例ですが、こんな感じで作れます。実体はクライアントサイドの JavaScript ですが、クロスサイトスクリプティングの制約を受けることなく AJAX が実行できたりするので、この中で外部連携も含めた様々な処理を記述できます。例えばここで紹介されているサンプルでは Chrome 拡張から Watson API を実行していたりします:
女優・ディベロッパー池澤あやかさんが作成したアプリとは? |Bluemix Developers Lounge


JavaScript で Cloudant や CouchDB を操作できるようになるライブラリの1つに PouchDB があります:
https://pouchdb.com/

2016031701


PouchDB は CouchDB および CouchDB と互換性のあるローカル NoSQL データベースです。JavaScript を使ってデータを読み書きし、またリモートの CouchDB サーバーにアクセスしたり同期したりすることもできるものです。広い意味ではデータベース本体および JavaScript ライブラリを合わせて PouchDB と呼ぶこともあります。

という特徴をもった、この PouchDB の JavaScript ライブラリを使うと、リモートの CouchDB データベースにアクセスすることができるようになります。JavaScript ライブラリなのでクロスサイトスクリプティング等のセキュリティ制約を意識しながら使う必要があります。

今回、紹介するのはこんな環境で JavaScript を使ってデータベースにアクセスする、というものです。HTML ファイルがローカルディスク内にあるので、普通にアクセスするとクロスサイトスクリプティング制約にかかって使えませんが、その制約を回避する方法と合わせて紹介します:
2016031705


まずは操作先の CouchDB サーバー環境を用意します。外部からアクセスするので Access-Control-Allow-Origin ヘッダのカスタマイズも必要になります。この辺りの手順は以下2つのブログエントリを参照ください:


この CouchDB サーバーの IP アドレスは XX.XX.XX.XX、ポート番号はデフォルトの 5984 であるとして以下を記述します。まずは CouchDB サーバーの管理コンソールにアクセスして、この後リモートから操作するためのデータベースを1つ作成しておきましょう。ウェブブラウザで http://XX.XX.XX.XX:5984/_utils/ にアクセスして管理コンソール画面にアクセスし、"Create Database" と書かれた箇所をクリックします:
2016031701


作成するデータベースの名前を問われるので適当な名称(この例では mydb)を入力して "Create" ボタンをクリックします:
2016031702


指定した名称のデータベースが作成され、その内容が表示されます。この時点では作成直後なのでドキュメントは存在していないはずです。とりあえず元の画面に戻るために "Overview" と書かれた箇所をクリックします:
2016031703


もとの管理コンソールのデフォルト画面に戻ります。先程まではなかった mydb というデータベースが追加されていることがわかります。このデータベースにリモートからアクセスすることが今回の目的です:
2016031704


次にローカル側の準備です。まずなにはともあれ PouchDB ライブラリが必要なので、PouchDB のトップページから最新版の PouchDB をダウンロードします。この記事を書いている時点での最新バージョンは 5.3.0 で、ダウンロードファイル名は pouchdb-5.3.0.min.js でした:
2016031701


次にダウンロードした PouchDB ライブラリを使って、リモートの CouchDB サーバーのデータベースにアクセスするプログラムをローカルに作成します。具体的には以下の様な HTML ファイル(index.html)をダウンロードした pouchdb-5.3.0.min.js と同じディレクトリに作成します:
<html>
<head>
<script src="./pouchdb-5.3.0.min.js"></script>
<script>
var db = new PouchDB('http://XX.XX.XX.XX:5984/mydb');
db.info().then( function( info ){
  console.log( info );
});
</script>
</head>
<body>
</body>
</html>

ソース内の XX.XX.XX.XX は CouchDB サーバーのホスト名または IP アドレスです。また mydb はアクセス先のデータベース名称です。この HTML では同じディレクトリにある pouchdb-5.3.0.min.js を読み込み、http://XX.XX.XX.XX:5984/mydb にアクセスして、成功したらその情報をウェブコンソールに表示する、というものです。繰り返しになりますが、このソースファイルはローカル環境にあり、このようなファイル構成になっています。そして HTML ファイルの読み込みと同時にリモートの CouchDB サーバーにアクセスして mydb データベースの情報を表示する、という内容の処理が記述されています:
2016031706


ではこの index.html をダブルクリックしてデフォルトブラウザで開きます。注意点としてウェブサーバー上の HTML ファイルを開いているのではなく、ローカルディスク内の HTML ファイルを開いている(アドレスのプロトコル部分が file:/// で始まっていることに注目)ことを確認してください。そして F12 を押してウェブコンソールを表示すると、上記 HTML 内の JavaScript コードが実行されて XX.XX.XX.XX:5984 の mydb データベースに関する情報が出力されていることを確認してください。ローカル環境から JavaScript でリモート環境の CouchDB サーバーのデータベースの情報を取得することができました!
2016031707


もしもここで以下のようなメッセージが表示されてしまう場合は、クロスサイトスクリプティングの制約にかかって、JavaScript が実行できなくなっている可能性が高いです。その場合はこのページの上部にある「CouchDB の Access-Control-Allow-Origin ヘッダを設定する」のリンク先に書かれている設定が足りない(または間違っている)ので、この内容を確認した上で再度試してみてください:
2016031708


以前のブログエントリで CouchDB サーバーの導入手順(というかビルド手順)を紹介しました:
CentOS に CouchDB をインストールする


上記の方法で自由に使うことのできる CouchDB サーバー1ノードが手元の環境に出来上がります。が、PouchDB などの JavaScript ライブラリを使ってこのサーバー上の DB を操作しようとすると、まだ手順が足りないこともあります。

具体的には、素のままの設定状態で JavaScript を使って CouchDB の REST API を実行しようとすると、クロスサイトスクリプティングのセキュリティ制約にかかってしまい、実行時にエラーを起こしてしまうのです。

クロスサイトスクリプティング制約を回避するには、(CouchDB の)HTTP サーバーに Access-Control-Allow-Origin ヘッダを付与して、明示的に制約を回避できるアクセス元を指定して回避する必要があります。その方法を紹介します。


まずは上記サイトを参照して CouchDB サーバー環境を用意します。以下の手順では上記方法で導入した CouchDB サーバー環境を利用するものとします。

次に /usr/local/etc/couchdb/local.ini をテキストエディタで開き、以下の内容を加えます(この例では Access-Control-Allow-Origin に * を設定して、事実上アクセス元の制約なしにアクセスできるような設定にしています):
  :
  :
[httpd]
port = 5984
bind_address = 0.0.0.0
enable_cors = true

[cors]
credentials = true
origins = *

  :
  :

この状態で CouchDB を再起動すると、CouchDB の REST API はどのサーバーからでもアクセスできるようになります。
# /etc/init.d/couchdb restart


実際に PouchDB を使って外部からのアクセスが可能であることを示すコードは別のエントリで用意する予定です。

このページのトップヘ