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

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

タグ:cloudant

IBM ワトソン対応の CMS である BlueCMS を公開しました。IBM Cloud を使ったセットアップ手順はこちらをご覧ください:
ワトソン対応の IBM Cloud 向き CMS "BlueCMS" を公開しました(セットアップ手順)


今回は初期セットアップ後の、実際の使い方を紹介します。


コンテンツタイトル等

初期セットアップの中で管理者権限を持った最初のユーザーを作っているので、このユーザーの ID とパスワードでログインします:
2018071001


管理コンソール画面が表示されます。管理コンソールにはコンテンツタイトルなどコンテンツ全体に関係する設定項目に続き、現在までに登録されている文書の一覧テーブルと、添付ファイルの一覧テーブルが表示されますが、ログインユーザーが管理者権限を持っている場合はコンテンツの設定項目の下にユーザー一覧テーブルも表示されます:
2018071101
(↑上からコンテンツ設定、ユーザー一覧)

2018071102
(↑上から文書一覧、添付ファイル一覧)

コンテンツ設定は以下のようになっています:
2018071103


これらは OGP(Open Graph Protocol) と言われる設定項目になっており、有名どころでは facebook で BlueCMS のトップページや各記事を共有した場合に表示される内容を定義します。

また title と desc は BlueCMS トップ画面の jumbotron の中で表示される内容でもあります。自分のブログのタイトルとその説明を記述するようにしてください。url はブログの URL、image_url は OGP イメージ画像の URL を指定します(指定していない場合は無視します)。

なお、現時点(2018/Jul/12)では個別ページの OGP を設定する機能がなく、個別ページをシェアするとトップページと同じ OGP が表示されます(リンク先の URL だけは個別ページになります)。この辺りは今後の機能拡張で対応したいと思っています。


ユーザー追加/管理

管理者権限を持ったユーザーはユーザー一覧テーブルで登録済みユーザーの一覧を確認したり、編集したり、削除したり、新規にユーザーを追加することができます:
2018071104


新規作成は一番下の編集行の各フィールドに入力して "update"、既存ユーザーの変更は右にある "edit" をクリックすると編集行に値がコピーされるので、ここで変更して "update"、ユーザーの削除は右にある "delete" をクリックします。

なおユーザー編集時には role の値に注意してください。この値が 0 のユーザーは管理者、1 のユーザーは編集者として扱われます。name は画面表示用の名称で、email はメールアドレスですが、これらは現時点では特に利用していません。


文書追加/管理

管理コンソールには現在までに登録されている文書の一覧も表示されます:
2018071105


新規作成は一番下の編集行の各フィールドに入力して "update"、既存文書の変更は右にある "edit" をクリックすると編集行に値がコピーされるので、ここで変更して "update"、文書の削除は右にある "delete" をクリックします。

なお文書の status は 1 のものが公開、0 のものは非公開(ドラフト)となります。body は nicEdit を使ったリッチテキスト編集が可能です。category はカテゴリー文字列を直接指定して入力します(category と body の値は IBM ワトソン連携時に利用する値となります)。

body の入力が狭い nicEdit を使っている点が不便であると理解しています。この辺りも今後も機能拡張の対象と考えています。


添付ファイル追加/管理

管理コンソールには現在までに登録されている添付の一覧も表示されます:
2018071106


添付ファイルの新規作成はファイルを選択後、一番下の編集行の name フィールドに入力して "update"、添付ファイルの削除は右にある "delete" をクリックします。添付ファイルには編集機能はありません。


ワトソン連携

セットアップ時に IBM ワトソンの NLC(Natural Language Classifier) 連携も含めて行っている場合は、BlueCMS 内のコンテンツを NLC に学習させたり、学習結果を使って問い合わせを行うことができます:
2018071107


文書一覧の下に NLC 関連のボタンが3つあります。それぞれ以下のように使います:

- "update NLC" : 現在までに BlueCMS に格納された全文書を NLC のトレーニングデータとして学習を初期化&再学習します。学習時には各文書の body 値と category 値だけを取り出して、body 値の内容を category 値として学習します。これを全ての文書に対して行います。

- "NLC status" : 上記学習命令を発生した後の、ワトソンのトレーニングステータスを確認します。この実行結果が "Available" となれば学習準備は完了していて、後述の "classify" で問い合わせが可能になります。一方、実行結果が "Training" であればまだ学習中なので、いましばらくお待ち下さい。

