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

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

タグ:cloudant

本ブログエントリは IBM Cloud アドベントカレンダー 2021 に参加しています。12/4 ぶんのネタです。


IBM Cloud から提供されているマネージド NoSQL データベースである IBM Cloudant をよく使っています。最大の魅力は無料アカウントでも容量 1GB まで使えるストレージで、かつデータが分散管理されているという安全性です。仕様が曖昧な状態からアジャイルに作り始める際は(テーブル定義などを意識する必要がないこともあって)むしろ NoSQL データベースの方が便利だったりもします。

この IBM Cloudant を Node.js プログラムから使う場合のライブラリとして長く @cloudant/cloudant を使ってきましたが、2021/12/31 を以って End of Support を迎えることになり、現在は deprecated なライブラリ、という扱いになっています。2021年8月の今は「まだ使えるけど、もうすぐ使えなくなるよ」って所でしょうか。可能であれば、これから新たに作り始める時には利用を避けたほうが安全だと思います。

で、その後継ライブラリとして公開されているのが @ibm-cloud/cloudant (IBM Cloudant Node.js SDK)です。IBM Cloudant だけでなく、そのベースとなっている Apache CouchDB に対してももちろん使うことができるものです。現在 @cloudant/cloudant ライブラリを使っているアプリケーションはなるべく今年中にこの新しい @ibm-cloud/cloudant に移植することをおすすめします。

・・・と言うのは簡単ですが、実際この @ibm-cloud/cloudant は @cloudant/cloudant と比べてどのくらい似ていて/同じで、移植はどの程度簡単/難しいのでしょう? というわけで、まずは @ibm-cloud/cloudant を使ってみることにしました。


【サンプルソースコード】
以下で紹介するサンプルアプリケーションのソースコードを github で公開しています:
https://github.com/dotnsf/cloudant-node-sdk_sample


【接続方法の決定】
@ibm-cloud/cloudant を使って Cloudant(CouchDB) にアクセスする場合の接続方法には3通りあります:
(1)IAM
(2)COUCHDB_SESSION
(3)BASIC


(1)の IAM は IBM Cloudant を IAM 接続サポート形式で作成した場合に利用できる方法です。公式ドキュメントでも「この方法が使える場合はこの方法で」と紹介されています。

(2)の COUCHDB_SESSION を使った方法でも接続が可能です。

(3)の BASIC はいわゆる「ベーシック認証」です。この3つの中では「この方法しか使えない場合の選択肢」と紹介されています。CouchDB を自分でインストールして使う場合など、この方法で接続する前提のセットアップをしているとこの方法でしか接続できないことになります。もちろん Cloudant でもベーシック認証をサポートした形式で作成している場合は利用可能です。

(1)、(2)、(3)のいずれの方法でアプリケーションとデータベースを接続するかを決めておく必要があります。正確には @ibm-cloud/cloudant は「環境変数を設定して接続する」のですが、どの方法で接続するかによって、設定が必要になる環境変数が変わる点に注意が必要です。


【Node.js から接続する際に必要な環境変数】
@ibm-cloud/cloudant を使って Cloudant/CouchDB に接続する際に設定が必要な環境変数は以下です(横のカッコ付き数字は、上述の(1)、(2)、(3)のどの方法を使った時に必要な環境変数か、を表しています)。また変数名の頭の "CLOUDANT" 部分は別の値でも構いませんが、同じ文字列に統一して設定する必要があります:

変数意味
CLOUDANT_AUTH_TYPE上述の接続方法。"IAM"(1), "COUCHDB_SESSION"(2), "BASIC"(3) のいずれか。デフォルト値は "IAM"
CLOUDANT_URLデータベースの URL((1)、(2)、(3))
CLOUDANT_APIKEYAPI キーの値((1))
CLOUDANT_USERNAMEユーザー名((2)、(3))
CLOUDANT_PASSWORDパスワード((2)、(3))


環境に合わせてこれらの値を用意して実際に接続してみます。


【Node.js から Cloudant に接続】
(1)IAM 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'IAM'; //(デフォルト値なので設定しなくてもよい)
process.env['CLOUDANT_APIKEY'] = 'xxxxx'; //(API Key の値)
process.env['CLOUDANT_URL'] = 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud'; //(Cloudant のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT' } ); 

