【MarkLogic Server】Node-REDと連携してみよう

はじめに
-----
[Node-RED](https://nodered.org/)をご存じでしょうか。
Node-REDはブラウザベースのGUI操作でアプリケーションを構築するためのオープンソースツールで、IBM社が開発しました。

ノードと呼ばれる、機能をカプセル化したモジュールをマウスでドラッグして結合してアプリケーションを開発します。Web-APIや各種オンラインサービス、ハードウェアデバイスなどに対応したノードが用意されており、これらを連携してロジックを作成します。ほぼプログラミング不要で開発できるのが特徴的です。

![1_NodeRED_実行例.png](https://qiita-image-store.s3.amazonaws.com/0/188643/ea45f589-4723-5853-4acb-ebecbcb906b0.png)

本家IBM社のクラウドサービス[Bluemix](https://www.ibm.com/cloud-computing/jp/ja/bluemix/)にはNode-REDが搭載されています。また、オープンソースとして公開されているため、AWS等のクラウドや自分のローカルサーバでもNode-RED環境を構築できます。

Node-REDのノードは日々新しいものが開発されており、[Node-REDライブラリ](https://flows.nodered.org/)で検索できます。

Node-REDの活用方法で最も注目されているものの1つにIoT(Internet of Things)との連携が挙げられます。
IBM社はWatson IoT PlatformというIoTデバイスに関するソリューションを展開しています。Bluemixにも対応しており、BluemixのNode-REDを使用することで、IoTデバイスとクラウドの各種サービスを連携するアプリケーションを簡単に開発できます。

また、Node-REDはMQTTにも対応しており、Node-RED上でMQTT Brokerを動作させることが可能です。Node-REDのBrokerに対してIoTデバイスからセンサー情報をPublishし、それをさらに他のサービスに連携するようなアプリケーションを簡単に構築できます。
IoTでトイレの個室の空き状況を監視して通知する、というアプリが話題になりましたが、トイレに設置したセンサーからNode-REDに状況を送信し、Node-REDからTwitterに連携して、個室の状況をつぶやくようなBotアプリも作れます。

そんな便利なNode-REDですが、残念なことにMarkLogicへの接続には対応していません。BluemixのNode-REDならば、IBM社のCloudantというNoSQL JSONデータベースに接続するAPIが提供されていますし、オープンソースのNode-REDでもMySQLやmongodbなど各種データベース用のノードが存在するようです。
しかしMarkLogicのノードは存在しないようなので、作ってしまいましょう。

前置きが長くなりましたが、本記事では主に以下の内容を記します。

* Node-RED環境を構築する
* MarkLogicのNode.jsクライアントAPIをインストールする
* JSONデータをMarkLogicに登録するためのノードを開発する
* Node-REDでMarkLogicにJSONデータを登録する簡単なフローを作る

なお、本編で使用する環境は以下の通りです。

| ソフトウェア | バージョン |
|---|---|
| CentOS 7 | 7.3.1611 |
| MarkLogic | 8.0-7 |
| Node.js | 6.11.1 |
| Node-RED | 0.17.5 |

Node-RED環境を構築する
-----
Node-REDはNode.jsで動作するため、まずNode.jsとパッケージマネージャのnpmをインストールします。

**Node.jsのインストール**
今回はyumを使ってインストールします。EPELリポジトリのNode.jsパッケージを利用します。

```
# yum install epel-release
# yum install nodejs npm
```

通常は上記の手順で大丈夫ですが、2017年9月3日現在、CentOS 7.3ではhttp-parserの依存関係によってインストール出来ない場合があるようです。EPELからhttp-parserが削除されてしまったことが原因のようです。もしhttp-parserの依存関係問題が発生したら、以下のようにパッケージをインストールした後、上記手順を実行して下さい。

```
# rpm -ivh https://kojipkgs.fedoraproject.org//packages/http-parser/2.7.1/3.el7/x86_64/http-parser-2.7.1-3.el7.x86_64.rpm
```

**Node-REDのインストールと起動**
いよいよNode-REDをインストールします。Node-REDはnpmでインストールします。

```
# npm install -g node-red
```
インストールが完了したら実行してみます。

```
# node-red
```

Node-REDはデフォルトでは1880ポートで動作します。http://(Node-REDを実行したサーバのIPアドレス):1880/にアクセスしてみて下さい。

![2_NodeRED起動直後.png](https://qiita-image-store.s3.amazonaws.com/0/188643/e8c5e397-be4a-fe87-b778-c17fbef4a29e.png)

左側に表示されているノードから必要な機能を選択し、中央のフロー作成のタブにドラッグします。ノードをつなぎ合わせてフローを作成し、画面右上の「デプロイ」を実行すると完成です。

例として固定の文字列(Hello, World)を受け取り、大文字、または小文字に変換するノードを用意し、最後に結果を出力する簡単なフローを作成してみます。

![3_NodeRED_サンプルフロー.png](https://qiita-image-store.s3.amazonaws.com/0/188643/4b232b95-6ea9-b8a4-a91d-1a7177f94264.png)

* ①は固定の文字列"Hello,World"を次のノードに渡すだけの入力ノードです。
* ②及び③は入力された文字列を全て大文字か小文字に変換して出力するfunctionノードです。functionノードはJavascriptで処理を定義することが出来ます。
ノードをダブルクリックすると、以下のような設定用のダイアログが表示されます。ここでは、文字列のtoUpperCase()で大文字に変換して、次のノードに連携します。

![4_NodeRED_サンプルノード.png](https://qiita-image-store.s3.amazonaws.com/0/188643/98ce02b5-a1cd-7fc1-292e-7f1b0da66921.png)

* ④はデバッグ出力用のノードです。受け取ったメッセージをNode-REDのデバッグタブに出力します。以下の例では、Hello,Worldを大文字と小文字に変換した結果が表示されています。
 
![4-1_NodeRED_サンプル結果.png](https://qiita-image-store.s3.amazonaws.com/0/188643/c756b712-6c0a-d42d-ff41-02067031887c.png)

以上でNode-REDのインストールから簡単なサンプルの実行までが完了しました。
Node-REDの使い方については良記事が多く存在するので調べてみて下さい。

MarkLogicのNode.jsクライアントAPIをインストールする
---
MarkLogicには[Node.js用のクライアントAPIライブラリ](https://developer.marklogic.com/products/node)が提供されています。これを利用することでMarkLogicに連携するNode.jsアプリケーションを開発できます。
Node-REDはNode.jsで動作するため、MarkLogicのNode.jsクライアントライブラリもインストールします。
なお、npmを初めて使用する際には、npm initで初期設定などが必要になります。

```
# npm install marklogic
```

このライブラリはREST-APIでMarkLogicに接続します。そのため、MarkLogicのHTTPサーバにREST-APIの設定が必要になります。MarkLogicの管理画面(8001番ポート)にアクセスし、HTTPサーバの設定画面を表示して下さい。以下の設定を行えばOKです。

* url rewriter : /MarkLogic/rest-api/rewriter.xml
* rewrite resolves globally ： true

JSONデータをMarkLogicに登録するためのノードを開発する
---
ここではNode-RED用のノードを作成する方法について簡単に記します。詳細な作り方は[公式サイト](http://nodered.jp/docs/creating-nodes/)をご参照下さい。

**ノードの構成とデプロイ**
ノードはHTMLとJavascriptから構成されています。ノードのUIをHTMLファイルに定義し、挙動をJavascriptファイルに定義します。

作成したHTMLファイルとJavascriptファイルは~/.node-red/nodesディレクトリ配下に置きます。新たにノードを配置したらNode-REDを再起動します。再起動後、作成したノードがNode-REDの左側のパネルに表示されます。

**MarkLogicノードの概要**
ここではJSONデータを受け取ってMarkLogicにインサートするノードを作成してみます。

MarkLogicに接続するには以下の情報が必要になります。これらの情報をノードのプロパティ画面で設定できるようにします。

* MarkLogicサーバのIPアドレス/ホスト名
* MarkLogicサーバのポート番号
* アクセスするユーザID
* パスワード

そして、実際にインサートするJSONデータやインサート先のURIは外部のノードから受け取るようにします。本ノードの入力値は以下のようなパラメータとします。

* msg.uri : インサートするJSONデータのURI
* msg.payload ： インサートするJSONデータそのもの


**ノードのJavascriptを作成する**
以下のJavascriptを~/.node-red/nodes/marklogic/marklogic-insert.jsとして保存して下さい。

```
module.exports = function(RED) {
  function MarkLogicInsertNode(config) {
    RED.nodes.createNode(this,config);
    var node = this;

    // 画面で入力されたMarkLogicの接続情報を元にMarkLogicに接続する。
    var marklogic = require('marklogic');
    var myCon = {
      host: config.host, 
      port: config.port,
      user: config.user,
      password: config.password 
    };
    var db = marklogic.createDatabaseClient(myCon);

    this.on('input', function(msg) {
      //本ノードの入力値であるURIとJSONデータから、インサート用のJSONデータを作成する。
      var docUri = config.documentDir + msg.uri;
      var documents = [{
        uri: docUri,
        content: {
          message: msg.payload
        }
      }];
      
      // MarkLogicにJSONデータをインサートする。
      db.documents.write(documents).result(
        function(response){
          node.log("Loaded the following document.");
          response.documents.forEach(function(document){
            node.log(" " + document.uri);
          });
        },
        function(error){
          node.error(error);
        }
      );
    });
  }
  // Node-REDに"marklogic-insert"ノードとして登録する。
  RED.nodes.registerType("marklogic-insert",MarkLogicInsertNode);
}
```

**ノードのHTMLを作成する**
以下のHTMLファイルを~/.node-red/nodes/marklogic/marklogic-insert.htmlとして保存して下さい。

```
// ノードのプロパティを定義する。
<script type="text/javascript">
  RED.nodes.registerType('marklogic-insert',{
    category: 'storage', //このノードのカテゴリ
    color: '#cc313b', //画面に表示するノードの色
    // 画面で入力するプロパティに関する定義
    defaults: {
      host: {value:"", required:true},
      port: {value:"", required:true},
      user: {value:"", required:true},
      password: {value:"", required:true},
      documentDir: {value:"/", required:true},
    },
    inputs:1, // このノードの入力ポートの数(0か1）
    outputs:0, // このノードからの出力の数(0以上)
    icon: "file.png", // ノードのアイコンファイル
    align: "right", // DBへの出力なのでフローの右側に配置することが多いため右寄せ
    label: function() {
      return this.name||"marklogic-insert";
    }
  });
</script>

// ノードのプロパティ画面。MarkLogicのホストやポート番号等を入力するフィールドを作成する。
<script type="text/x-red" data-template-name="marklogic-insert">
    <div class="form-row">
        <label for="node-input-host"><i class="icon-tag"></i> Host</label>
        <input type="text" id="node-input-host" placeholder="host">
    </div>
    <div class="form-row">
        <label for="node-input-port"><i class="icon-tag"></i> Port</label>
        <input type="text" id="node-input-port" placeholder="port">
    </div>
    <div class="form-row">
        <label for="node-input-user"><i class="icon-tag"></i> User</label>
        <input type="text" id="node-input-user" placeholder="user">
    </div>
    <div class="form-row">
        <label for="node-input-password"><i class="icon-tag"></i> Password</label>
        <input type="password" id="node-input-password" placeholder="password">
    </div>
    <div class="form-row">
        <label for="node-input-documentDir"><i class="icon-tag"></i> Document Directory</label>
        <input type="text" id="node-input-documentDir" placeholder="document directory">
    </div>
</script>

// ノードの説明書き
<script type="text/x-red" data-help-name="marklogic-insert">
  <p>Simple insert a json document to MarkLogic.</p>
</script>
```

JavascriptとHTMLをサーバ上に配置してNode-REDを再起動するとノードが追加されます。

![5_NodeRED_追加ノード.png](https://qiita-image-store.s3.amazonaws.com/0/188643/fd39bf3d-9134-b83e-53b5-c56d5eca6b17.png)

また、このノードをフローに配置してダブルクリックすると設定タブが表示されます。ここにMarkLogicへの接続情報を設定します。

![6_NodeRED_MLノード_設定.png](https://qiita-image-store.s3.amazonaws.com/0/188643/f539fb5b-0436-0c20-0301-8993f3e4439e.png)

以上でMarkLogicへのドキュメント登録ノードが使えるようになりました。

Node-REDでMarkLogicにJSONデータを登録する簡単なフローを作る
-----
**フローを作ってみる**
では、実際に追加したノードを使用してMarkLogicにJSONデータを登録するフローを作ってみましょう。
冒頭で作成したサンプルフローを元にしてみます。変換した大文字・小文字の文字列をJSONデータとしてMarkLogicに登録するフローに変えてみます。

以下に変更後のフローを記します。

![7_NodeRED_MLサンプルフロー改.png](https://qiita-image-store.s3.amazonaws.com/0/188643/66bef6ad-fe7d-df6a-df73-ba704d5399e2.png)

joinノードは、大文字変換と小文字変換の結果を受け取り、文字列の配列として次のノードに渡します。2つの変換処理の結果を待ち合わせて、次のノードに遷移するようにしています。

![8_NodeRED_サンプル_JOINノード_設定.png](https://qiita-image-store.s3.amazonaws.com/0/188643/d76a86e7-aae4-12da-5e70-5a0276850eae.png)

Create JSONノードはMarkLogicに登録するJSONデータと、そのURIを組み立てています。
URIは"node-red/test.json"を固定的に設定しています。
登録するJSONデータはmsg.payloadに定義しており、前のjoinノードの結果である文字列配列をそれぞれmessage1、message2としてJSONデータとしています。

![9_NodeRED_サンプル_CreateJSONノード設定.png](https://qiita-image-store.s3.amazonaws.com/0/188643/daf64f5b-26e9-80a0-6c6f-3a119177f4d2.png)

最後に作成したMarkLogicのノードです。対象のMarkLogicの接続情報を設定します。

![10_NodeRED_サンプル_MLノード設定.png](https://qiita-image-store.s3.amazonaws.com/0/188643/61dfddbd-e7a7-63bd-cdc0-9eaecf65dca5.png)

DocumentDirectoryは受け取ったJSONデータを登録するルートディレクトリです。アプリケーション毎にルートディレクトリを設定できるようにしてみました。Create JSONノードで指定したURIはこのルートディレクトリからの相対パスとなります。

**デプロイして実行してみよう**
フローの作成が完了したらデプロイします。デプロイは画面右上の「デプロイ」ボタンを押します。これでフローを実行できます。フローを実行するにはスタートとなるノードの左側のボタンをクリックすればOKです。

![11_NodeRED_サンプル_実行ボタン.png](https://qiita-image-store.s3.amazonaws.com/0/188643/24919979-9977-2bf2-a4e2-bff2db4d1e89.png)

実行したらMarkLogicにデータが登録されているか確認してみましょう。ブラウザでQueryConsole(8000番ポート)にアクセスしてドキュメントを確認してみます。

![12_NodeRED_サンプル_実行結果.png](https://qiita-image-store.s3.amazonaws.com/0/188643/09e7dce1-e07a-2fa2-bad6-9f47865b2ca4.png)

無事、登録されています！

おわりに
-----
今回は単純にJSONデータをMarkLogicに登録するノードを作成しましたが、もちろんMarkLogicからデータを取り出すノードを作成して、他のAPIと連携するアプリケーションも簡単に作れます。
Node-REDはIoTデバイスとの親和性も良く、様々なセンサー情報をMarkLogicに取り込んで活用するようなアプリケーションも簡単に作れるようになります。
いずれ、IoTデバイスとMarkLogicを連携するような記事も投稿したいと思っています！

```math
\def\textsmall#1{%
  {\rm\scriptsize #1}
}
```

------------------------------------------------------------------------

**免責事項**

​​​​​$\textsmall{当ユーザ会は本文書及びその内容に関して、いかなる保証もするものではありません。}$
​​​​​$\textsmall{万一、本文書の内容に誤りがあった場合でも当ユーザ会は一切責任を負いかねます。}$
​​​​​$\textsmall{また、本文書に記載されている事項は予告なしに変更または削除されることがありますので、予めご了承ください。}$