- "classify" : 学習が済んだ後に問い合わせを実行します。具体的には編集行の body に何か文章を入力した後にこのボタンをクリックすると、上述で学習させたコーパスに対してこの body 内容を問い合わせ、「今までの学習データから、どのカテゴリーがふさわしいか」の結果を取得し、category フィールドを更新します。いわば「ワトソンがその内容に相応しいカテゴリーを自動的に決めてくれる」機能です。


現時点での制限事項等

このブログエントリを編集している 2018/Jul/12 時点での BlueCMS の機能と使い方を紹介しました。上述のように CMS として足りない機能や使いにくい部分も多くあり、ワードプレスなどと比較するとまだまだだと思っています。

一方で新しくスクラッチで開発したからこそできた挑戦的な機能もあります。特に標準で IBM ワトソンと連動する機能については BlueCMS の特徴の1つだと思っています。

自分でも少しずつ使っていきながら感じた機能を拡張させていく予定ですが、もしお試し程度でも使ってみていただける場合は、感想や希望を伝えていただければと思っています。


私自身、ふだん CMS (コンテンツ管理システム)といえばワードプレスを使うことが多かったのですが、思い立って自分でも CMS を作ってみることにしました。それもせっかくなので(?)あまり例のない Node.jsIBM Cloudant をベースとした、IBM Cloud 環境向きのインフラで実装し、かつオプションで、この中で管理するコンテンツが IBM Watson の自然言語分類機能の学習コンテンツにできるような機能も付与して BlueCMS という名称で github.com で公開しました:
https://github.com/dotnsf/bluecms

使い方やセットアップ手順は README.md に(英語で)記載していますが、いちおうここでは日本語で(図解入りで)、2017/Jul/11 時点での実装内容やセットアップ手順を紹介します。

なお、このブログエントリのタイトルで「IBM Cloud 向き」と表現していますが、この意図は「IBM Cloud (の PaaS)を使うとセットアップが楽」という意味であって、普通の PC や仮想環境、各種 IaaS サーバーなどからでもセットアップ可能です(ただし IBM Cloud の Cloudant は必要です)。


BlueCMS はコンテンツ管理システムの実装の1つです。現時点では機能的にはまだまだ足りない部分もあると思いますが、CMS としての最小限の機能に加えて、以下のような特徴を持っています:
(1) 実装は Node.js、データストアには IBM Cloudant を利用
(2) IBM Cloud のライトアカウント(無料版)でも使える
(3) コンテンツ管理だけでなく、ユーザー管理機能を内蔵している
(4) 添付ファイルを格納することもできる(IBM Cloudant 内にバイナリデータとして格納)
(5) IBM Cloud ライトアカウントの範囲ではないが、IBM ワトソンと連動する機能をビルトインで使える
(6) ソースは MIT ライセンスで公開


BlueCMS そのものは IBM Cloud 以外の環境でも動きますが、IBM Cloud のアカウントを所有していると PaaS の機能を活用して比較的すぐ&簡単に環境構築できます。なお BlueCMS 自体は無料のライトアカウントでも(他にランタイムやサービスを使っていなければ)環境構築可能ですが、後述するオプションのワトソン NLC(Natural Language Classifier) 機能を利用する場合はクレジットカードを登録してスタンダードアカウント等にしておく必要があります(その場合でも利用量によっては無料枠内で運用可能です)。


セットアップ手順

IBM Cloud アカウントを所有している場合、以下の4ステップで最低限の動作環境を構築します:

(1) インフラ(ランタイムインスタンスとサービスインスタンス)の作成

とりあえず IBM Cloud にログインします:
2018071001


まず BlueCMS を実行するための Node.js ランタイムを作成します。ログイン直後の画面(ダッシュボード)右上の「リソースの作成」ボタンをクリックします:
2018071002



「Compute」カテゴリーから「SDK for Node.js」ランタイムを選択します:
2018071102


ランタイムを作成する場合は名称と地域に注意が必要です。名称は URL の一部になるためユニークな文字列を指定します(下図では bluecms としていますが、この名称は僕が使っているので使えません)。また地域はどこでもいいのですが、以降で Cloudant や NLC を作成する場合に同じ地域を指定する必要があるので、どこを選択したかを覚えておきましょう。最後に「作成」ボタンをクリック:
2018071004


これで Node.js のランタイム(アプリケーションサーバー)を作ることができました:
2018071005


このまま次のステップに移りますが、ダッシュボードへの戻り方をガイドしておきます。IBM Cloud の画面のどこからでも画面左上の三本線メニュー(「ハンバーガーメニュー」)からダッシュボードに戻ることができます。まずはここをクリック:
2018071006


