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

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

タグ:couchdb

CouchDBCloudant というデータベースに対してブラウザの JavaScript から(API サーバーなどを使わずに)直接アクセスできるような SDK を作ってみました。


CouchDB CRUD SDK について】
CouchDB は現在は Apache CouchDB としてオープンソースで開発されている JSON ドキュメント型の NoSQL データベースです。そして Cloudant はその CouchDB を IBM からのサポート付きで IBM Cloud から提供されているものです。要するに CouchDB と Cloudant は実体としては同じものです。ということもあって、以下では両方を指す言葉として "CouchDB" という表現をします(Cloudant を使う場合は "Cloudant" と読み替えてください):
2024090900


CouchDB には REST API が提供されています。したがって CORS に注意する必要はありますが、ウェブブラウザの JavaScript とこの REST API だけでデータベースやドキュメントの読み書き更新削除(=CRUD、Create Read Update Delete)といった処理そのものは可能です。ただ REST API を実行するのはパスやパラメータの指定で直感的に分かりにくい点があったり、検索方法や CouchDB の特徴ともいえる添付ファイル対応(バイナリデータ対応)の REST API は更に複雑だったりするので、私自身も使うたびに調べる必要に迫られたりと、少々使いにくい点があると感じていました。そういった使い勝手の点も含めて分かりやすい JavaScript での SDK を提供できないかと思いついて作ってみたものです。

作成した CouchDB CRUD SDK 自体は README.md 含めて GitHub で公開しています:
https://github.com/dotnsf/couchdb-crud-sdk


SDK の説明(英語)も GitHub に含まれる SDK.md で紹介していますが、せっかくなのでこのブログでは日本語で使い方を紹介します。


CouchDB CRUD SDK を使う前提条件】
前提条件というか、当然条件というか、使う対象となる CouchDB(Cloudant) サーバーが必要です。この CouchDB サーバーはインターネットからアクセスできる必要はありませんが、クライアントとなるウェブブラウザを使う PC がインターネットに接続していない場合は後述の CDN が使えないため、SDK の JavaScript ファイルをあらかじめダウンロードするなどしておく必要があります。

またここで用意する CouchDB サーバーはユーザー ID とパスワードで(ベーシック認証で)アクセスできるように構築されている必要があります。API キーを使う方法には未対応です。

自由に使える CouchDB サーバーを持っていない場合に備えて、以下では2つの方法で CouchDB サーバーを用意する方法を紹介します:

(1)docker のコンテナとして用意する
自分の PC に docker をインストールしてある場合であれば以下のコマンドを実行するだけで CouchDB サーバーを localhost:5984 で(ユーザーID=user、パスワード=pass で)動かすことができます:
$ docker run -d --name couchdb -p 5984:5984 -e COUCHDB_USER=user -e COUCHDB_PASSWORD=pass

(2)IBM Cloud のアカウントをお持ちであれば、lite plan という無料枠の範囲内で使える Cloudant サーバーを1アカウントにつき1つだけ使うことができます。ちなみに Cloudant の lite plan で使える容量は 1GB です(その他の条件は下図参照)。バイナリデータなどの膨大な容量のファイルを添付したりしなければ、そこそこ使えるサイズだと思っています:
2024090800


IBM Cloud 内の Cloudant サーバーを作成した場合のデータベースサーバーのホスト名、ユーザーID、パスワードなどの情報は「サービス資格情報」タブ内の下図の変数として確認することができます:
2024090802


なお、この方法で Cloudant サーバーを用意する場合は認証方法として "IAM and legacy credential" を選択してください(この "legacy credential" がベーシック認証です。"IAM" のみだとベーシック認証には非対応です)
2024090801


IBM Cloud のアカウントをこれから作る場合はこちらから登録してください(アカウント作成時にクレジットカードの登録が必要ですが、有料サービスを使わなければ課金対象にはなりません)。

もう1つの前提条件として外部の JavaScript から CouchDB サーバーを利用できるように CORS を正しく設定している必要があります。CouchDB の設定画面内で CORS タブから CORS が enalbed になっていることを確認してください。また CORS 対象のオリジンについては "All domains" を選択するか、"Restrict to specific domains" を選択した上で、この後動かすことになるウェブページのオリジン(URL の "スキーマ://ホスト:ポート" の部分)を指定してください。ここが正しく設定できていないと CORS の制約にかかって JavaScript SDK が正しく実行できなくなります:
2024090802


