Watson Retrieve and Rankの公式ツールを利用して質問応答システムを作る (3)

# 2017年11月1日追記

[[Watson] Retrieve and Rankのサービス終了について](https://qiita.com/y-some/items/fc0937e031a3ab52d459)

# 概要

本稿では、Retrieve and Rank Web interfaceという公式ツールを利用してRetrieve and Rank（以下R&R）のトレーニングを行い、簡単な質問応答システムを作るまでの手順をシリーズ化して解説します。

(1) R&Rおよび公式ツール利用開始〜回答データ投入
(2) 質問データ投入〜Rankerトレーニング
**(3) Node-REDアプリケーションとR&Rの連携　※この記事で説明する部分**

まだ[第1回の記事](http://qiita.com/y-some/items/3dc83f9d8c3c3513ecc7)、[第2回の記事](http://qiita.com/y-some/items/976185718c7ded7179ee)をお読みでない方は、そちらを先にお読みください。

前回までの作業でR&Rのトレーニングが完了し、利用可能になりました。
今回はいよいよアプリケーションからR&Rを呼び出してみます。

# 免責事項

本稿におけるWatsonサービスに関する解説は執筆者の個人的な見解であり、公式資料に基づいていない内容を含みます。
また本稿の情報は2017年4月現在のものです。
必ず[Watson Developer Cloud](https://www.ibm.com/watson/developercloud/doc/retrieve-rank/index.html)などの公式資料も併せて確認してください。

# 完成イメージ

以下のようなシンプルなWebアプリケーションを、Node-REDを利用して作成します。
<img width="1226" alt="スクリーンショット 2017-05-07 7.31.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/6d1a5c57-026e-6b2b-ec7b-7bf1e545d8bf.png">

# Node-REDとは

あらかじめ用意された「ノード」と呼ばれる部品をつなげて「フロー」を作成することにより、最小限のコードでIBM Bluemix上にWebアプリを構築できます。

## Node-REDの利用開始

Bluemixのカテゴリーにて「ボイラープレート」－「Node-RED Starter」をクリックします。
<img width="1208" alt="スクリーンショット 2017-05-02 20.04.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/0b9005e4-4d15-aead-fad4-c8db4446af3d.png">

アプリ名を指定します。アプリ名＋ドメインがルートURLになります。
<img width="1204" alt="スクリーンショット 2017-05-02 20.04.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/fe2ee61a-742c-2dfe-68a9-e42f4832465f.png">
「作成」をクリックすると、環境作成が開始されます。

5-10分待ち、以下のように「実行中」の表示になったらURLをクリックします。
<img width="1198" alt="7a4d7780-3ea9-d60c-b2f7-cc2497a482b2.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/24ac528b-2826-5214-a958-5cc1282672d8.png">

以下の画面はNextで進めます。
![FireShot Capture 9 - Node-RED in IBM Bluemix - https___y-someya.au-syd.mybluemix.net_.png](https://qiita-image-store.s3.amazonaws.com/0/122917/33b40049-f48b-1303-dd54-5ae7b0f42462.png)
![FireShot Capture 11 - Node-RED in IBM Bluemix - https___y-someya.au-syd.mybluemix.net_.png](https://qiita-image-store.s3.amazonaws.com/0/122917/43139992-8c22-5e4b-1a85-1c5030d7254a.png)
![FireShot Capture 12 - Node-RED in IBM Bluemix - https___y-someya.au-syd.mybluemix.net_.png](https://qiita-image-store.s3.amazonaws.com/0/122917/f5b15787-6b71-6150-8798-4d9737f6323e.png)

「Go to your Node-RED flow editor」をクリックします。
<img width="1204" alt="スクリーンショット 2017-05-02 20.16.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/b2d2298f-0d11-c9ae-5efb-27baf6784376.png">

フローエディターにアクセスするためのユーザーIDとパスワードを指定します。<img width="663" alt="スクリーンショット 2017-05-02 20.17.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/2a9b9b82-ffd9-31d3-19bf-a199e71f566e.png">

フローエディターが起動します。
<img width="1225" alt="スクリーンショット 2017-05-02 20.17.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/d21959b6-79a0-b6e2-9361-148a3e4c0b5c.png">

左側に並んでいるのがノードです。
Node-REDでは、これらのノードをドラッグ＆ドロップし、ノード同士をつなげてフローを作成することでアプリケーションを構築します。

Watson用のノードはあらかじめ準備されており、R&Rのノードもあります。
<img width="178" alt="スクリーンショット 2017-05-07 8.27.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/38b5f551-78cc-eafb-ee17-44be14f8ba27.png">

# 本アプリのフロー定義

## 環境

|Item|Version|
|:--|:--|
|Node-RED|0.16.2|

2017年4月末時点でBluemix上にNode-RED環境を構築すると上記バージョンとなりました。

## フロー定義のインポート

フロー定義はインポート可能です。
本アプリのフロー定義は以下になります。

```json

[{"id":"506aa228.0d18dc","type":"http in","z":"33444cbb.50d004","name":"","url":"/rr","method":"get","swaggerDoc":"","x":996,"y":711,"wires":[["ef47a863.502158"]]},{"id":"86c01a38.dd5dc8","type":"debug","z":"33444cbb.50d004","name":"Debug","active":true,"console":"false","complete":"payload","x":1387.687744140625,"y":947.4136352539062,"wires":[]},{"id":"656875de.65f10c","type":"function","z":"33444cbb.50d004","name":"回答取り出し","func":"if (msg.payload.response.docs.length > 0) {\n    msg.payload = msg.payload.response.docs[0].title + \"です。\";\n} else {\n    msg.payload = \"回答できません\";\n}\nreturn msg;\n","outputs":1,"noerr":0,"x":1485,"y":1154,"wires":[["c0549441.3da728","990f985c.6896c8"]]},{"id":"74e06930.a54698","type":"http response","z":"33444cbb.50d004","name":"","x":1309.2205810546875,"y":710.2529907226562,"wires":[]},{"id":"3c2845a4.0d8f0a","type":"change","z":"33444cbb.50d004","name":"質問文の取り出し","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.q","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1133,"y":933,"wires":[["e256f1d1.ecef9","eb8da968.b80668"]]},{"id":"e256f1d1.ecef9","type":"debug","z":"33444cbb.50d004","name":"Debug","active":true,"console":"false","complete":"payload","x":1294,"y":879,"wires":[]},{"id":"c0549441.3da728","type":"debug","z":"33444cbb.50d004","name":"Debug","active":true,"console":"false","complete":"payload","x":1658,"y":1152,"wires":[]},{"id":"6b42b6dc.85bd78","type":"switch","z":"33444cbb.50d004","name":"レスポンスチェック","property":"payload.response.docs","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","outputs":2,"x":1368,"y":1065,"wires":[["990f985c.6896c8"],["656875de.65f10c"]]},{"id":"ba873c1.293cbc","type":"comment","z":"33444cbb.50d004","name":"エラーの場合","info":"","x":1521,"y":1026,"wires":[]},{"id":"a4f56bee.7efa18","type":"comment","z":"33444cbb.50d004","name":"正常の場合","info":"","x":1403,"y":1109,"wires":[]},{"id":"ee8036b0.1888d8","type":"comment","z":"33444cbb.50d004","name":"初期表示","info":"","x":993,"y":662,"wires":[]},{"id":"22940a42.554616","type":"comment","z":"33444cbb.50d004","name":"質問応答","info":"","x":993,"y":827,"wires":[]},{"id":"eb8da968.b80668","type":"watson-retrieve-rank-search-and-rank","z":"33444cbb.50d004","name":"R&R search","servicecreds":"","clusterid":"","collectionname":"","searchmode":"search-and-rank","rankerid":"","x":1236,"y":997,"wires":[["86c01a38.dd5dc8","6b42b6dc.85bd78"]]},{"id":"d612ed97.00bf9","type":"http in","z":"33444cbb.50d004","name":"","url":"/rr/ask","method":"get","swaggerDoc":"","x":1019,"y":868,"wires":[["3c2845a4.0d8f0a"]]},{"id":"990f985c.6896c8","type":"http response","z":"33444cbb.50d004","name":"","x":1621,"y":1059,"wires":[]},{"id":"ef47a863.502158","type":"template","z":"33444cbb.50d004","name":"html","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"ja\">\n    <head>\n        <meta charset=\"utf-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n        <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n        <title>Watson R&R Test</title>\n        <link rel=\"stylesheet\" href=\"//bootswatch.com/cerulean/bootstrap.min.css\">\n\n        <script type=\"text/javascript\">\n            // Node-REDのWeb API連携\n            var askWatson = function() {\n                // ボタン無効\n                $(\".btn\").addClass(\"disabled\").addClass(\"btn-default\").removeClass(\"btn-primary\");\n                // Watson連携\n                $.ajax({\n                    type: \"GET\",\n                    url: \"./ask\",\n                    dataType: \"text\",\n                    timeout: 10000,\n                    data: {\n                        //質問文を取得してパラメータとして渡す\n                        q: $(\"#question\").val()\n                    }\n                })\n                .done(function(data, textStatus, jqXHR) {\n                    // 通信成功\n                    // フローから返ってきた結果をanswerに入れる\n                    $(\"#answer\").val(data);\n                    // ボタン有効\n                    $(\".btn\").removeClass(\"disabled\").removeClass(\"btn-default\").addClass(\"btn-primary\");\n                })\n                .fail(function(jqXHR, textStatus, errorThrown) {\n                    // 通信エラー\n                    $(\"#answer\").val(\"通信エラー  \" + jqXHR.status + \" : \" + jqXHR.statusText);\n                })\n                .always(function(data) {\n                    // Debug\n                    console.log(data);\n                });\n            }\n           \n            // 質問文入力済み判定\n            var checkLengthQ = function() {\n                if ($(\"#question\").val().length > 0) {\n                    // ボタン有効\n                    $(\".btn\").removeClass(\"disabled\").removeClass(\"btn-default\").addClass(\"btn-primary\");\n                } else {\n                    // ボタン無効\n                    $(\".btn\").addClass(\"disabled\").addClass(\"btn-default\").removeClass(\"btn-primary\");\n                }\n            }\n        </script>\n    </head>\n    \n    <body>\n        <nav class=\"navbar navbar-default\">\n            <div class=\"container-fluid\">\n                <div class=\"navbar-header\">\n                    <a class=\"navbar-brand\" href=\"{{req._parsedUrl.pathname}}\">Watson R&R Test</a>\n                </div>\n            </div>\n        </nav>\n        <div class=\"container-fluid\">\n            <div class=\"row\">\n                <div class=\"col-xs-1 bg-left\"></div>\n                <div class=\"col-xs-10 bg-main\">\n                    <center>\n                        <h3>北海道の観光地について質問してください</h3>\n                        <div class=\"form-group\">\n                            <textarea class=\"form-control\" rows=3 id=\"question\" placeholder=\"ここに質問を入力してください\" oninput=\"checkLengthQ()\"></textarea>\n                            <br>\n                            <a class=\"btn btn-default disabled\" id=\"askButton\" href=\"javascript:askWatson()\">質問する</a>\n                        </div>\n                        <br>\n                        <div class=\"form-group\">\n                            <textarea class=\"form-control\" rows=5 id=\"answer\" placeholder=\"Watsonからの回答\" disabled=\"\"></textarea>\n                        </div>\n                    </center>\n                </div>\n                <div class=\"col-xs-1 bg-right\"></div>\n            </div>\n        </div>\n    <body>\n        \n    <script src=\"//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\" integrity=\"sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7\" crossorigin=\"anonymous\"></script>\n    <script src=\"//cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js\" integrity=\"sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8\" crossorigin=\"anonymous\"></script>\n    <script src=\"//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js\" integrity=\"sha384-BLiI7JTZm+JWlgKa0M0kGRpJbF2J8q+qreVrKBC47e3K6BW78kGLrCkeRX6I9RoK\" crossorigin=\"anonymous\"></script>\n</html>","x":1154,"y":709,"wires":[["74e06930.a54698"]]}]

```

クリップボードにコピーし、ハンバーガーメニューの「読み込み」－「クリップボード」を選択します。

<img width="1229" alt="スクリーンショット 2017-05-02 21.35.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/32563cb3-482b-6695-e89d-3a2bc97f7c19.png">

以下のようなフロー定義がインポートされます。
<img width="817" alt="スクリーンショット 2017-05-07 8.51.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/1c77f4e3-3b9a-e852-c80a-e2151a939395.png">

## フロー定義解説

本アプリはシンプルなSingle Page Applicationです。

「初期表示」のフローではHTMLをブラウザに返します。
Submit時に「質問応答」フローにAjaxでアクセスし、R&Rの応答を返します。

各ノードの解説は以下の通りです。

### http request

<img width="134" alt="スクリーンショット 2017-05-07 16.50.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/4de93937-04a7-b5d3-8b19-8b1820baab89.png">
HTTPリクエストを受け付けるノードです。
<img width="517" alt="スクリーンショット 2017-05-07 9.33.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/11ed4552-119a-5cde-d4df-48094af7402b.png">
Method欄でHTTPメソッドを選択します。
ここで指定したURLがアプリのアクセスポイントとなります。
Nameは全ノード共通のプロパティで、フローエディタ上に表示する名前です。（入力は任意）

### template

<img width="136" alt="スクリーンショット 2017-05-07 16.51.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/ddd34341-0e99-7654-5941-f853f887bda0.png">
HTMLテンプレートを記述します。
<img width="967" alt="スクリーンショット 2017-05-07 9.37.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/e9bff749-3157-f988-f232-7a83aa7a1381.png">
テンプレートエンジン[Mustache](https://mustache.github.io/)を使うことができます。

```html

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>Watson R&R Test</title>
        <link rel="stylesheet" href="//bootswatch.com/cerulean/bootstrap.min.css">

        <script type="text/javascript">
            // Node-REDのWeb API連携
            var askWatson = function() {
                // ボタン無効
                $(".btn").addClass("disabled").addClass("btn-default").removeClass("btn-primary");
                // Watson連携
                $.ajax({
                    type: "GET",
                    url: "./ask",
                    dataType: "text",
                    timeout: 10000,
                    data: {
                        //質問文を取得してパラメータとして渡す
                        q: $("#question").val()
                    }
                })
                .done(function(data, textStatus, jqXHR) {
                    // 通信成功
                    // フローから返ってきた結果をanswerに入れる
                    $("#answer").val(data);
                    // ボタン有効
                    $(".btn").removeClass("disabled").removeClass("btn-default").addClass("btn-primary");
                })
                .fail(function(jqXHR, textStatus, errorThrown) {
                    // 通信エラー
                    $("#answer").val("通信エラー  " + jqXHR.status + " : " + jqXHR.statusText);
                })
                .always(function(data) {
                    // Debug
                    console.log(data);
                });
            }
           
            // 質問文入力済み判定
            var checkLengthQ = function() {
                if ($("#question").val().length > 0) {
                    // ボタン有効
                    $(".btn").removeClass("disabled").removeClass("btn-default").addClass("btn-primary");
                } else {
                    // ボタン無効
                    $(".btn").addClass("disabled").addClass("btn-default").removeClass("btn-primary");
                }
            }
        </script>
    </head>
    
    <body>
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="{{req._parsedUrl.pathname}}">Watson R&R Test</a>
                </div>
            </div>
        </nav>
        <div class="container-fluid">
            <div class="row">
                <div class="col-xs-1 bg-left"></div>
                <div class="col-xs-10 bg-main">
                    <center>
                        <h3>北海道の観光地について質問してください</h3>
                        <div class="form-group">
                            <textarea class="form-control" rows=3 id="question" placeholder="ここに質問を入力してください" oninput="checkLengthQ()"></textarea>
                            <br>
                            <a class="btn btn-default disabled" id="askButton" href="javascript:askWatson()">質問する</a>
                        </div>
                        <br>
                        <div class="form-group">
                            <textarea class="form-control" rows=5 id="answer" placeholder="Watsonからの回答" disabled=""></textarea>
                        </div>
                    </center>
                </div>
                <div class="col-xs-1 bg-right"></div>
            </div>
        </div>
    <body>
        
    <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7" crossorigin="anonymous"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js" integrity="sha384-BLiI7JTZm+JWlgKa0M0kGRpJbF2J8q+qreVrKBC47e3K6BW78kGLrCkeRX6I9RoK" crossorigin="anonymous"></script>
</html>

```

### http response

<img width="133" alt="スクリーンショット 2017-05-07 16.52.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/c3ef7b36-35fd-8aa5-81f7-f5a557c1f7b5.png">
HTTPレスポンスをクライアントに返すノードです。
<img width="519" alt="スクリーンショット 2017-05-07 9.41.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/2ca619c2-1955-c9ff-b4fa-4d57a0e48412.png">
何も入力すべき項目はないのですが、このノードを配置しないとクライアントに何も返らないので注意が必要です。

### change

<img width="135" alt="スクリーンショット 2017-05-07 16.52.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/201d1f30-eff0-eaef-ee93-171e82598781.png">
changeノードはデータを編集するために利用します。
<img width="534" alt="スクリーンショット 2017-05-07 9.47.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/18544c1c-2d2e-f230-5ff8-2a63f2afb62d.png">
msgオブジェクトは、ノード間でやりとりされるデータです。payloadはそのボディ部にあたります。
ここでは、ブラウザから渡された引数payload.qをpayload自体に詰め替えて次のノードに渡しています。

### retrieve and rank search and rank

<img width="141" alt="スクリーンショット 2017-05-07 16.53.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/e7106b0c-6edd-d5dc-1622-e0908fafa9fc.png">
R&RのSearch and rank APIを呼び出すためのノードです。
<img width="516" alt="スクリーンショット 2017-05-07 9.55.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/f3d017fa-ae83-3f0d-b981-90c225f64968.png">

Service Credentialsの鉛筆アイコンをクリックすると、サービス資格情報の入力が表示されます。
<img width="595" alt="スクリーンショット 2017-05-07 10.07.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/77af890d-e9f8-78ce-ea0a-8f1670fa528c.png">

サービス資格情報は、Bluemixダッシュボードの「サービス」一覧にてR&Rを選択し、「サービス資格情報」タブ「資格情報の表示」で確認できます。
<img width="1026" alt="スクリーンショット 2017-05-07 10.09.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/8372e79c-908f-b0b1-01aa-8f2dd15a2080.png">

Cluster ID, Collection Name, Ranker IDは、公式ツールで確認し入力します。
<img width="906" alt="スクリーンショット 2017-05-07 10.13.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/c9fbf0f9-101d-24f6-7ac6-089129f2583f.png">
<img width="1225" alt="スクリーンショット 2017-05-07 10.14.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/f138c974-8e07-bb85-07a5-d1e583e21c5c.png">

### switch

<img width="137" alt="スクリーンショット 2017-05-07 16.53.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/4d66c113-0b7c-6720-5112-578a85e47fae.png">
判断分岐を行うためのノードです。
<img width="532" alt="スクリーンショット 2017-05-07 10.21.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/48e6306a-2ad9-2f75-0936-e421ff4e12ea.png">

### debug

<img width="134" alt="スクリーンショット 2017-05-07 16.54.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/ae54a64c-0a3d-db28-c476-e0adadc8aa58.png">
デバッグ出力を行うためのノードです。
<img width="512" alt="スクリーンショット 2017-05-07 10.28.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/c7ffe9dc-23b8-ddd6-471f-b9f5af8e037e.png">

以下のように、フローエディタ右側のデバッグエリアにデバッグ情報を出力してくれます。
<img width="312" alt="スクリーンショット 2017-05-07 10.27.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/311da66d-2574-7ab0-6a22-a455b9d83075.png">

前述のswitchノードでは、R&Rから"docs"というオブジェクトが返って来れば正常として後続処理を行い、そうでなければR&Rからのエラーをクライアントに返しています。

### function

<img width="137" alt="スクリーンショット 2017-05-07 16.55.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/37b17773-cffd-3a6e-d897-5561cd04c7f4.png">
JavaScriptを記述するためのノードです。
<img width="805" alt="スクリーンショット 2017-05-07 10.35.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/122917/4ef96c66-54a1-cf41-f613-9c649fc9cb1c.png">
次ノードにデータを引き継ぐには、msgオブジェクトをreturnします。

# 補足

R&R search and rankノードではR&Rのレスポンスとして回答データ（Solrコレクションデータ）のIDとTitleしか取得できず、Body（本文）を取得できません。
※2017年4月末現在の情報です。

[公式のAPI Reference](https://www.ibm.com/watson/developercloud/retrieve-and-rank/api/v1/#query_ranker)のサンプルにも載っていないのですが、実はR&R Search and rank APIではBodyも取得できます。

API Referenceのサンプルにある、"fl=id,title"の部分を"fl=id,title,body"に変更することで、Bodyを取得可能です。

ただし、functionノードにJavaScriptを記述してR&Rを呼び出す必要があります。
[Bluemix Node-REDを使ってWatson APIを呼び出す](http://qiita.com/VegaSato/items/7ce47e6a9c06c290b212)

# まとめ

[第1回の記事](http://qiita.com/y-some/items/3dc83f9d8c3c3513ecc7)に記載した通り、R&Rは、オープンソースのApache Solrと、Watson独自のRankerという仕組みを組み合わせたサービスです。

その特性上、curlコマンドによるトレーニングは少々わかりづらく、また手間のかかる作業でしたが、公式ツールであるWeb interfaceを利用することで、わかりやすく、簡単にトレーニングが可能になりました。

Node-REDについては他にもいろいろなノードがあり、また自分でノードを作成することもできますので、[Node-RED日本ユーザ会](https://nodered.jp/)のサイトなどをご参照ください。

＜参考記事＞
[Bluemix Node-REDを使ってWatson APIによる日本語質問応答システムを作ってみた](http://qiita.com/VegaSato/items/5883552a2dad2a231f07#_reference-11649d1e24dbd60f419a)