そしてポップアップメニューから「ダッシュボード」を選択します。これでダッシュボードに戻ります:
2018071007


ダッシュボードに戻ると、上記で作成したランタイムが一覧に加わっていることが確認できるはずです:
2018071008


次にデータをストアするための IBM Cloudant サービスインスタンスを作成します。やはりダッシュボードから「リソースの作成」をクリックし、今度は「Databases」カテゴリの「Cloudant」を選択します:
2018071103


サービスの地域は先程 Node.js ランタイムを作成した時と同じ地域を選択し、またプランは "Lite" を選択しておくと容量やトランザクションパフォーマンスに制約があるものの、料金はかかりません。最後に「作成」をクリック:
2018071010


これだけで IBM Cloudant のインスタンスを作ることができました:
2018071011


なお Cloudant のライトプランでの制限やライトプラン以外での料金についてはこの画面内の記述を参照してください。ライトプランの場合、容量は 1GB、パフォーマンスとしては 20 データ読み取り/秒、10 データ書き込み/秒、5 データクエリー/秒となります:
2018071105



有料アカウントを利用している場合で、かつワトソン連携機能を有効にしたい場合は IBM Watson NLC サービスインスタンスを作成します。再度ダッシュボードに戻り、「リソースの作成」から「AI」カテゴリーの「Natural Language Classifier」を選択します:
2018071101


こちらでも Node.js ランタイムを作った時と同じ地域を選択し、「作成」をクリックします:
2018071013


なお NLC の料金や無料枠についてはこの画面内の記述を参照してご注意ください。1ヶ月あたりで最初の1インスタンス、4 回の学習実行、1000件の問い合わせまでは無料ですが、それらを超えた分は課金対象となります:
2018071104


こちらもサービスを作ることができました:
2018071014


ここまでの作業でアプリケーションサーバーとデータベース(、とオプションでワトソン)の各サービスが IBM Cloud 内で有効になりました。 PaaS だとこういう所が簡単で便利です:
2018071003



(2) ランタイムとサービスのバインド

IBM Cloud を利用している場合、ランタイムとサービスをバインドすることで面倒な認証情報の設定や交換を安全かつ簡易化することができます。BlueCMS はこの仕組に対応しているので、そのための設定を行います。

まずダッシュボードを表示し、ランタイム一覧から作成した Node.js ランタイムを選択します:
2018071015


現在のランタイム動作状況が確認できる画面が表示されます:
2018071016


ここで画面左のメニューから「接続」を選択します。バインド済みサービスの一覧が表示されますが、この時点では何もバインドされていないので、何も表示されません。ここに上記で作成した IBM Cloudant(とワトソン NLC)サービスをバインドします。「接続の作成」ボタンをクリックします:
2018071017


バインド可能なサービスの一覧が表示されます。上記のインスタンス作成の過程で作成地域が全て同じであれば、この一覧の中に IBM Cloudant (とワトソン NLC)が含まれているはずです。まずは IBM Cloudant サービスを選び(マウスオーバーし)、「Connect」ボタンをクリックします:
2018071018


環境を再ステージングするか、という確認ダイアログが表示されるので「再ステージ」を選択して、再起動します。再起動が完了するまで5分弱かかります:
2018071019


再ステージが完了すると、Node.js ランタイムに IBM Cloudant がバインドされた状態になり、接続一覧からも確認できるようになります:
2018071020


ワトソン NLC サービスも作成している場合は、こちらも続けてバインドします。接続メニューの一覧から NLC を選択(マウスオーバー)し、「Connect」をクリックします:
2018071021


こちらも再ステージして、Cloudant と NLC 両方のサービスがバインドされた状態になりました:
2018071022


ここまでの作業で作成した各インスタンスがバインドされ、連携できる準備が整いました:
2018071004



これでアプリケーションインフラ部分の構築が完了しました。後は BlueCMS の中身をアップロードするだけです。


(3) ソースコードの用意とランタイムにプッシュ

あとは BlueCMS のソースコードを用意して、ランタイムにプッシュ(転送)すればいいのですが、そのためには cf というツールを使います。最初に cf ツールをダウンロード&インストールします:
https://github.com/cloudfoundry/cli/releases

上記ページから自分の環境にあった cf ツールをダウンロードしてインストールします。

改めて、いよいよソースコードを用意します。github のリポジトリからソースコードを git clone するか、(その意味がわからなければ)zip ダウンロード&展開します:
2018071001