CouchDB CRUD SDK のロードと初期化】
実際にデータの読み書きをする前に SDK ライブラリをロードして初期化する必要があります。その流れを説明します:

まずブラウザ内に以下の1行を加えて、CDN の SDK ライブラリをロードします:
<script src="https://dotnsf.github.io/couchdb-crud-sdk/couchdb-crud-sdk.js"></script>

インターネットに接続していない環境で使う場合は上記 URL から couchdb-crud-sdk.js をあらかじめダウンロードして、HTML と同じフォルダにコピーするなどして(その場合は src="./couchdb-crud-sdk.js" と指定)対応してください。

次にユーザー ID、パスワード、CouchDB サーバーのベース URL を指定して CouchDB_CRUD_SDK クラスのインスタンスを初期化します:
var cdb = new CouchDB_CRUD_SDK( username, password, base_url );

これでインスタンス変数 cdb の初期化が完了しました。この変数を使うことで CouchDB サーバー内のデータを読み書きできます。


【CouchDB CRUD SDK を使ったデータベースの作成と削除と一覧取得】
上述の初期化まで完了していればインスタンス変数(cdb)のメソッドでリモートの CouchDB に対する各種処理が実行できます。例えば CouchDB のデータベースに関しては以下のようなメソッドが用意されています:
//. データベース一覧取得
var result0 = await cdb.readAllDbs();

//. データベース新規作成
var result1 = await cdb.createDb( 'newdb' );

//. データベース削除
var result2 = await cdb.deleteDb( 'newdb' );

後述のドキュメント向けメソッドについてもいえることですが、メソッドの実行結果は成功した場合は
{
  status: true,
  result: (実行した結果のオブジェクトや配列)
}

といった JSON が返されます。また失敗した場合は
{
  status: false,
  error: (失敗した原因の内容を示すオブジェクトや文字列)
}

という JSON になります。status の true/false で成功か失敗かを判断し、成功の場合は(必要であれば)result の内容を参照、失敗の場合は error の内容に失敗の理由が記載されているのでそれぞれ参照して処理することができます。 なお、cdb のメソッドは全て非同期(async)関数として定義されている点に注意してください。


【CouchDB CRUD SDK を使ったドキュメントの操作】
次にデータベース内のドキュメントに関しては以下のようなメソッドが用意されています:
//. データベース内のドキュメント一覧取得
var result3 = await cdb.readAllDocs( 'db' );

//. ドキュメント新規作成
var result4 = await cdb.createDoc( 'db', doc );

//. ドキュメント一件取得
var result5 = await cdb.readDoc( 'db', 'doc_id' );

//. ドキュメント更新
var result6 = await cdb.updateDoc( 'db', 'doc_id', doc );

//. ドキュメント削除
var result7 = await cdb.deleteDoc( 'db', 'doc_id' );

createDoc や updateDoc のパラメータで指定されている doc オブジェクトは
{
  _id: 'doc_id',
  name: "ジュース",
  price: 120
}

といった JSON オブジェクトです(_id の値はいわゆるドキュメント ID です。新規作成時であれば指定されていてもされていなくても構いません、指定がない場合は自動的に付与されます)。

CouchDB の特徴的な機能を使ったメソッドについても紹介しておきます。例えば以下のようなメソッドも用意されています:
//. 特定ドキュメントの全リビジョン取得
var result8 = await cdb.readAllRevisions( 'db', 'doc_id' );

//. 添付ファイルをドキュメントとして保存する
var result9 = await cdb.saveFile( 'db', 'doc_id', 'selector', 'filename' );

上は特定の ID ('doc_id')を持ったドキュメントの全リビジョンを取得するメソッドです。CouchDB はドキュメントの更新履歴が全て(自動的に)リビジョンとして記録されています。つまりドキュメントを新規作成し(リビジョン1)、一度更新して(リビジョン2)、更に更新(リビジョン3)した場合、最新データはリビジョン3ですが、過去のリビジョンも全て記録されていて、取り出すことができます。上の readAllRevisions() メソッドは特定ドキュメントの全リビジョンを一度の取り出すメソッドです。