このコード実行が成功すると、Cloudant に接続したインスタンスが client という変数に格納され、実際のデータの CRUD 処理が可能になります。

なお上記コードの serviceName 値として 'CLOUDANT' を指定していますが、この部分は変更可能です。ただ変更する場合は環境変数として指定した変数名の頭の CLOUDANT 部分をここで指定する値と同じものに変更してください。

(2)COUCHDB_SESSION 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'COUCHDB_SESSION';
process.env['CLOUDANT_USERNAME'] = 'username'; //(ユーザー名の値)
process.env['CLOUDANT_PASSWORD'] = 'password'; //(パスワードの値)
process.env['CLOUDANT_URL'] = 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud'; //(Cloudant/CouchDB のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT' } ); 

(3)BASIC 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'BASIC';
process.env['CLOUDANT_USERNAME'] = 'username'; //(ユーザー名の値)
process.env['CLOUDANT_PASSWORD'] = 'password'; //(パスワードの値)
process.env['CLOUDANT_URL'] = 'http://xxx.xxx.xxx.xxx:5984'; //(Cloudant/CouchDB のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT', disableSslVerification: true } ); 

なお(CouchDB の場合に多いと想像していますが) SSL 接続が不要の場合は上述のように接続時のパラメータに disableSslVerification: true を追加してください。