本来であればここで認証情報の取得や設定が必要になるのですが、IBM Cloud を使っているとその部分を上記の「バインド」設定で済ませていることになります。設定を行う場合はソースコード内の settings.js 内の各種値を変更します(IBM Cloud 環境を使う場合、変更する必要があるとしたら exports.search_analyzer(検索インデックスで使う言語)と、exports.nlc_language(ワトソン NLC の学習時に指定する言語)くらいです。コンテンツが日本語の場合はともに変更の必要はありません):
2018071002


settings.js の準備が完了したら、いよいよ cf を使ってコードをランタイムにプッシュします。コマンドプロンプトやターミナルを開き、まずは cf コマンドで IBM Cloudant にログインします(ログインパスワードを聞かれるので入力します):
$ cf login -a https://api.ng.bluemix.net/ -u (IBM Cloud のログイン名)

ログインできたらランタイムにプッシュするだけです:
$ cf push (ランタイムの名前(上記例だと bluecms となっている所に指定したもの))

プッシュが成功すると BlueCMS が Node.js ランタイム上で動き出します。BlueCMS は起動と同時に Cloudant 上にデータベースをインデックスごと作成するので、あらかじめ Cloudant 側で準備しておく必要はありません:
2018071005


まだ必要な管理用/編集用のユーザーを作っていないため投稿はできないのですが、この時点でトップページを表示することはできます。

ウェブブラウザで https://(ランタイム作成時に指定したアプリケーション名).mybluemix.net/ にアクセスします:
2018071000
 (まだ中身がないので今はこれだけ)

↑こんな感じのトップ画面が表示されればほぼ完成、あともう少しです!


(4) 最初の管理ユーザー ID の作成

(注 この部分の手順は将来変更の可能性がありますが)専用の API を使って BlueCMS の最初の管理ユーザーを作成します。

BlueCMS には2種類のユーザーがいます。1つが管理者ユーザー、もう1つが編集者ユーザーです。BlueCMS のコンテンツを作成/編集することができるのは管理者ユーザーと編集者ユーザーで、ユーザーの作成/変更/削除といったことができるのが編集者ユーザーです。

BlueCMS の場合、ログインしなくてもコンテンツの一覧や1つ1つを参照することはできます。ただコンテンツの中身を編集したり、編集者ユーザーを登録したりするにはユーザー登録が必要になります。これらの編集/登録作業は BlueCMS にログインした後のコンソールから行うことが可能ですが、一番最初の管理者ユーザーだけは(まだ誰もコンソールにアクセスできないため)別の方法で作成する必要があります。その方法を以下に紹介します。

最初の管理者ユーザーは curl コマンドを使って、以下の REST API を使って作成します:
$ curl -XPOST -H 'Content-Type: application/json' 'https://(ランタイム作成時に指定したアプリの名前).mybluemix.net/adminuser' -d '{"id":"abc@xyz.com","password":"yourpassword","name":"yourname","email":"abc@xyz.com"}'

-d オプションに続いてパラメータが指定されています。それぞれ以下のような意味があります:
 id: ログインID(ログイン時に指定するユーザーID)
 password: ログインパスワード(ログイン時に指定するパスワード)
 name: 画面に表示される時の名前、省略時は id が使われる
 email: メールアドレス、省略時は admin@admin となる


管理者権限で編集することはあまり想定しておらず、あくまで管理者権限で編集者を作成し、編集者権限でログインしてコンテンツを編集、することを想定しています。そのためこの最初の管理者ユーザーはどちらかというと「編集者ユーザーの管理者」的な位置づけになりますが、その管理者ユーザーを上記コマンドで作成しました:
2018071006


このコマンドが成功(結果に status:true が含まれている)すれば、このユーザーで BlueCMS にログインすることができるようになります。これで BlueCMS を使うために必要な最低限の準備は完了です。


ログイン

では作成した管理者ユーザーでログインしてみます。ウェブブラウザで https://(ランタイムに作成したアプリ名).mybluemix.net/ にアクセスし、画面右上の login ボタンをクリックします:
2018071000


ログインフォームが表示されたら、先程の REST API で作成した管理者ユーザーの id とパスワードを指定して login をクリックします:
2018071001


正しい情報が指定されていればログインし、管理用コンソールに移動します:
2018071002


この管理用コンソールでユーザーの作成/編集/削除を行ったり、コンテンツの作成/編集/追加、添付ファイルの作成と削除を行うことができます。その使い方についてはまた別の機会に、とりあえず BlueCMS とその初期セットアップ方法の紹介でした。


(2018/Jul/12 追記)
続きの使い方の説明はこちら:
ワトソン対応の IBM Cloud 向き CMS "BlueCMS" を公開しました(使い方)