下は添付ファイルを JSON データの一部として記録する場合のメソッドです。例えば
<input type="file" id="attachment_file"/>

のような HTLM 要素を使って添付ファイルを読み込もうとした場合であれば、'selector' は "#attachment_file" となります(jQuery などのセレクタだと思ってください)。'filename' は指定されていればそのファイル名で保存されます('filename' 指定がなかった場合は実際のファイル名で保存されます)。なお 'doc_id' が指定されている場合はその ID のドキュメントを更新し、'doc_id' が指定されていない場合は新規にドキュメントを作成します。 この関数を使って(バイナリデータなどの)添付ファイルもブラウザの JavaScript でリモートデータベースに格納/更新できるようになります。またこの添付ファイルの更新記録もリビジョンとして記録されます。

一応ここに挙げたメソッドで一通りの読み書き更新削除まではカバーしていると思いますが、その他全てのメソッドを(最新情報として)参照したい場合はこちらを参照ください(英語です):
https://github.com/dotnsf/couchdb-crud-sdk/blob/main/SDK.md


【CouchDB CRUD SDK のサンプル】
この CouchDB CRUD SDK を比較的汎用的に使ったサンプルアプリケーションを GitHub Pages に用意しておきました。docker コンテナであっても CouchDB/Cloudant 環境があれば試しにアクセスすることができます:
https://dotnsf.github.io/couchdb-crud-sdk/


上記 URL にアクセスすると以下のような画面が表示されます:
2024090901


