Node-REDでもデータベース(以下DB)を使ってみたいなと思いました。実はNode-REDでは使えるDBが豊富にあります。 結構時期的には昔のエントリーですが、この時点でもかなり豊富な選択肢を選べます。
【参考】 qiita.com
この中で、RaspberryPiでローカルに使用できることを考えるとSQliteあたりがいいのかなと思いました。(難点としてはSDカードへのアクセスは遅い点ですが…)
拡張ノードとしては以下のnode-red-node-sqlite
を使用していきます。SQLiteで拡張ノードを検索するともう一つnode-red-contrib-sqlitedb
がでてきますが、こちらはクラウド上のDBへのアクセスを行うもののようなので、今回はnode-red-node-sqlite
を扱うことにします。
インストール時に少しだけ引っかかる点があるのでご注意ください。(ただし、RaspberryPi固有の可能性もあります)
今後は以下の書籍も参考しています。購入してみましたが非常に内容が良くて理解の助けになります!多分これが大概のことが達成できる人も多いかなと覆いますよ。
拡張ノードnode-red-node-sqlite
をインストールする
基本はその他の拡張ノード同様です。 画面の右上にある【三】ボタンをクリックして、プルダウンメニューから【パレットの管理】を選択してクリックします、。
ユーザー設定の表示が行われたら、【ノードの追加】タブをクリックし、検索のテキストボックスにnode-red-node-sqlite
を入力して検索を行います。
1件に候補が絞られますので、【ノードの追加】ボタンをクリックします。
インストール確認のダイアログが表示されるので、【追加】ボタンをクリックして処理を進めます。
これでインストール作業が開始されるのですが、数分ほどすると処理進行の処理が止まり、もとの画面に戻ってしまいます。拡張ノードにも追加されている気配はありません。実はバックグラウンドでインストール作業は行われています。
一応、ドキュメントにも
Note: the install process requires a compile of native code. This can take 15-20 minutes on devices like a Raspberry Pi - please be prepared to wait a long time.
と注記がされていますが、UIの表示でこういうことが起こるとは書いていないので注意かもしれません。
実際にはどうなっているかというと、ログ上はインストール開始されていること表示されています。
プロセスを眺めてみると(topコマンド
で確認)、ネイティブコードのビルド処理がされていることがわかります。ここでいうと`CPU使用率99.7%となっている``cc1```のプロセスが該当します。インストール時はRaspberryPiの処理はものすごく遅くなっています。
15分ほど待つとインストール完了のダイアログがWeb画面上にも表示されます。これでインストールは完了となります。
インストール前には【ノードの追加】となっていたボタンが【追加しました】の表示に変わっていると思います。
パレット(画面左側のノードがある部分はこのように呼ぶようです。最近知ったw)一覧のストレージカテゴリーにもsqlite
ノードが追加されています。
ログも念の為確認するとノードが追加されたことがわかります。
これでインストールは完了です。 注意点としてはこのインストールではSQLiteのCLIは導入されないので注意が必要かもしれません。
実際にSQLiteのローカルデータベースを使用してみる
あとは基本的にはSQLiteの使い方に近いのですが、一応やってみます。 これまでのノードとは異なり、入力時には送信されるJSONのtopicがクエリとして使用されます。 injectノードではトピックもUIとして表示されているのでこちらに入力していくことになります。
テーブルの作成
では、動作テストを行っていきます。まずはテーブルを作成していこうと思います。 以下3つのノードをワークフローに配置していきます。
inject
ノードsqlite
ノードdebug
ノード
こんな感じになると思います。
今回扱う処理の基本の処理はinject
ノードでクエリやコマンドを生成、sqlite
ノードで処理を行い、結果をdebug
ノードで表示となります。
まずは、injectノードのクリックしてプロパティを編集していきます。これまでは【ペイロード】を編集していましたが、sqliteノードでは【トピック】を使用します。【トピック】のテキストボックスに以下のクエリを入力します。
create table tbl1(one varchar(10), two smallint);
今回の例では2つのカラムから構成されたデータとしています。1つ目は10文字の文字列、2つ目は2バイトの整数で構成しています。
【名前】の値にもテーブルの作成
と入力しましょう。今回はinjectノードが多いので…多くなくても名前は入れたほうがいいですけど。
入力が終わったら【完了】ボタンをクリックします。
続いて、sqlite
ノードをクリックして、プロパティを編集していきます。初回はデータベースのファイル名の設定が必要になります。これが終わるとsqlite
ノード側がクエリを受信すると自動的に処理を行ってくれるので、編集の必要はなくなります。
プロパティ画面の【Database】のプルダウンが、ありますが初期段階では「新規にsqlitedbを追加…」となっていますが、ここにデータベースファイルを入れていきます。プルダウンの隣りにある【編集】ボタンをクリックすると【sqlitedbノードを編集】画面が表示されます。
【sqlitedbノードを編集】画面の【Database】のテキストボックスにデータベースファイルのパスを入力し、【Mode】の項目もプルダウンから適切なものを選ぶことになりますが、今回はRead-Write-Create
のモードを設定しています。設定が終わったら右上の【更新】ボタンをクリックして(sqliteノードの編集の)プロパティ画面に戻ります。
【Database】のフィールドに指定したパスが格納されていればOKです。あとは名前のフィールドにコマンドの実行
と入力しておきます。
ここまでの編集でフローが以下のようになっていると思います。
あとは各ノードの端子を結んでいきます。
ここまできたら【デプロイ】ボタンをクリックします。これで準備は完了です。
では実行してみましょう。テーブル作成のinjectノードの左側のボタンをクリックしてみると…
デバックタブにはempty
という風には表示されていますが、ちゃんとテーブルは作成されています。データベースのファイルも指定したパスに生成されています。
これでデータを格納するテーブルが生成されました。
生成したテーブルにデータを格納する
続いてはデータの格納を行います。ワークフローに2つのinject
ノードを配置し、
それぞれにデータを格納するクエリを書いていきます。基本的にはテーブルの生成と同じように設定していくことになりますので少し説明は省きます。プロパティの【名前】と【トピック】を編集していきます。
【1つ目の要素の登録
のノード】
【2つ目の要素の登録
のノード】
編集が終わったら、以下の画面のようにノードを接続していきます。割と接続の様子がこんな感じのフローになるのが多いのがNode-REDっぽい感じでもあります。
ここまでできたら【デプロイ】ボタンをクリックします。エラーがなければそれぞれ実行されていきます。
とくに問題なく実行できました。エラーが起きてないだけでデータの格納が行われたことは確認できません。今度はデータの取り出しを行っていきます。
テーブルからデータを取り出す
これまでと同様にinjectノードをワークフローに配置し、
プロパティの編集を以下のように行います。(名前
とトピック
のフィールドを編集)終わったら【完了】ボタンをクリックします。
フロー画面に戻ったら
ノード間を結び、【デプロイ】ボタンをクリックします。
実行すると値は各エントリーごとにオブジェクトの配列として返されるので、デバッグ画面を操作して取り出したデータの確認を行います。
問題なくデータを取り出すことができました。
テーブルの削除
ここまでて一通りのことはやったので終わってもいいのですが、テーブルの削除も念の為行ってみます。
injectノードを配置して
プロパティを編集し、
ノードを接続して、【デプロイ】ボタンをクリックします。
後は実行でエラーが出なければ完了となります。
テーブルの削除処理は基本的には必要はありません。実験のレベルとしてはテーブルの作成後に、更に同名のテーブルを作成しようとするとエラーが発生するので必要なこともあるでしょう。(本当か?)
おわりに
RaspberryPiでローカルにデータベースを保つ場合には比較的SQLiteを使用するのは有り得る話かなと思います。機能としてもCLIを叩くような感じで実行できるので、比較的理解しやすかったのではないかなと思います。
ただ、リモートDBに接続するとなると、選択肢は増えますがお手軽さからするとkintone
かなと思います。WebのUI関連をkintone
を任せることもできますし。Node-REDにはkintone
の拡張ノードもあります。データの格納まではRaspberryPi、データの可視化などはkintone
という風に分業もできると思います。
ここまでで、データベースまで格納できるようになったので、次はTwitterあたりのノードを触ってみたいと思います。
【今回作成したフロー】
[ { "id": "2f3780d4.eadb4", "type": "tab", "label": "フロー 1", "disabled": false, "info": "" }, { "id": "f591ad2a.5511", "type": "debug", "z": "2f3780d4.eadb4", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 550, "y": 80, "wires": [] }, { "id": "38caa3c0.1e6eec", "type": "inject", "z": "2f3780d4.eadb4", "name": "テーブルの作成", "topic": "create table tbl1(one varchar(10), two smallint);", "payload": "", "payloadType": "date", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 120, "y": 80, "wires": [ [ "ef7b3bc4.8a1f68" ] ] }, { "id": "ef7b3bc4.8a1f68", "type": "sqlite", "z": "2f3780d4.eadb4", "mydb": "56879991.d56508", "sqlquery": "msg.topic", "sql": "", "name": "コマンドの実行", "x": 340, "y": 80, "wires": [ [ "f591ad2a.5511" ] ] }, { "id": "3360e990.2bf7e6", "type": "inject", "z": "2f3780d4.eadb4", "name": "1つ目の要素の登録", "topic": "insert into tbl1 values('hello!',10);", "payload": "", "payloadType": "date", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 130, "y": 140, "wires": [ [ "ef7b3bc4.8a1f68" ] ] }, { "id": "37f6b040.824ef", "type": "inject", "z": "2f3780d4.eadb4", "name": "2つ目の要素の登録", "topic": "insert into tbl1 values('goodbye', 20);", "payload": "", "payloadType": "date", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 130, "y": 200, "wires": [ [ "ef7b3bc4.8a1f68" ] ] }, { "id": "6f32dcb6.0e9d14", "type": "inject", "z": "2f3780d4.eadb4", "name": "テーブル内のデータの表示", "topic": "select * from tbl1;", "payload": "", "payloadType": "date", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 150, "y": 260, "wires": [ [ "ef7b3bc4.8a1f68" ] ] }, { "id": "3593304c.3c048", "type": "inject", "z": "2f3780d4.eadb4", "name": "テーブルの削除", "topic": "drop table tbl1;", "payload": "", "payloadType": "date", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "x": 120, "y": 320, "wires": [ [ "ef7b3bc4.8a1f68" ] ] }, { "id": "56879991.d56508", "type": "sqlitedb", "z": "", "db": "/home/pi/example.db", "mode": "RWC" } ]