僕自身はオープンソース製品である Hyperledger FabricHyperledger Composer を使ったブロックチェーンプラットフォームや、その対応アプリケーションを業務で開発する機会が多くあります。

ありがたいことに実際に作る機会も多いのですが、「作る」という決断に到達する前に方針を変更することも少なくありません。よくある理由としては「(現行の)ブロックチェーンに向かない要件」であるというケースです。パフォーマンス要件だったり、検索機能の要件だったり、現在のブロックチェーンでは本格運用に向かないケースが前提になっていたりすると、そのことを伝えた上で「それでも作るかどうか」を判断していただいています。そして「作らずに検討し直す」と判断されることもある、という意味です。

でも、これはブロックチェーンを使いたいと思いながらも「現在のブロックチェーン技術」では向かない、と判断されたからです。需要としては存在していることも意味しています。

そんな場面を多く見ている中で「だったらブロックチェーンといえるかどうかはともかく、同じような仕組みで耐改竄性を高めながら、この要件も満たせるようなデータストアの仕組みを作れないか?」と考えるようになりました。それが今回紹介するものです。


【「ブロックチェーン」の定義】
まず最初に、以下で紹介するアプリケーションは「ブロックチェーン」と呼べるものかどうかと言われると、私個人的には「呼べないのではないか」と考えています。これは「ブロックチェーンの定義」をどのようにするか、という問題にも関わってくるのですが、ここでは1つの目安として、JBA("Japan Blockchain Association" 日本ブロックチェーン協会)が定義した以下の2つの定義を満たすものをブロックチェーンと定義するものとします:

・ビザンチン障害を含む不特定多数のノードを用い、時間の経過とともにその時点の合意が覆る確率が0へ収束するプロトコル、またはその実装をブロックチェーンと呼ぶ。
・電子署名とハッシュポインタを使用し改竄検出が容易なデータ構造を持ち、且つ、当該データをネットワーク上に分散する多数のノードに保持させることで、高可用性及びデータ同一性等を実現する技術を広義のブロックチェーンと呼ぶ。

(参照)
http://jba-web.jp/archives/2011003blockchain_definition

2018062901



要するに「ブロックチェーンとは『ブロック』が『チェーン』のようにつながって格納される仕組み」というゆるい定義ではなく(注 私個人的にはこれがブロックチェーンの定義でもいいと思ってます)、「ハッシュポインタを用いて接続し、データを複数のノード上に分散して保持し、かつその複数ノードが耐ビザンチン障害性を持っている実装」のことを「ブロックチェーン」と定義する、ということです。 そしてこの定義の上で考えた場合、以下で紹介するものはブロックチェーンではない、ということになることを予め伝えておきます。

自分の考えは、上記のものだと耐ビザンチン障害性をもたせるためのコンセンサスアルゴリズムに問題が発生したり(最近だと「51%攻撃」と呼ばれる手法が話題になりました)、そのためにトランザクションのパフォーマンスを高く実現するのが難しかったりする、と思っています。なのでこの要件が必須でないケースでは、この条件を確保しないことでパフォーマンスや検索機能など他の要件機能を向上させることができるのではないかと考えました。


【色々出てきている】
現状のブロックチェーンにはパフォーマンス遅延や電力消費など当初は想定していなかったことが問題になっていることもあって、それらの苦手部分に対処した新しい仕組みも生まれています(それらが上記ブロックチェーンの定義を満たしているかどうかはともかく)。例えば全参加者のコンセンサスを得る上でのパフォーマンス遅延を解決するために、ランダムに選ばれた一部参加者のコンセンサスを得るように改良したものも出ているようです:
ブロックチェーンを超える?ハッシュグラフ(Hashgraph)とは?


【作ってみようと思ったもの】
今回、自分は以下4つの特徴を持つようなデータストアシステムを新たに開発してみようと思いました:
(1)ハッシュポインタを利用した、改竄検出が容易なブロックチェーンの仕組みはそのまま取り入れる
(2)データは中央集権管理型とする。これによりコンセンサスアルゴリズムや耐ビザンチン障害性の問題を発生させなくした上で、トランザクションパフォーマンスの向上を実現する
(3)データそのものは分散データストアに格納して(格納可能にして)高可用性を実現する。つまり単なる1ピア運用とは異なる
(4)バイナリデータやファイル添付を含む大きなレコードのデータストアへの格納や全文検索機能、データを読み書きするための REST API を標準装備する



