Node-REDでCloudantのWarehouse連携機能を使わず直接Db2 Warehouse on Cloudに書き込みする方法

Bluemix、改め IBM cloudでは、Cloudantから Db2 Warehouse on Cloud(旧dashDB)にデータを連携する機能がありました。

> - Qiitaでの関連記事
> 	- [Node-REDを使ってセンサーデータをCloudantに格納する](https://qiita.com/asmanabu/items/809e1276f95e76b989a3)
> 	- [CloudantからdashDBへのレプリケーション](https://qiita.com/Komachi77/items/3e35ec852740ee1de004)
> - 機能について → [データウェアハウジング](https://console.bluemix.net/docs/services/Cloudant/guides/warehousing.html#deprecating-cloudant-nosql-db-s-db2-warehouse-on-cloud-feature-february-7-2018-) (IBMcloud内)


IoTデータのJSONデータを自動的に表形式のRDBに格納してくれるので、便利だったのですが、この機能が2018/4/30にSunsetしてしまうようです。



> 案内(2018/2/13) → [Feature Retirement – IBM Cloudant’s Warehousing feature](https://www.ibm.com/blogs/bluemix/?p=123675?cm_mmc=Email_Nurture-_-Cloud_Platform-_-WW_WW-_-LC66-B-CDb2&cm_mmca1=000017OB&cm_mmca2=10001675&cm_mmca3=M00006664&cvosrc=email.Nurture.M00006664&cvo_campaign=000017OB)




「案内」には、代替案リンクがついているのですが、そこに飛ぶと現在の方法しか紹介されていないですね、、

> 「案内」(Feature Retirement」
>
> - IBM Cloud has several alternatives to the Warehouse feature. We have documented alternative approaches using Spark SQL, which you can read  [here](https://console.bluemix.net/docs/services/Cloudant/release_info/deprecations.html?cm_mc_uid=61406104120615202371420&cm_mc_sid_50200000=96051181520732472594&cm_mc_sid_52640000=25328531520732472597#deprecations) .
>
> 上記の「here.」から行ける「[deprecations](
https://console.bluemix.net/docs/services/Cloudant/release_info/deprecations.html#deprecations)」のリストの記載
>
> - You can find a schedule and alternatives to IBM® Db2® Warehouse on Cloud [here](https://console.bluemix.net/docs/services/Cloudant/guides/warehousing.html#deprecating-cloudant-nosql-db-s-db2-warehouse-on-cloud-feature-february-7-2018-).
>
> 上記の「here.」から行けるサイトは[こちら](https://console.bluemix.net/docs/services/Cloudant/guides/warehousing.html#deprecating-cloudant-nosql-db-s-db2-warehouse-on-cloud-feature-february-7-2018-)。
Spark SQLの説明はなく、現行機能の説明があるのみです。(2018/3/11時点)


ちなみに IBM cloudで使えるSparkは[Apache Spark](https://console.bluemix.net/catalog/services/apache-spark)で、こちらは今のとこ Liteプランで2個無料で使えるようです。
ただ、コーディングが必要で、ちょっと敷居が高いですね。2018/4/30までにできるのかな。

やりたいことは、**JSONデータをRDBに保存するだけ** なのに、、

というわけで、今現在、上記の機能を使っている人向けに、もっと簡単にデータ書き込みできる方法を紹介します。



# 前提
- IBM CloudのNode-RED上で、Cloudantを使っている
- Cloudantから、Db2 Warehouse on Cloudへの連携機能を使っている
    - なお、この連携機能の新規作成機能は、2018/1/16で止まっています。新しく作ることはできません。
- Node-REDで、dashDBのノードを使える状態である
    - 最新のNode-RED 0.18.4だと、dashDBのノードがなく、~~パレットから追加しようとすると失敗します。~~ (2018/3/11現在) ← 本日実施したら 0.18.4でも追加できました 。(2018/3/23)
    - Node-RED 0.17.xだと、dashDBノードがありませんが、パレットから追加できます。
    - Node-REDのバージョン0.16.xだと、dashDBノードがはじめからついてます。


# 手順概要

Node-REDとDb2 Warehouse on Cloudを接続し、ノードを2個追加するだけです。
なお、手順の過程で Node-REDが再ステージ(再起動)しますので、フローのバックアップを取得することをおすすめします。

# 手順詳細

- 1. IBM Cloudのコンソールで、Node-REDと、Cloudantから自動作成されたDb2 Warehouse on Cloudを接続します。

この手順を行うと、Node-REDを再ステージするよう促されますので、OKします。

<HR>

- 2. Node-REDで、下記のfunctionノードを作ります。

<img width="247" alt="スクリーンショット 2018-03-11 11.53.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/190535/83ef8f1d-d5f7-cb50-5e14-d03624958c25.png">

ノードの内容

```js

// ノードIDを設定 (複数のノードがある場合ここを固有のIDにする)
var node_id = "abc";


// キー項目にするために現在時刻を取得
var getCurrentTime = function () {
    var date = new Date();
    date.setHours(date.getHours() + 9);
    var d = date.getFullYear() + '-';
    d += ('0' + (date.getMonth() + 1)).slice(-2) + '-';
    d += ('0' + date.getDate()).slice(-2) + 'T';
    d += ('0' + date.getHours()).slice(-2) + ':';
    d += ('0' + date.getMinutes()).slice(-2) + ':';
    d += ('0' + date.getSeconds()).slice(-2) + '.';
    d += ('0' + date.getMilliseconds()).slice(-3) + 'Z';
    return d;
};

//さらにランダムなシードをmax〜minの範囲で取得
var max = 10000;
var min = max/10;
var random_val = Math.floor( Math.random() * (max + 1 - min) ) + min ;

var keyNames = [];
var varNames = [];
for( var a in msg) {
    if (a == "_msgid") continue;    // _msgidは加工しない
    keyNames = keyNames.concat(a.toUpperCase());
    varNames = varNames.concat(msg[a]);
    delete msg[a];  // 既存のenumerableプロパティは削除
}

// キー項目として先程の時刻情報+ランダムシードを入れる
msg.payload =  {
"_ID": getCurrentTime()+"+"+node_id+":"+random_val,
"_REV": 0,
} 

for(var b in keyNames){
  msg.payload[keyNames[b]] = varNames[b];
}


return msg;
```
<hr>
- 3. dashDBノードを作成します。

<img width="123" alt="スクリーンショット 2018-03-11 12.02.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/190535/cd321e42-cca1-457d-8102-6ffb29f93acc.png">
プロパティで、Cloudantから書き出しを行っているDb2 Warehouse on Cloudを選択します。

- 4. Cloudantに書き込みを行っているノードから、上記のノードの左にリンクをつなぎます。
- 5. 上記のノードの右から、dashDBのノードにリンクをつなぎます。
- 6. フローを「デプロイ」します。ここで稼働確認して下さい。
- 7. 問題なければ、Cloudantのコンソールで、「Analytics」のdashDB連携機能を停止して下さい。

下記の「Action」で「■」を押すと止まります。
![スクリーンショット 2018-03-11 12.17.42.png](https://qiita-image-store.s3.amazonaws.com/0/190535/e4c19887-ae2a-1368-2aea-24afb2e5b2ed.png)



以上で完了です。

# 注意点
- 上記のコードだと、既存のmsgに格納しているプロパティの内容が「 _msgid」以外すべてmsg.payloadに変換されます。
もし、そのままの形で保存したいプロパティがあれば、「 _msgid」同様に、スキップ(continue)するようにして下さい。
- Db2 Warehouse on Cloudに存在しない列項目があったり、欠けている列項目があると、データベースInsertが失敗します。
    - 存在しない列項目については、いままではCloudantでRescanすれば復旧できましたが、今後はできなくなります。必要な列項目についてはあらかじめ追加しておいて下さい。
    - 欠けている列項目については、**項目名: ""** 等と記載して補ってやる必要があります。