参考までに、@cloudant/cloudant の場合は、
var Cloudantlib = require( '@cloudant/cloudant' );
var cloudant = Cloudantlib( { account: "username", password: "password", url: 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud' } );
var db = cloudant.db.use( "dbname" );

といった感じでデータベースまでを取得した上で、
db.get( "id", function( err, result ){
  if( err ){
    console.log( err );
  }else{
    console.log( result );
  }
});

のようにして特定データを取得したりしていましたが、この @ibm-cloud/cloudant ではまだデータベースが特定されていない点にご注意ください。


【接続後の CRUD 処理の例】
では接続後の Cloudant クライアントを使って、IBM Cloudant のデータを読み書きしてみましょう。まず対象データベースと id がわかっている特定データを取得してみます。@ibm-cloud/cloudant では以下のような getDocument() メソッドを使うコードとなります:
client.getDocument( { db: "dbname", docId: "id" } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

client.getDocument() を実行し、そのパラメータ内でデータベースと id を指定する、という方式になります。この点からして @cloudant/cloudant とは異なってますね。

また特定データベースの全データをまとめて取得(@cloudant/cloudant だと db.list() )する場合は以下のような postAllDocs() メソッドを使うコードです:
client.postAllDocs( { db: "dbname", includeDocs: true } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

データを一件追加する場合は以下のような postDocument() メソッドを使うコードになります:
client.postDocument( { db: "dbname", document: { name: "Kimura" } } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

以上、あくまでいくつかのメソッドを紹介しただけですが、全般的に行うオペレーションとメソッド名の関係がわかりやすく整頓されているように感じて、比較的慣れやすいのではないかと感じています。



IBM Cloud から提供されている 30 日間無料 Kubernetes サービスIBM Kubernetes Service 、以下 "IKS")環境を使って利用することのできるコンテナイメージを1日に1個ずつ 30 日間連続で紹介していきます。

環境のセットアップや制約事項については Day0 のこちらの記事を参照してください。

Day 6 からはデータベース系コンテナとその GUI ツールを中心に紹介してます。Day 16 では個人的に大ファンな IBM Cloudant データベースサービスのベース製品でもあるApache CouchDB イメージをデプロイする例を紹介します。
couchdb



【イメージの概要】
元 IBM 社員だった Damien Katz さんが開発した NoSQL 型データベースです。単にデータを格納するだけでなく、画面の UI データを格納して文書データと合わせて表示する、といった(ノーツに似た)デザインドキュメントの概念も実装されていて、ノーツファンとしては非常に感銘を受けるデータベースです。IBM Cloudant ではこの CouchDB をマルチサーバーによる分散データ管理する仕組みがマネージドサービスとして提供されています。



【イメージのデプロイ】
まずはこちらのファイルを自分の PC にダウンロードしてください:
https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/couchdb.yaml

次にこのファイルをテキストエディタで開いてパラメータを編集します。具体的には以下2箇所の value 値を変更してください:
・COUCHDB_USER : ログイン時のユーザー名(初期値 admin)
・COUCHDB_PASSWORD : ログイン時のパスワード(初期値 P@ssw0rd)


ではこのダウンロード&編集した couchdb.yaml ファイルを指定してデプロイします。以下のコマンドを実行する前に Day 0 の内容を参照して ibmcloud CLI ツールで IBM Cloud にログインし、クラスタに接続するまでを済ませておいてください。

そして以下のコマンドを実行します:
$ kubectl apply -f couchdb.yaml

以下のコマンドで CouchDB 関連の Deployment, Service, Pod, Replicaset が1つずつ生成されたことと、サービスが 30984 番ポートで公開されていることを確認します:
$ kubectl get all

NAME                           READY   STATUS    RESTARTS   AGE
pod/couchdb-7866c5d4c6-z462x   1/1     Running   0          12s

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/couchdbserver   NodePort    172.21.71.164   <none>        5984:30984/TCP   13s
service/kubernetes      ClusterIP   172.21.0.1      <none>        443/TCP          27d

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/couchdb   1/1     1            1           14s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/couchdb-7866c5d4c6   1         1         1       14s

この後に実際にサービスを利用するため、以下のコマンドでワーカーノードのパブリック IP アドレスを確認します(以下の例であれば 161.51.204.190):
$ ibmcloud ks worker ls --cluster=mycluster-free
OK
ID                                                       パブリック IP    プライベート IP   フレーバー   状態     状況    ゾーン   バージョン
kube-c3biujbf074rs3rl76t0-myclusterfr-default-000000df   169.51.204.190   10.144.185.144    free         normal   Ready   mil01    1.20.7_1543*

つまりこの時点で(上述の結果であれば)アプリケーションは http://169.51.204.190:30984/ で稼働している、ということになります。CouchDB はこの URL が管理ダッシュボードの URL になっているので早速実行してみます。ウェブブラウザを使って、アプリケーションの URL(上述の方法で確認した URL)にアクセスしてみます:
couchdb1


(シンプルすぎて)一瞬びっくりしますが、稼働中の CouchDB に関する情報が表示されました。CouchDB も IKS 上で動かすことができました。


【YAML ファイルの解説】
YAML ファイルはこちらを使っています(編集する前の状態です):
apiVersion: v1
kind: Service
metadata:
  name: couchdbserver
spec:
  selector:
    app: couchdb
  ports:
  - port: 5984
    protocol: TCP
    targetPort: 5984
    nodePort: 30984
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: couchdb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: couchdb
  template:
    metadata:
      labels:
        app: couchdb
    spec:
      containers:
      - name: couchdb
        image: couchdb
        env:
        - name: COUCHDB_USER
          value: "admin"
        - name: COUCHDB_PASSWORD
          value: "P@ssw0rd"
        ports:
        - containerPort: 5984

Deployment 1つと、Service 1つのごくごくシンプルな YAML ファイルですが、一応解説を加えておきます。アプリケーションそのものは 5984 番ポートで動作するように作られているため、NodePort 30080 番を指定して、外部からは 30080 番ポートでアクセスできるようにしています(NodePort として指定可能な番号の範囲は 30000 ~ 32767 です、指定しない場合は空いている番号がランダムに割り振られます)。また ReplicaSet は1つだけで作りました(データベースなので、別途クラスタ構成の準備をしない限りはこの数値だけを増やしてもあまり意味ないと思います)。


デプロイしたコンテナイメージを削除する場合はデプロイ時に使った YAML ファイルを再度使って、以下のコマンドを実行します。不要であれば削除しておきましょう:
$ kubectl delete -f couchdb.yaml


【紹介したイメージ】
https://hub.docker.com/_/couchdb


【紹介記録】
Dayカテゴリーデプロイ内容
0準備準備作業
1ウェブサーバーhostname
2Apache HTTP
3Nginx
4Tomcat
5Websphere Liberty
6データベースMySQL
7phpMyAdmin
8PostgreSQL
9pgAdmin4
10MongoDB
11Mongo-Express
12Redis
13RedisCommander
14ElasticSearch
15Kibana
16CouchDB
17CouchBase
18HATOYA
19プログラミングNode-RED
20Scratch
21Eclipse Orion
22Swagger Editor
23R Studio
24Jenkins
25アプリケーションFX
262048
27DOS Box
28VNC Server(Lubuntu)
29Drupal
30WordPress

IBM Cloud から提供されている NoSQL のマネージドデータベースである Cloudant (実体は Apache CouchDB)を便利に使っています。最大の特徴の一つが「無料でも一定条件下で使える」点ですが、今回は無料である以外の便利な使い方を紹介します。

この記事の中で紹介するのはこのような使いかたです:
(1) Cloudant の CORS を有効に設定する(特定ドメインのページからの REST API リクエストを受け付けて読み書きできるようにする)
(2) Github ページに用意したウェブページから (1) で有効に設定した Cloudant のデータを読み書きする


2021022401


最終的に実現したいのは、世の中の SaaS(といっていいのかな)の中でもかなり可用性高く、安定して動いている印象の Github ページを使ったウェブアプリケーションを作ることです。ただ静的ページそのものは安定して稼働していてもデータの読み書きが出来なければウェブアプリケーションとしては不十分で、そこを REST API で読み書きできる IBM Cloud の Cloudant データベースで賄えないか、と考えました。ただそのままでは CORS の制約もあって API が実行できないのですが、Cloudant 側の設定で CORS を正しく設定して Github ページからの読み書きを可能な状態にする、そのための Cloudant の設定手順を確認する、というのが目標です。

以下では Github のアカウントを持っている方を対象に、Github ページを使って作成するウェブページから Cloudant にアクセスするサンプルを紹介します。Github ページでなくても(静的ページのホスティング環境があれば)同様に可能なはずですが、その場合は適宜読み直してください(特に CORS 設定時のドメインなど)。


【CORS】
"Cross-Origin Resource Sharing" 、つまり「オリジン間リソース共有」の略語です。ここでの「オリジン」は「ドメイン」と読み替えたほうが理解しやすいかもしれまえん。

例えば A というウェブサーバーが https://aaa.com/ 上で、また B というデータベース・サーバーが https://bbb.net/ 上で動いていると仮定します(つまり全く別々に管理されている2つのサーバーです。この状態を Cross-Origin と呼びます)。A 上のウェブページで表示される画面の中で B から取得したデータを表示しようと思っています。

例えば A が Java などのプログラミング言語によって作られたアプリケーションサーバーであった場合、A は B からのデータ取得に Java を使うことになります。A の中で Java が実行され、B にアクセスしてデータを取り出し、その結果を使って A のページの画面を作成することができます。A の内部でプログラミング言語が使われている場合は、Cross-Origin であってもこのような方法でデータを取得することができます。

ところが A がアプリケーションサーバーではなく、ただの(既存の HTML ページを表示するだけの)ウェブサーバーであった場合、A から B にアクセスしてデータを取得する手段はかなり限られてしまいます。その限られた手段の1つが AJAX などに代表される JavaScript 処理です。A のサーバーにプログラミング言語が用意されていなくても、ウェブブラウザ自体が持つ JavaScript 実行環境を使って B サーバーにアクセスしてデータを取得する、という考え方です。ただし Cross-Origin の場合、A にアクセスしたウェブブラウザから B のサーバーに JavaScript で HTTP アクセスすることは原則できないことになっています(Cross-Origin でなければ、つまり A = B であれば可能です)。これが CORS の制約です。

CORS の制約はウェブブラウザが持っている制約なので、ウェブブラウザを使わない HTTP 通信(上述の Java 言語によるプログラミングによる通信など)には関係ありませんが、(ウェブブラウザの)利用者側でこの制約を解除する方法はありません。唯一の方法がリソース提供側(この場合だと B サーバー)による許可のみです。

今回のブログエントリは IBM Cloud のデータベースの1つである Cloudant の CORS をうまく設定することで、代表的な静的ウェブページの1つである Github ページから(Cloudant 内の)外部データを読み書きする方法について紹介する、というものです。


【Cloudant の用意】
まず IBM Cloud のアカウントを取得します。IBM Cloud アカウントを新規に取得した時点では「ライトアカウント」と呼ばれる無料の制約のあるアカウントとなります。今回紹介する作業を実行するだけであれば(ライトアカウントだとデータは 1GB までの格納となりますが、この容量などに問題なければ)ライトアカウントのまま実行いただいても構いません。

改めて取得したアカウントで IBM Cloud にログインし、「リソースの作成」ボタンクリック後に「Cloudant」を選択します:
2021022501

2021022502


インスタンス作成前にいくつか設定箇所があります。主なものは以下になりますが、"Authentification Method" だけはデフォルトの "IAM" ではなく "IAM and legacy credentials" を選択してください:
・Available Regions: インスタンスを作成するエリア、お好きな場所でいいが「東京」あたりが無難
・Authentification Method: 今回は必ず "IAM and legacy credentials" を選択
・Plan: 料金プラン。「Lite」であれば無料

最後に "Create" ボタンで作成します:
2021022503


Authentification Method について補足しておくと、デフォルトの "IAM" だといわゆる API キーを使った認証/認可を行います。ただ今回はアプリケーションサーバーからではなく(GitHub ページの)静的なページから Basic 認証を使ってデータベースの REST API を使うことを想定しています。この場合は "IAM" ではなく "IAM and legacy credentials" (API キーまたは Basic 認証を使う)を選択しておく必要があるためです。


少し待つと Cloudant インスタンスが起動済みになります。インスタンスを開いて「サービス資格情報」タブを選び、「新規資格情報」ボタンを押して資格情報を作成します。作成後に作成した資格情報(「サービス資格情報-1」のような名前になっていると思います)を選択・展開して、中身を確認します:
2021022504


資格情報の中身は以下のような内容の JSON 文字列になっているはずです(この中身は他人に見せないように注意してください):
{
  "apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "host": "xxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "iam_apikey_description": "Auto-generated for key xxxxxxxxxxxxxxxxxxx",
  "iam_apikey_name": "サービス資格情報-1",
  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/xxxxxxxxxxxxxxxxxxxx::serviceid:ServiceId-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "password": "パスワード",
  "port": 443,
  "url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "username": "ユーザー名"
}

この情報のうち、後で必要になるのが以下の(赤字の)4つです。メモするか、コピペできるようにしておいてください:
キー意味補足
hostnameホスト名*******-bluemix.cloudantnosqldb.appdomain.cloud といった感じの文字列になっています(******* 部分が可変)
portポート番号443 で固定のはずです
usernameユーザー名ログイン時にこの2つの値が必要です
passwordパスワード


認証情報を確認したら、実際に Cloudant データベースのダッシュボードを見てみましょう。画面左のメニューから「管理」タブを選択し、右上の「Launch Dashboard」ボタンをクリックします:
2021022505


(初期状態では空の)ダッシュボード画面が表示されます。最初にアクセスした時点ではまだ何のデータベースも作成されていないため、下図のような画面になります。後述の作業のためのデータベースをここで1つ作成しておくことにします。画面右上の「Create Database」ボタンをクリックします:
2021022506


データベースを作成するメニューが画面右に表示されます。ここでは Database name に「mydb(異なる名前を指定しても構いませんが、その場合は後述のコマンド等を作成した名前に読み替えて実行してください)」、Partitioning は「Non-partitioned」を選択し、「Create」ボタンをクリックします:
2021022507


作成した mydb データベースが出来上がりました。このデータベースもまだデータが1件もないので、このような画面になります。一旦元の画面に戻るため、画面左上の "<" マークをクリックします:
2021022508


一つ前のデータベース一覧画面に戻りました。先程は空でしたが、作成したデータベースが1つ追加された画面になっているはずです:
2021022509


Cloudant 側の準備はいったんここまでできていればOKとします。 次に CORS のための設定を行います。


【Cloudant に CORS を設定する】
次に Cloudant に CORS の設定をして外部からのアクセスを許可します。ダッシュボードのメニューから「アカウント」(下から3番め)を選び、「CORS」タブを選択します。以下のような画面(「CORS は有効になっていて、外部のどこからのリクエストも受け付けない」という内容)になるはずです:
2021022501


画面下部の「Restrict to specific domains」欄にアクセスを許可するドメインの URL を指定して「Add Domain」ボタンで追加します。ここでは "https://(github のアカウント名).github.io/" を指定することで自分の Github ページからのアクセスを許可することになるので、自分の github アカウントに合わせて設定してください:
2021022502


このような画面になれば自分の github ページ用の CORS 設定ができたことになり、自分の github ページからは JavaScript で Cloudant データを読み書きできることになります:
2021022503


【Cloudant に初期データを入力する】
今回の紹介では Github ページから Cloudant のデータを REST API 経由で表示する(CORS の設定をしていれば取得できるはず)、ということを試してみます。そのための「表示するデータ」をあらかじめ Cloudant に入力しておきます。

もちろん(ダッシュボードから)自分で好きにデータを入力いただいても構いませんが、比較的簡単に動作を確認できるサンプルを用意しており、そこで対象とするデータを curl 一発でまとめて入力できるように準備しているのでこちらの方法を紹介します。

まず以下の URL からサンプルデータをダウンロード(表示後に右クリック→名前を付けて保存で "prefs.json" という名前で保存)してください:
https://raw.githubusercontent.com/dotnsf/cloudant_cors/main/prefs.json

2021022701


このファイルの中身は以下のようなフォーマットになっており、各都道府県庁所在地と、その位置情報が格納されています:
{
  "docs": [
    { "code": 1, "prefecture": "北海道", "capital": "札幌市", "geometry": { "type": "Point", "coordinates": [ 141.34694, 43.06417 ] } },
    { "code": 2, "prefecture": "青森県", "capital": "青森市", "geometry": { "type": "Point", "coordinates": [ 140.74, 40.82444 ] } },
    { "code": 3, "prefecture": "岩手県", "capital": "盛岡市", "geometry": { "type": "Point", "coordinates": [ 141.1525, 39.70361 ] } },
    { "code": 4, "prefecture": "宮城県", "capital": "仙台市", "geometry": { "type": "Point", "coordinates": [ 140.87194, 38.26889 ] } },
    { "code": 5, "prefecture": "秋田県", "capital": "秋田市", "geometry": { "type": "Point", "coordinates": [ 140.1025, 39.71861 ] } },
    { "code": 6, "prefecture": "山形県", "capital": "山形市", "geometry": { "type": "Point", "coordinates": [ 140.36333, 38.24056 ] } },
    { "code": 7, "prefecture": "福島県", "capital": "福島市", "geometry": { "type": "Point", "coordinates": [ 140.46778, 37.75 ] } },
    { "code": 8, "prefecture": "茨城県", "capital": "水戸市", "geometry": { "type": "Point", "coordinates": [ 140.44667, 36.34139 ] } },
    { "code": 9, "prefecture": "栃木県", "capital": "宇都宮市", "geometry": { "type": "Point", "coordinates": [ 139.88361, 36.56583 ] } },
    { "code": 10, "prefecture": "群馬県", "capital": "前橋市", "geometry": { "type": "Point", "coordinates": [ 139.06083, 36.39111 ] } },
    { "code": 11, "prefecture": "埼玉県", "capital": "さいたま市", "geometry": { "type": "Point", "coordinates": [ 139.64889, 35.85694 ] } },
    { "code": 12, "prefecture": "千葉県", "capital": "千葉市", "geometry": { "type": "Point", "coordinates": [ 140.12333, 35.60472 ] } },
    { "code": 13, "prefecture": "東京都", "capital": "新宿区", "geometry": { "type": "Point", "coordinates": [ 139.69167, 35.68944 ] } },
    { "code": 14, "prefecture": "神奈川県", "capital": "横浜市", "geometry": { "type": "Point", "coordinates": [ 139.6425, 35.44778 ] } },
      :
      :
  ]
}

このデータを先程作成した Cloudant 内の mydb データベースに格納します。curl コマンドを使うと以下のコマンド(※)で格納できます:
$ curl -u "username:password" -XPOST "https://host/mydb/_bulk_docs" -H "Content-Type: application/json" -d @prefs.json

username, password, host の値は上述で確認した以下の値を指定して入力してください:
 username: 上述の資格情報で確認した username の値
 password: 上述の資格情報で確認した password の値
 host: 上述の資格情報で確認した host の値


このコマンドが成功すると Cloudant の mydb データベースに47都道府県のデータが挿入されます。ダッシュボードから mydb データベースを選択すると以下のように 47 件のデータが表示されるようになります:
2021022702


試しにどれか1つ選択してみると、個々の内容を確認することも可能です(下図は北海道の例、"Cancel" で一覧に戻ります):
2021022703


ここまでの作業で Cloudant に Github ページからアクセスするための CORS を設定し、表示用のデータも格納できました。ではこのデータを Github ページから REST API 経由で取得して表示してみましょう。


【Github ページを用意する】
では実際に Github ページからクロスオリジンな Cloudant データベースに REST API 経由でアクセスできることを確認してみます。

まず Github にログインし、その後で以下のページにアクセスし、画面右上の "Fork" をクリックしてください:
https://github.com/dotnsf/cloudant_cors

2021022706


Fork 処理が成功すると、
 https://github.com/(あなたの github アカウント名)/cloudant_cors
というリポジトリができるはずです。

このリポジトリの Github ページを有効にします。リポジトリページの右上にある "Settings" をクリックします:
2021022707


画面を下に "GitHub Pages" と書かれている箇所までスクロールします。"Source" に "main" ブランチを指定して保存すると、"main" ブランチの内容がそのまま Github ページとして公開され、この中にある CORS の動作確認用に作った index.html を参照するための URL(https://(あなたの github アカウント名).github.io/cloudant_cors/)とそのリンクが表示されます(リンク先が有効になるまで1分程度かかります):
2021022708


少し待ってからリンク先にアクセスします。以下のような画面が表示されるはずです(Github ページなので、****.github.io というドメインのページになっていることを再確認してください):
2021022704


改めて資格情報から取得した値を画面内の各該当箇所に入力します。上にある横幅の大きなフィールドには host の値、その下は左から作成した DB 名(mydb)、username、password の値を入力します。最後に "Refresh" ボタンを押すと入力された情報を使ってこのページから AJAX で Cloudant へ REST API を実行して文書一覧を取得し、表形式で出力します。成功すると以下のように47都道府県の情報が(クロスオリジンの制約を乗り越えて)表示されます:
2021022705


以上、正しく設定することでクロスオリジンの壁を超えて Github ページから Cloudant のデータを利用することができました。Github も Cloudant も(一定制限の中で)無料で利用できるので、安定したウェブページを Github で運用しつつ、表示データを Cloudant から提供する、といった形で利用することができそうです。


なお、Cloudant(CouchDB) の REST API についての詳しくは、こちらのリファレンスを参照してください:
https://docs.couchdb.org/en/stable/api/index.html


なお、今回使った「特定データベース内の全文書を取得する」 REST API はこちらです:
GET /{db}/_all_docs

また準備段階で都道府県データをバルクインサートしましたが、その REST API はこちらで紹介されています:
POST /{db}/_bulk_docs


このフローは以前に少し違う形で業務やイベントのネタ(の裏側)として使っていたことがあったのですが、埋もれてしまうのはもったいない気がしたので公開しちゃいます。

Node-REDFX (外国為替)情報を取得するフローを作りました。FX というのは例えば USDJPY だと USD と JPY 、つまり米ドルと日本円の関係です。「1ドル=107円23銭」みたいなやつですね。これの EURUSD (ユーロドル)やら EURJPY (ユーロ円)やら AUDJPY (豪ドル円)やら、、主に日本円が絡む通貨ペアを中心に 20 ペアの情報を1分おきにリアルタイムで取得するものです。

フローはこちらで公開しています:
https://flows.nodered.org/flow/9d045f691b6d7c5cb3259c197ad365d0

2020060105



このページ内のフロー定義を "Copy" して、Node-RED 環境に「クリップボードから読み込み」するだけでフローが再現できます。フロー1本だけの、それも標準ノードの組み合わせだけで構成されているシンプルな内容です。動く条件は「Node-RED 環境がインターネットに接続されていること」だけでいけると思います:
2020060101


↓ペースト後、こんなフローのタブが作られていれば成功:
2020060102


あとはこのまま「デプロイ」すれば1分おきに inject ノードが動き出し、取得した FX 情報を debug タブに出力し続けます:
2020060103


1回実行した時の debug タブの様子はこんな感じです。_id に実行時の日付時刻が入り、あとは通貨ペアとその瞬間の価格がまとめて出力されます:
2020060100



公開しているフローではこれだけ(debug タブに出力するだけ)ですが、IBM Cloud 内の Node-RED として動いている環境であれば、バインド済みの Cloudant out ノードをフローの最後に追加して、DB 名を指定するだけで出力される情報を1つのレコードとして DB に格納する所まで簡単に実現できます。他の環境でも各種データベースノードに渡すことで取得データの DB 格納ができます:
2020060104


中身は inject node が一分ごとに発火してオープンな API を使って FX 相場を取得し、(Cloudant DB に格納する前提での)JSON フォーマットに変換して debug ノードに渡す、というものです。FX は24時間相場が動くので、1日に 60 * 24 = 1440 データ集まります。(データ量に気をつけながら)1ヶ月程度動かしっぱなしにしておくとそこそこの為替情報データベースができあがります。シンプルですが API やフォーマットを変えることで応用範囲が広くなりそうだと思っています。

本来は集まったデータをグラフ表示したり、上下動の予測をしたり、、、といった使いみちになると思っています。サンプルではない実データを簡単に集めることができるので、説得力のあるデモアプリに応用しやすいと思っています。興味ある方は使ってみてください。


IBM Cloudant(Apache CouchDB) の MapReduce ビューを使って、特定フィールドの値ごとの文書数を返す API を作ってみました。

なお以下の内容は IBM Cloudant でも Apache CouchDB でも同様に有効だと思っていますが、スクリーンショットなどは IBM Cloudant のものを使って説明しています。ご了承ください。


まず、前提として現状 Cloudant DB 内に以下のような JSON 文書が複数格納されているとします:
{
  "_id": "(id値)",
  "name": "(名前)",
  "date": "(日付)"
}

"name" フィールドに名前、"date" フィールドに日付文字列が格納されます。同じ "name" の値でも "date" の値は異なっていたり、同じ "date" の値でも "name" は異なっていたりするとします:
2020021401


この DB の状態から
 名前(name)ごとにグルーピングして、文書数がいくつずつあるか?
を調べる、というのが今回やりたいことです。

例えば上記例の場合であれば、"name" = "K.Kimura" の文書数は 5 、"name" = "K.Hashimoto" の文書数は 3 、"name" = "M.Matsuoka" の文書数は 2 、といった結果を導き出すための方法です。SQL の使える RDB であれば count() 関数と group by 句を使えば簡単そうですが、NoSQL 型である Cloudant でいちいち全件検索してから "name" の値ごとにカウントして・・・という REST API を作らずに調べるにはどうすればいいでしょうか?

その答が本ブログエントリのテーマでもある MapReduce ビューを作って、Cloudant REST API でこのビューを呼び出すことで実現できます。以下、その手順を紹介します。


まず DB 内に MapReduce ビューを定義するデザイン文書を作成します。画面左のメニュー "Design Documents" の+部分をクリックし、"New Doc" を選択します:
2020021402


新規にデザイン文書を追加する編集画面になるので、以下の内容を入力して "Create Document" ボタンをクリックします:
2020021401

{
  "_id": "_design/myindex",
  "language": "query",
  "views": {
    "count_by_name": {
      "map": {
        "fields": {
          "name": "asc"
        },
        "partial_filter_selector": {}
      },
      "reduce": "_count",
      "options": {
        "def": {
          "fields": [
            "name"
          ]
        }
      }
    }
  }
}

JSON の中身を一応解説すると、"myindex" という名前のデザイン文書を作り、その中で "count_by_name" という名前のビューを定義しています。このビューではまず "name" の値ごとにソート(map)し、その結果を _count 関数でカウント(reduce)した結果を値として持つよう定義しています。

正しく操作できていると Design Documents の中に定義した文書が追加されているはずです。これで MapReduce ビューが定義できました。
2020021404


後は Cloudant REST API でこのビューを呼ぶだけで結果を得ることができます。IBM Cloudant のホストURL (https://xxxx.cloudant.com)に続けて、DB 名(mapreduce)、デザイン文書名(myindex)とビュー名(count_by_name)を指定し、以下の URL にウェブブラウザでアクセスします:
https://xxxx.cloudant.com/mapreduce/_design/myindex/_view/count_by_name?group=true


すると以下のような結果が得られ、期待通りの結果を参照することができました:
2020021405
{
  "rows": [
    { "key" : [ "K.Hashimoto" ], "value" : 3 },
    { "key" : [ "K.Kimura" ], "value" : 5 },
    { "key" : [ "M.Matsuoka" ], "value" : 2 }
  ]
}

これで「DB 内にどんな名前の文書が存在しているか」や「各名前ごとの文書数」を簡単に調べることができるようになりました。

後はこのような処理を行う必要があるぶんだけビューを追加で定義しておけば、それぞれのビューごとに(フィールドとその値ごとに)文書数を調べたり、特定フィールド値の合計値を求めることができるようになります。


このページのトップヘ