(2)の時点で上記ブロックチェーンの定義を満たさなくなっています(ブロックチェーンは非中央管集権管理型)。(3)ただ単に1ピアにデータを格納するのではなく、論理的な1データストアが実際には複数のロケーションに分散できるようにしており、これにより特定ロケーションが単一障害点とはならないようにします。そして(4)ブロックチェーンが比較的苦手とする巨大オブジェクトの格納や検索機能もこのシステムでは標準装備することを目標としています。加えて標準で REST API を準備することで(導入すればすぐ使えるようになって)導入時の負担軽減も実現するつもりです。

2018062902



【作っているもの】
上記仕組みを実現するためのソフトウェア(というかプラットフォームというか、API 群というか・・)を開発中です。まだ開発中ですが Github にて MIT ライセンスで公開しています:
https://github.com/dotnsf/hashchainsolo


上記の事情もあり、開発コード名には「ブロックチェーン」は含めず「ハッシュチェーンソロ」という表現にしました。レコードのブロックをチェーンのようにつなげていくものではあるのですが、上述の定義を満たすようなものではなく(むしろそこを犠牲にしている)、自分も「ブロックチェーン」という表現にこだわるわけではないので「ハッシュチェーン」という表現にしたかったのですが、これはこれで別の意味に使われており、ややこしいことになりそうだったので(ブロックチェーンだと単一ピアに相当する設計になるという意味で)「ハッシュチェーンソロ」と命名しています。某○icrosoft Office 365 Solo の影響とかではありませんw

インフラとして、分散データストアには IBM Cloudant を採用しています。Apache CouchDB をベースとした NoSQL 型のマネージド DBaaS で、容量・トランザクションパフォーマンスに制約があるものの無料プランを選択することもできるので、「とりあえず動作確認してみる」ためのハードルは低く実現できると思っています。一方でプランを見直して容量やパフォーマンスを変えたり、要件によってはプライベートクラウド版やオンプレミスソフトウェア版を使うことも可能です。またこの Cloudant 自体がバイナリ(添付ファイル)格納や検索インデックスといった機能を内包しているので、ブロックチェーンでいうところの「ステート DB」に相当する仕組みや外部ファイルシステム等がなくても各種データを格納することができ、上述の4つの特徴を兼ね備えた単独の仕組みを比較的作りやすいと思っています。

#現時点では実装していない余談ですが、IBM Cloudant をデータストアに使っていることで、携帯版の PouchDB と組み合わせてモバイルプラットフォームへの移植も想定しやすい設計にしています。

ハッシュポインタを使って耐改ざん性を高める部分は全てスクラッチで実装しています。タイムスタンプと nonce を併用してマイニングを行い、特定条件(変更可)を満たした時だけハッシュを採用できるようにしています。

2018/Jun/30 の現時点での開発状況は「とりあえず一通りの REST API は動くようになっている」レベルです。時間を見つけてぼちぼちと改良中です。


【Call for Code】
この製品で Call for Code に参加予定です。自然災害の支援を目的としたソリューションのコンテストで、ブロックチェーンを使う事例は多く存在すると思いますが、そのブロックチェーン技術の選択肢の1つとして、既存技術では難しいものも場合によっては解決できる、と思っていて、その実装として提供・参加予定です。




 

 は IBM CloudantApache CouchDB と API 互換のある NoSQL データベースです。JavaScript で操作することができます。npm を使ってサーバーサイドで動かすこともできますが、ブラウザから JavaScript ライブラリをロードして、個々のブラウザ内で使うことも可能です。

特にこれをブラウザから使う場合、マスターデータはクラウドの IBM Cloudant で保持しつつ、必要なデータをユーザーのブラウザと同期して、ほぼすべての処理をローカルブラウザ内で完結させる(=時間のかかるDBアクセスをローカルDBだけを対象に行えばよくなるので、アプリケーションとしての安定性やパフォーマンス向上も期待できる)、ということが可能になります。とても強力な同期機能を持ったデータベースエンジンと言えます。
2018040900


この「同期」を具体的にどうやるか、という内容が今回のブログエントリです。


今回の説明では、サーバー側の DB を IBM Cloudant で用意することにします。IBM Cloud のライトアカウントを作成すると容量 1GB まで使えるライトプランの DBaaS が無料で用意できます。

