Couchbase

NoSQL データベース。

環境構築

Docker で動かす方がおすすめ。

インストールして動かす

Windows 用インストーラ
https://www.couchbase.com/downloads

インストール後、PowerShellで以下のコマンドを実行すると起動、停止ができる。

# Couchbase Serverの起動
net start CouchbaseServer
# 停止
net stop CouchbaseServer

起動後、ブラウザからlocalhost:8091にアクセスすると WebUI が表示される。

Docker で動かす

以下のコマンドを実行

winpty docker run -it -d --name db -p 8091-8093:8091-8093 -p 11210:11210 couchbase

couchbaseイメージは最新の EnterPrise エディション(開発、テスト、検証は無料)となっているので、本番環境で無料版を使いたい場合は代わりに couchbase:community-4.0.0を使用する。
ref: https://hub.docker.com/_/couchbase

起動後、ブラウザからlocalhost:8091にアクセスすると WebUI が表示される。

削除する場合

docker rm db

N1QL

インデックス作成

https://docs.couchbase.com/server/6.0/getting-started/try-a-query.html
インデックスがないとSELECT name FROM usersみたいなN1QLは使えない。

# 名前なしインデックスを作成する
create primary index on users

# 名前なしインデックスを削除する
drop primary index on users

ref:
DROP PRIMARY INDEX

バケット内のドキュメント数を取得

SELECT COUNT(*) AS size FROM `users`

Rubyの例。

require 'couchbase'

c = Couchbase.connect('http://localhost:8091', :bucket => "users", :username => nil, :password => 'password')
size = c.query('SELECT COUNT(*) AS size FROM `users`')
p size[:rows].size

バケットの全ドキュメント取得

ドキュメントIDはmeta([buketName]).idで指定できる。

res = c.query('SELECT meta(users).id, name, age FROM users')

# IDの昇順でソートしてオブジェクトに変換
rows = res[:rows].sort {|a, b| a['id'] <=> b['id']}.map {|row| 
    {
        'id'=> row['id'],
        'name'=> row['name'],
        'age'=> row['age'],
    }
}

WHERE句でドキュメントIDを使った検索

res = c.query('SELECT meta(u).id, name, age FROM users u WHERE meta(u).id = "12345"')
p res

Couchbase CLI

docker で起動している場合はまずコンテナに入る。

docker exec -it db bash

クラスタ作成

https://docs.couchbase.com/server/6.0/cli/cbcli/couchbase-cli-cluster-init.html#host-formats
hostname -iは自分の IP アドレス。
--servicesqueryを設定しないとN1QLが使えない。

couchbase-cli cluster-init \
    --cluster=`hostname -i` \
    --cluster-username=admin \
    --cluster-password=password \
    --services data,index,query,fts,analytics

容量が足りない的なエラーが出たら--servicesdata,queryだけにするか、--cluster-ramsizeの設定を追加してみる。

バケット作成

--clusterに設定する IP アドレスは、WebUI の Servers から確認できる。
--bucket-ramsizeは MB 単位

couchbase-cli bucket-create \
    --cluster=`hostname -i`
    --username=admin \
    --password=password \
    --bucket=myBucket \
    --bucket-type=couchbase \
    --bucket-ramsize=100 \
    --enable-flush=0 # 1にするとSDKなどからflush(全ドキュメント削除)ができる

ユーザー作成

https://docs.couchbase.com/server/6.0/cli/cbcli/couchbase-cli-user-manage.html
couchbaseの5.0からはバケット名と同じ名前のユーザーがいないとSDKからアクセスできない。
GUIから確認するにはSecurity -> Users

couchbase-cli user-manage \
    --cluster=`hostname -i` \
    --username=Administrator \
    --password=password \
    --set \
    --roles=admin \
    --auth-domain=local \
    --rbac-username=users \
    --rbac-password=password

バケットアクセス用のユーザ作成

アプリからアクセスするとき用のユーザを作る。
--roles=bukcet_full_access[<バケット名>]オプションで指定したバケットへのアクセス権を指定する。

couchbase-cli user-manage \
    --cluster `hostname -i` \
    --username=Administrator \
    --password=password \
    --set \
     --roles=bucket_full_access[mybucket] \
     --auth-domain=local \
     --rbac-username=mybucket \
     --rbac-password=mybucketpassword