上部に3つのテキストフィールドがあり、左からユーザーID、パスワード、CouchDB の URL (docker であれば http://localhost:5984 など)を入力し、最後に Login ボタンをクリックします:
2024090902


入力した内容が正しい場合は指定された CouchDB にアクセスし、画面左にデータベースの一覧が表示されます。またログインフィールドが画面から消えます(ログインフィールドが残っている場合はログインに失敗しています)。この画面から新しくデータベースを作成(Create DB)することもできます:
2024090903


データベースを1つ選択すると、そのデータベース内のドキュメント一覧が画面右側に表示されます。この部分からは選択したデータベースの削除(Delete DB)、ドキュメントの新規作成(Create Doc)や、既存ドキュメントの参照(Show Doc)、変更(Edit Doc)、削除(Delete Doc)といったアクションを実行できます:
2024090904


以下は "Create Doc" ボタンをクリックしてドキュメントを新規作成している画面です:
2024090905


ドキュメントの内容は JSON 形式であればどのようなフォーマットでも指定可能です。最後に "Save" で保存します:
2024090906


(わかりにくいかもしれませんが)新たに1つドキュメントが追加されました(赤枠):
2024090907


このドキュメントの横にある "Show Doc" ボタンをクリックするとドキュメントの内容が表示されます。実際に入力した JSON の内容に加えて、"_id" や "_rev" といったキーとその値が追加されているのがわかります。これらが(自動的に付与された)ドキュメント ID や、そのリビジョンです。同様にして編集や削除も可能です:
2024090908


このサンプルアプリでできることはこの程度ですが、ブラウザの JavaScript だけでリモートのデータベース内を読み書きできていることがわかると思います。もちろんデータベースを読み書きする REST API があれば(CORS の設定もできていれば)普通にできることではあるんですが、REST API を意識しなくても関数を呼び出すだけでできるようにしたのはまあまあ便利かなと自分でも思っています。API サーバーなしでも動くフロントエンドアプリケーションの SDK という位置付けであり、それが実現できているので現に GitHub Pages でこのサンプルが作れているわけです。

サンプルアプリの内容を見たい場合はこちらを参照ください(index.html, viewer.js, viewer.css の3つのファイルで作られているウェブアプリケーションです):
https://github.com/dotnsf/couchdb-crud-sdk/tree/main/docs


【最後に】
・・・というものを作って公開してみました。ローカルストレージではなくリモートデータベースをブラウザの JavaScript だけで(比較的簡単に)操作できることと、CouchDB の持つリビジョン管理機能や添付ファイル管理まで含めて操作できる点が特徴的なライブラリかな、と思っています。

CouchDB や Cloudant といったデータベースに馴染みのない人もいるかもしれませんが、この記事をきっかけに知ったり興味持ったりしてもらうことがあれば嬉しいです。


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 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 内にどんな名前の文書が存在しているか」や「各名前ごとの文書数」を簡単に調べることができるようになりました。

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


ラズベリーパイ(Raspbian OS)に NoSQL 型のデータベースである Apache CouchDB を導入する手順を紹介します:
2019011402


Apache CouchDB (以下、"CouchDB")はオープンソースで提供されている NoSQL データベースです。IBM Cloud の IBM Cloudant はこの CouchDB をベースに提供されているマネージド DBaaS です:
2019011401


なお、IBM Cloudant は分散データベース環境がはじめから提供されていますが、以下で紹介する例では CouchDB を1インスタンスで運用する前提での導入方法とさせていただきます。また導入する CouchDB のバージョンは 2019/01/14 時点での最新版である 2.3.0 を対象としています。

まずは準備作業としてリポジトリをアップデートしておきます:
$ sudo apt-get update -y

$ sudo apt-get dist-upgrade -y

続いて Erlang Solutions のリポジトリを追加し、再度アップデートします:
$ wget http://packages.erlang-solutions.com/debian/erlang_solutions.asc

$ sudo apt-key add erlang_solutions.asc

$ sudo apt-get update

追加したリポジトリを使って、ビルドに必要なライブラリ等を導入します:
$ sudo apt-get --no-install-recommends -y install build-essential pkg-config erlang libicu-dev libmozjs185-dev libcurl4-openssl-dev

またビルドしたバイナリを実行するユーザー(couchdb)と、そのホームディレクトリ(/home/couchdb/)をこの段階で作成しておきます:
$ sudo useradd -d /home/couchdb couchdb

$ sudo mkdir /home/couchdb

$ sudo chown couchdb:couchdb /home/couchdb

ではいよいよ CouchDB のビルドを行います。2.3.0 のソースコードをダウンロード&展開して、ビルドします:
$ cd

$ wget http://mirror.ibcp.fr/pub/apache/couchdb/source/2.3.0/apache-couchdb-2.3.0.tar.gz

$ tar -zxvf apache-couchdb-2.3.0.tar.gz

$ cd apache-couchdb-2.3.0/

$ ./configure

$ make release

ビルドが成功すると、完成したバイナリが ./rel/couchdb/ 以下に作成されています。このフォルダ以下を /home/couchdb/ 以下にコピーし、couchdb ユーザー向けに実行権限を変更します:
$ cd ./rel/couchdb/

$ sudo cp -Rp * /home/couchdb

$ sudo chown -R couchdb:couchdb /home/couchdb

これでビルドは完了です。不要であればソースコードやリポジトリのファイルは削除しても構いません(残しておいても構いません):
$ cd

$ rm -R apache-couchdb-2.3.0/

$ rm apache-couchdb-2.3.0.tar.gz

$ rm erlang_solutoins.asc

この段階で CouchDB を起動することは可能ですが、デフォルト設定のままだと CouchDB はローカルホストからのリクエストのみ受け付ける設定になっています。CouchDB を外部ホストから利用する場合や、管理者向けコンソールに外部ホストからアクセスする場合は外部アクセスを許可するよう、起動前に設定を変更しておく必要があります。

外部ホストからのアクセスを許可するには、/home/couchdb/etc/local.ini ファイルをテキストエディタで開き、
#bind_address = 127.0.0.1

となっている箇所を、以下のように書き換えて保存します:
bind_address = 0.0.0.0

そして couchdb ユーザーの権限で CouchDB を起動します:
$ sudo -i -u couchdb /home/couchdb/bin/couchdb

起動後、別のターミナルから以下の curl コマンドを実行して動作を確認します:
$ curl http://localhost:5984/

{"couchdb":"Welcome","version":"2.3.0","git_sha":"07ea0c7","uuid":"48b039b37b2b29236f33806d9a96c248","features":["pluggable-storage-engines","scheduler"],"vendor":{"name":"The Apache Software Foundation"}}


私自身はラズパイ3とラズパイゼロで動作を確認しましたが、ラズパイゼロでも CouchDB 程度であれば大した負荷にはならずに実行できました。




(参考)
https://www.hackster.io/mehealth-ch/installing-couchdb-on-raspbian-stretch-ccb2a7



(この記事は IBM Cloud アドベントカレンダー 2018 に参加しています。3日目の記事です)

IBM Cloudant (Apache CouchDB) にあまり詳しくない人が他のデータベースと同じ感覚でデータを扱っている時に、特に既存データを更新している時にふと気づくことがあります。例えば以下のような現象を目の当たりにした時、何が起こっているのか正しく理解できるでしょうか?


IBM Cloudant のダッシュボード画面にアクセスし、今回は "testdb" という名称のデータベースを IBM Cloudant 上に新規に作成しました。以下の手順はすべてこのデータベースを対象に行います(CouchDB でも同様の結果になります)。作成したばかりなのでまだドキュメント数はゼロです:
2018100201


testdb データベースを選択した画面です。普通はここで testdb 内のドキュメント一覧が表示されますが、まだ1つも存在していないので "No Documents Found" と表示されています。ここでドキュメントを新規に作成するため "Create Document" ボタンをクリックします:
2018100202


新規に JSON ドキュメントを作成する画面に切り替わります。Cloudant(CouchDB) のドキュメントは "_id" というユニーク ID を含める必要があります(API 経由で _id を含めずに作ると自動的に割り振られます)。自動的に設定された "_id" 以外に "name" というキーを作り、適当な値(下図では "kkimura")を設定して "Create Document" ボタンをクリックします(JSON ドキュメントなので "_id" キーの最後にカンマをつけることを忘れずに):
2018100203


先程のドキュメントが作成され、ドキュメント一覧に1つのドキュメントが表示されるようになりました:
2018100204


ちなみに、この段階でデータベース一覧に戻ると testdb データベースのドキュメント数もゼロから 1 に変わっていることが確認できます:
2018100205


またドキュメント一覧からこのドキュメントを選択するとドキュメントの確認/編集画面になります。"_rev" という先ほど指定しなかったキーと値が追加されていますが、こちらは後で説明します:
2018100206


ここまでは特別におかしな所はないと思います。この文書を編集するあたりから Cloudant 特有のクセというか、「あれ?」と感じる所が出てくるようになってきます。

この画面から JSON ドキュメントを編集してみます。試しに "name" の値を(下図では "Kei Kimura" に)変更し、"Save Changes" ボタンをクリックします:
2018100207


変更内容が保存されて、ドキュメント一覧に戻ります。既存文書を編集して保存したので文書数は変わらずに1つのままです。ではこの文書を選択して開いてみます:
2018100208


"name" の値が "Kei Kimura" になった文書が開きました。が、よく見ると "_rev" の値が先程と異なっています。最初に作った直後は "1-" で始まる値だったのが、 "2-" で始まる値になっています。ここは変更しなかったはずなんですが・・・:
2018100209


また、このタイミングでデータベース一覧の画面に戻ると、testdb の文書数は1のままなんですが、データベースサイズが微妙に増えています。これほどの差がでるような変更をしたつもりはないのですが・・・:
2018100210


更にこの文書を開いて、再度 "name" 値を "kkimura" に変更して(元に戻して)みます。値を変更して "Save Changes" ボタンをクリックします:
2018100211


すると(中を開いて確認してもいいのですが)また "_rev" の値が変わっていることが一覧からもわかります。今度は "3-" で始まる値になっていました:
2018100212


この辺りから「???」と感じることが増えてきました。では最後にこの文書を削除してみます。一覧からチェックをつけてゴミ箱ボタンをクリックします:
2018100213


削除すると一覧からは文書は消えて、元通りの "No Documents Found" が表示されます:
2018100214


しかしデータベース一覧に戻って testdb を見ると、文書数は "0" ですが、横に!マークが付いています。また文書を削除した割にはデータベースサイズがあまり減っていないように見えます:
2018100215


この!マーク部分にマウスカーソルをあわせると、"This database has just 0 docs and 1 deleted docs" と表示されます。このメッセージの意味はいったい・・・:
2018100216


ドキュメントに勝手に "_rev"(と "_id")が付与されること、編集して保存すると "_rev" の値が勝手に変更されること、文書を削除してもデータベースサイズが減らないこと、文書を削除した時の謎のメッセージ、・・・ と、この辺りが Cloudant(CouchDB) を始めて使うと戸惑う点でしょうか? 前置きが長くなってしまいましたが、以下にこの謎を解くための説明を記載します。


上記の振る舞いを理解するには、まず自動付与される2つの値 "_id" と "_rev" の意味と役割を正しく理解する必要があります。

"_id" はいわゆる「文書 ID」です。この値はデータベース内でユニークな値をなっており、各文書を一意に取得することができるキー値となっています。正しい ID 値が与えられるだけで(他の絞り込み条件がなくても)データベース内から目的の文書を特定して取得することができます。ID 値については普通のデータベースでも扱うものなので、あまり難しくないと思っています。

一方、もうひとつの "_rev" 、こちらは IBM Cloudant(CouchDB) の特徴的な予約語となっており、「文書のリビジョン」を管理する値となっています。「リビジョン」は「バージョン」と読み替えていただいてもいいです。

上記の例だと、最初に "name" = "kkimura" という値で文書を作成しました。この時点ではこの文書のリビジョン(バージョン)は 1 で、"_rev" 値は "1-" で始まる値になっていました:
2018100204


次に同じ文書を "name" = "Kei Kimura" と変更して保存しました。この時点でこの文書のリビジョンは 2 となり、"_rev" 値も "2-" で始まる値に更新されました:
2018100208


更に同じ文書を "name" = "kkimura" に戻して保存しました。この時点でこの文書のリビジョンは 3 となり、"_rev" 値も "3-" で始まる値に更新されました:
2018100212


つまり "_rev" 値は "_id" 値で決まる文書のバージョンを管理する役割を持って自動的に更新されるシステム値ということになります。ただ Cloudant(CouchDB) でドキュメントが更新される際にはもう1つの特徴があります。

実は Cloudant(CouchDB) ではドキュメントが更新されることはほぼなく、「新しいドキュメントが新しい "_rev" 値を持って新規作成」されます。つまり厳密には同じ "_id" 値を持った複数のドキュメントがデータベース内には存在しているが、その中で最も大きな "_rev" 値を持ったドキュメントだけが有効になります。論理的にドキュメントを更新したつもりでいても、物理的には古いドキュメントは消えずに残っていて、新しいドキュメントが同じ "_id" 値&新しい "_rev" 値で作成されるのでした。なお最新でないリビジョンのドキュメントは _id 値を指定してドキュメントを取得する時に { revs_info: true } というオプションを指定することで取得することができます(このオプションをつけない限り、最新 _rev のものだけで取得できます):
http://docs.couchdb.org/en/stable/api/document/common.html


上記で Cloudant(CouchDB) のドキュメントが更新されることは「ほぼ」ないと書いたのですが、厳密にはあります。それが文書削除時です。Cloudant(CouchDB) の文書削除はいわゆる「ソフトデリート(論理削除)」であって、「ハードデリート(物理削除)」ではありません。文書に削除フラグ( { _deleted: true } )をつけて更新し、最新 "_rev" の文書が削除されているようにすることで、論理的に文書が削除されたことにしています。そしてこの論理削除を行う際には _id 値だけではなく、_rev 値と合わせて指定して、「この ID 値の、このリビジョンの文書を削除する」ことを明示的に指定する必要があります。論理的には _id 値だけで削除できそうな感覚を持ってしまいますが、その場合はまずその _id 値を持ったドキュメントの最新リビジョンを取得し、取得したドキュメントから _rev 値を取り出し、改めて _id 値と _rev 値を指定して論理削除する、という流れになります。


これらの部分を理解していると、文書を更新したり、削除した時にデータベースサイズが増える謎が理解できると思います。要は物理的に書き換えたり、物理的に削除しているわけではなく、新リビジョンのドキュメントを追加したり、削除フラグをつけたりしているだけなので、(別途物理削除するまでは)データベースサイズという観点では減ることがないのでした。








 

このページのトップヘ