で、この Cloudant DB をブラウザに同期・・・するわけですが、IBM Cloudant を使う場合はその前に1つ設定が必要です。標準状態の IBM Cloudant はクロスオリジンからのアクセスを許可していません。そのため、標準設定のままウェブブラウザから同期をとろうとするとクロスオリジンアクセスになってしまい、エラーとなってしまいます。したがってクロスオリジンアクセスを許可するよう、設定を変更する必要があります。詳しくはこちらにも記載していますが、curl コマンドと IBM Cloudant の API を使って IBM Cloudant の CORS アクセスを有効にするための設定を行います:
$ curl -i -u 'db_username:db_password' -XPUT 'https://db_username.cloudant.com/_api/v2/user/config/cors' -H 'Content-type: application/json' -d '{"enable_cors":true,"allow_credentials":true,"origins":["*"]}'

↑db_username, db_password は IBM Cloudant のインスタンスにアクセスするためのユーザー名およびパスワードです。


次にブラウザ内の JavaScript で DB の同期を行います。普通に「データベースそのものの同期をとる」のであれば話は単純で、以下のようなコードを記述するだけです:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

remoteDB.sync( localDB, {
live: true, retry: true });
: :

まず CDN を指定して PouchDB のライブラリをロードします。そして IBM Cloudant のユーザー名(db_username)とパスワード(db_password)を指定して、'testdb' という名前のデータベースインスタンスを remoteDB 変数に代入します。これで IBM Cloudant 上のリモートデータベースを remoteDB から操作できるようになりました。同時にローカルブラウザ内に(同じ名前の)'testdb' という名前のデータベースインスタンスを作って localDB 変数に代入しています(こちらは作成時点では空です)。 この2つのリモート/ローカルデータベースを sync() 関数で同期するように指定しています。これによってこれら2つのデータベースインスタンス変数の内容は自動同期され、一方(例えばブラウザ内の localDB)に変化が起こるともう一方(サーバーの remoteDB)にその変化内容が勝手に反映される、という仕組みが実現できます。

ちなみに sync() 実行時の live:true オプションはリアルタイム同期の指定で、retry:true オプションは一度接続が切れた後に自動的にリトライして接続が戻った時に同期も復活させるための指定です。ここまでは超簡単です。


さて、本来やりたかったのはデータベースをまるごと同期するのではなく、データベースの一部だけを同期する、というものです。上記例だと remoteDB はサーバー側のものなので、全部で数ギガバイトになったりそれ以上になったりすることも想定しないといけないのですが、localDB はブラウザ内で作るものなのであまり大きくなっては困ります。そこで(一般的には部分同期とか Partial Sync とか呼ばれる方法で)特定条件を満たす一部の文書だけを対象にローカルに同期し、ローカルでの変更・追加・削除といった処理をサーバー側のマスターに同期し直す方法を紹介します。

PouchDB にも部分同期機能は存在しています。ただそこで「この条件を満たす文書だけ」を指定する方法がかなり限られていて、現時点では文書 ID を配列で指定する方法しかないように見えます(このフィールドがこの値で・・・みたいなクエリーではできないっぽい)。具体的にはこんな感じ:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

var doc_ids = [ 'xxx', 'yyy', 'zzz' ];  //. 同期対象文書の _id 値配列

remoteDB.sync( localDB, {
doc_ids: doc_ids,
live: true, retry: true }); : :

上記例では 'xxx', 'yyy', 'zzz' という3つの文書ID(Cloudant 内だと文書の _id の値)を指定して、sync() 関数を doc_ids パラメータで配列指定しています。これだけで localDB には指定された3文書だけが同期され、削除を含めた変更があるとリモートにも即時に反映されるようになります。

したがって実装する場合はページロード時の最初に IBM Cloudant に対してクエリー API を実行して同期の対象となる文書(の ID 配列)をまとめて取得し、その ID 配列を指定して PouchDB と部分同期する、という流れになると思っています。この方法なら最初のロード時のネットワーク接続は必須になりますが、どのみちページをロードするにもネットワークは必須だし、同期をとった後はネットワークが切れても平気、というシステム構成が可能になります。


なお、一点だけ注意が必要なことがあります。この方法で部分同期した後に localDB に新たに文書を追加した場合です。部分同期の条件は doc_ids に指定された文書ID配列だったので、ここに含まれない新しい文書を追加してもサーバー側には同期されません。その場合は新たに doc_ids を指定しなおして(新しい文書の ID を追加して)改めて sync() 関数を実行する必要があります:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

var doc_ids = [ 'xxx', 'yyy', 'zzz' ];