Ref:
https://docs.couchbase.com/server/current/cli/cbcli/couchbase-cli-user-manage.html

Json をインポート

json ファイルをインポートする場合、ファイル名が ID、ファイル中身が Value となる。

https://docs.couchbase.com/server/6.0/tools/cbimport-json.html

https://docs.couchbase.com/server/5.5/cli/cbdocloader-tool.html

cbdocloader \
    --cluster `hostname -i` \
    --username admin \
    --password password \
    --bucket myBucket \
    --bucket-quota 100 \
    [json or zip file]

ex) test.json

{
  "hoge": 123
}

cbimportコマンドでも似たようなことができる。

cbimport json \
    --cluster `hostname -i` \
    --username admin \
    --password password \
    --bucket permission \
    --dataset file://initdata.json \
    --format list \
    --generate-key %email%

CLIでN1QL

cbqを使う。
Running N1QL queries from a command line

# cbq開始
/opt/couchbase/bin/cbq -engine=http://localhost:8093 -c Administrator:password 

# クエリを直接実行
/opt/couchbase/bin/cbq -engine=http://localhost:8093 -c Administrator:password -script "create primary index on users"

# help
cbq -h

REST API

REST API 経由でいろいろ設定できる。
https://docs.couchbase.com/server/6.0/rest-api/rest-endpoints-all.html

バケットの情報を取得する例

# -uオプションはBASIC認証
curl -u [ユーザ名]:[パスワード] http://localhost:8091/pools/default/buckets/[バケット名]

クラスタ設定

https://docs.couchbase.com/server/6.0/rest-api/rest-node-set-username.html

curl -u [admin]:[password] -d username=[new_admin] \
-d password=[new_password] \
-d port=8091 \
http://[localhost]:8091/settings/web

バケット作成

エンドポイント

POST /pools/default/buckets

  • content-type
    application/x-www-form-urlencoded

  • 応答 application/json

  • 認証 BASIC 認証が必要

{
    "authType": "",
    "bucketType": "couchbase",
    "name": "b1",
    "ramQuotaMB": 100
}

Dockerfile

FROM couchbase

SDK

Java(Kotlin)

Bucketにデータを登録する例。

@Service
class CouchService {

    lateinit var bucket: Bucket

    init {
        val cluster =CouchbaseCluster.create()
        bucket = cluster.openBucket("user", "password")
    }

    fun insert() {
        val user = JsonObject.empty()
                .put("firstname", "Walter")
                .put("lastname", "White")
                .put("job", "chemistry teacher")
                .put("age", 50)
        val stored = bucket.upsert(JsonDocument.create("walter", user))
    }
}

バケット名と同じユーザを作成しておき、openBucket()でそのユーザ名、パスワードを入れる必要がある。

FAQ

java.lang.RuntimeException: java.util.concurrent.TimeoutException

アプリがリソース不足なせいで接続するまで時間がかかっている。

Nodejs

yarn add couchbase

Dockerで動かす場合はイメージによってyarn installでコケるので、node:8イメージを使う。
node:8.9.4-alpineだとダメでした。

Ruby

apk add gcc libcouchbase-dev
apk add libcouchbase
apk add make
gem install couchbase

yajlが入れれないエラーが出た場合(alpine)、makeできるようにするため以下コマンド実行 apk add --virtual build-dependencies build-base gcc wget git
https://github.com/gliderlabs/docker-alpine/issues/24#issuecomment-411117124

事前にバケットに対応するユーザを作っておく必要がある。

require 'couchbase'

# usernameは`nil`かバケット名と同じ文字列が入る
c = Couchbase.connect('http://localhost:8091', :bucket => "users", :username => nil, :password => 'password')

# ドキュメント取得
res = c.get('key')

# 全ドキュメント削除
c.query('drop primary index on users') # flushの前にインデックスを削除しないといけない
c.flush

# DELETE文ならインデックス削除しなくていい(むしろ必要
c.query('delete from users')

# ドキュメント追加(更新)
c.set('id', {:email => "hoge@example.com", :name => "hoge"})

# N1QL
c.query('create primary index on users') # selectの前にインデックスを追加しないといけない
res = c.query('SELECT meta(users).id, email, name FROM users')
p res[:rows].map {|row| 
    {
        'id'=> row['id'],
        'email'=> row['email'],
        'name'=> row['name'],
    }
}