remoteDB.sync( localDB, {
doc_ids: doc_ids, live: true, retry: true }); : : function add( doc ){ //. ドキュメントを追加する処理 localDB.put( doc ).then( function( res ){ //. localDB に文書(doc)追加 doc_ids.push( doc._id ); //. 文書の _id 値を配列に追加 remoteDB.sync( localDB, { //. sync() 再実行 doc_ids: doc_ids, live: true, retry: true }); }).catch( function( err ){ console.log( err ); }); }

例えば上記例では add( doc ) 関数の中で doc に指定されたオブジェクトを localDB に追加する想定で処理を記述していますが、localDB.put() が成功したら doc_ids 配列を更新した上で remoteDB.sync() を再実行して同期条件を変えるようにしています。

現実問題としては追加なのか更新なのか(どちらも put() 関数を使う)、更新だとすると _rev の値も必要になって・・・とか、多少細かい実装が必要になることも事実ですが、一応これだけでローカルDBとその部分同期を使ったアプリの実装はできることになります。

問題は上述したローカルDBに同期したい文書の ID をどうやって調べるかですが、そこはやっぱり一度サーバーにクエリー投げるしかないのかなあ・・・


なお、PouchDB の API Reference はこちらを参照ください:
https://pouchdb.com/api.html


IBM Cloud(Bluemix) から提供されている NoSQL データベースの DBaaS である Cloudant は読み書きのための REST API が公開されています。各種プログラミング言語から HTTP ベースの API を実行してデータを読み書きすることが可能です。

ただ、これらの API には一般的な CORS(Cross-Origin Resource Sharing) の制限がかかっており、ウェブブラウザの JavaScript からは読み書きができないように設定されています。この CORS 制限を無効にする方法が分かったので、ブログで紹介する形で手順等を紹介します。

まず今回ブラウザからアクセスする対象とするデータベースをこちらとします。pouchdb というデータベースで、現在4件のドキュメントが登録されています:
2018040900


このデータベースに簡単にアクセスするため、今回は PouchDB ライブラリを使うことにします。PouchDB は軽量かつ CouchDB(Cloudant) 互換のデータベースです。この PouchDB を CDN(//cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js) からロードして、データベースオブジェクトを作り、その中の全文書を取り出して表示する、という処理を実装すると↓のような感じになります:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>CORS check</title>
<script src="//cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var cloudant_db_url = 'https://USERNAME:PASSWORD@USERNAME.cloudant.com/pouchdb';

var db = new PouchDB( cloudant_db_url );

db.allDocs( { include_docs: true } ).then( function( docs ){
  console.log( docs );
}).catch( function( err ){
  console.log( 'error' );
});
</script>
</head>
<body>
</body>
</html>

主な処理内容を簡単に解説します。まず CDN から PouchDB ライブラリをロードし、Cloudant のデータベース URL を指定して、データベースオブジェクトを作ります(上記の USERNAME は Cloudant のユーザー名、PASSWORD は同パスワードです)。そして allDocs() メソッドで全文書を取り出して結果を console.log() でコンソールに表示する、という内容の JavaScript を含む HTML になっています。


この HTML を HTTP サーバー上に配置してウェブブラウザからアクセスします。取得結果はコンソールに表示されるので、あらかじめコンソール画面を表示(FireFox であれば F12 キー)しておきます。その状態でブラウザから同ページにアクセスすると・・・コンソールには「クロスオリジン要求・・」というエラーが表示されます。これはつまり Cloudant 側でクロスオリジンからのアクセスを許可していないため、アクセスは拒絶され、そのエラーが表示されています。これが Cloudant のデフォルトでの挙動です:
2018040901


では Cloudant の CORS アクセス(クロスオリジンからのアクセス)を有効にしてみます。curl コマンドの使えるターミナルから、以下のコマンドを実行します:
$ curl -i -u 'USERNAME:PASSWORD' -XPUT 'https://USERNAME.cloudant.com/_api/v2/user/config/cors' -H 'Content-type: application/json' -d '{"enable_cors":true,"allow_credentials":true,"origins":["*"]}'

このコマンドでは認証用の ID とパスワード、HTTP ヘッダの Content-Type: application/json を指定し、/_api/v2/user/config/cors パスに対して、CORS アクセスを有効にするようデータを POST して実行しています:

2018040902


上記のように {"ok": true} という結果が返ってくればコマンドは成功し、クロスオリジンからのアクセスも許可されています。試しに再度同じ HTML ページを(リロードするなどして)表示すると、今度は allDocs() メソッドが成功し、期待通りに(4件の)データを取得し、コンソールに表示できているはずです:
2018040903


これで Cloudant の API をブラウザ(の JavaScript )からも直接実行する術が確保できました。これでウェブブラウザの HTML から直接 Cloudant を操作したり、ウェブブラウザ内の PouchDB と連携することもできるようになります。



(参考)
How to use CORS with a Cloudant account

CORS


このページのトップヘ