ハイレベルなキャッシング CouchDB クライアント の Node.js ライブラリ Cradle を利用して、Cloudant をアクセスする方法の備忘録です。 Cloudant は CouchDB の DB as Service で、独自拡張した Cloudant API ライブラリもありますが、CouchDB のAPIライブラリも、同様に利用できます。
Cradleの情報源
日本語の情報が殆ど無いマイナーな感じもするけど、そんな事は気にしないで良いものは使いたいですよね。
- npm cradle https://www.npmjs.com/package/cradle
- GitHub https://github.com/flatiron/cradle
Bluemix の Cloudant への接続
下記のコードで、Bluemix のCloudant へ接続できました。
var cradle = require("cradle");
var cred = require('./cloudant_credentials.json');
var dbn = "test01";
var options = {
cache : true,
raw : false,
secure : true,
auth : {
username : cred.credentials.username,
password : cred.credentials.password
}
};
// Cloudant接続
var db = new cradle.Connection(
cred.credentials.host,
cred.credentials.port,
options
).database(dbn);
cloudant_credentials.jsonは、Bluemix の Cloudant サービス資格情報をコピペするだけで作成できます。
{
"credentials": {
"username": "********-****-****-****-************-bluemix",
"password": "****************************************************************",
"host": "********-****-****-****-************-bluemix.cloudant.com",
"port": 443,
"url": "https://********-****-****-****-************-bluemix:*************************************************\
***************@********-****-****-****-************-bluemix.cloudant.com"
}
}
もちろん、npm install は必要です。
$ node --version
v4.4.7
$ npm install cradle
データベースの作成
DBが存在しなければ、作成する定番パターン
// DBが存在しなければ作成
db.exists(function (err, exists) {
if (err) {
console.log('error', err);
} else if (exists) {
console.log('exist database: ', dbn);
} else {
console.log('create database:', dbn);
db.create(function(err) {
if (err) {
console.log("err = ", err);
} else {
console.log("created");
}
});
}
});
データベースの削除
削除した後に確認するものです。
// データベース削除
db.destroy(function(err) {
db.exists(function (err, exists) {
if (err) {
console.log('error', err);
} else if (exists) {
console.log('exist database: ', dbn);
} else {
console.log('database does not exists.');
}
});
});
データの保管
キーを指定しての保存
キーを指定して取り出す場合などで、有用なパターンです。 このサンプルコードでは、JSON形式のデータをキー yumi で登録します。
doc = {
name: '由美',
address: '東京都江東区',
age: 25,
email: 'yumi@sample.co.jp'
}
// データベース保存
db.save('yumi', doc, function (err, res) {
if (err) {
console.log("err = ", err);
} else {
console.log("res = ", res);
}
});
キー無しで登録するパターン
IoT機器からのデータをロギングするなど、時系列に発生するデータを登録する目的に適していると思います。
doc = {
name: 'ゴルゴ斎藤',
address: '東京都江東区',
age: 43,
email: 'gsaito@sample.co.jp'
}
// データベース保存
db.save(doc, function (err, res) {
if (err) {
console.log("err = ", err);
} else {
console.log("res = ", res);
}
});
一度に登録するパターン キー指定有りと無し
JSONデータの配列に、_id の項目を付与する事で、キー指定ありで、一括で登録できます。また、_id を削除すれば、キー指定無しで、登録できます。
docs = [
{
_id: 'X-FILE-001',
name: 'ゴルゴ斎藤',
age: 48,
occupation: 'スナイパー',
address: '東京都江東区'
},
{
_id: 'X-FILE-002',
name: '次元大介',
age: 32,
occupation: 'ガンマン',
address: '東京都港区'
},
{
_id: 'X-FILE-003',
name: 'スパイク・スピーゲル',
age: 27,
occupation: '賞金稼ぎ',
address: '東京都大田区'
},
{
_id: 'X-FILE-004',
name: 'サイトー',
age: 34,
occupation: 'スナイパー',
address: '東京都新宿区'
}
];
// DBへ一度に保存
db.save(docs, function (err, res) {
console.log("err = ", err);
console.log("res = ", res);
});
読み出しパターン
キーを指定して一件だけ取り出すパターン
// データ一件取り出し
db.get('yumi', function (err, doc) {
if (err) {
console.log("err = ", err);
} else {
console.log("doc = ", doc);
}
});
上記コードの実行結果です。 キー 'yumi' で一つのデータを取り出します。
$ ./r08_cradle_get.js
doc = Response {
_id: 'yumi',
_rev: '9-72e9f33952de62788f3bd99cf607c356',
name: '由美',
address: '東京都江東区',
age: 25,
email: 'yumi@sample.co.jp' }
全件取り出しパターン
格納されている全データを取り出します。 db.all だけでは、id,rev のヘッダー部分のみになりますから、id (key)を指定して、内容を取得する必要があります。
// 全データの取得
db.all(function (err, res) {
console.log("err = ", err);
console.log("res = ", res);
res.rows.forEach(function (row) {
console.log(row.key);
db.get(row.key, function(err,doc) {
console.log("name = ", doc.name);
});
});
});
複数のキー指定で、複数のデータを取り出すパターン
キーを指定した分だけ、配列で結果が返ってきます。
keys = ['X-FILE-001','X-FILE-002','X-FILE-003'];
db.get(keys, function (err, docs) {
console.log("err = ", err);
console.log("docs = ", docs);
});
以下は、上記のコードの実行結果です。
err = null
docs = Response [
{ id: 'X-FILE-001',
key: 'X-FILE-001',
value: { rev: '9-2684c79638a30d75ce021aa2ef9a745c' },
doc:
{ _id: 'X-FILE-001',
_rev: '9-2684c79638a30d75ce021aa2ef9a745c',
name: 'ゴルゴ斎藤',
age: 48,
occupation: 'スナイパー',
address: '東京都江東区' } },
{ id: 'X-FILE-002',
key: 'X-FILE-002',
value: { rev: '9-1f30b948bc8f585d129573ffd99e1f62' },
doc:
{ _id: 'X-FILE-002',
_rev: '9-1f30b948bc8f585d129573ffd99e1f62',
name: '次元大介',
age: 32,
occupation: 'ガンマン',
address: '東京都港区' } },
{ id: 'X-FILE-003',
key: 'X-FILE-003',
value: { rev: '9-1eb62e0bf2d0268ba5d7a83ff23bbe35' },
doc:
{ _id: 'X-FILE-003',
_rev: '9-1eb62e0bf2d0268ba5d7a83ff23bbe35',
name: 'スパイク・スピーゲル',
age: 27,
occupation: '賞金稼ぎ',
address: '東京都大田区' } } ]
キー指定のアップデート
id を指定して save する事で、UPDATE になり、revを指定する必要はない。
// 更新 idとrev の前にアンダースコアは無い
db.get(res.id, function (err, doc) {
doc.age = doc.age + 1
// id のみで更新可能 _id への変換は cradleの中で実施
db.save(res.id, doc, function (err, res) {
console.log("err = ", err);
console.log("update = ", res);
});
});
削除
一件削除
キーを指定して削除します。
db.remove('yumi', function (err, doc) {
if (err) {
console.log("err = ", err);
} else {
console.log("doc = ", doc);
}
});
複数キー指定の一括削除 (失敗)
下記の様に削除したいキーを配列で渡して、一括削除はできませんでした。
keys = ['X-FILE-001','X-FILE-002','X-FILE-003'];
db.remove(keys, function (err, docs) {
console.log("err = ", err);
console.log("docs = ", docs);
});
実行結果では、_revが無いというエラーが返ってきます。
~~~
err = [Error: No _rev found for X-FILE-001,X-FILE-002,X-FILE-003]
docs = undefined
~~~
レビジョン数の指定と設定取得
レビジョン数の設定(失敗)
Cloudant では、以下のメソッドが提供されていない様です。
db.maxRevisions(1, function(err, res) {
if (err) {
console.log('error ', err);
} else {
console.log('res ', res);
}
});
実行結果はエラーでした。
db.maxRevisions(1, function(err, res) {
^
TypeError: db.maxRevisions is not a function
at Object.<anonymous> (/home/tkr/bluemix-cloudant-coding-patterns/r15_cradle_set_revisions_limit.js:31:4)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Function.Module.runMain (module.js:441:10)
at startup (node.js:139:18)
at node.js:968:3
レビジョン数の設定取得(失敗)
Cloudant では、以下のメソッドが提供されていない様です。
db.maxRevisions(function(err, limit) {
if (err) {
console.log('error', err);
} else {
console.log('Revisions limit is: '+limit);
}
});
実行結果です。
db.maxRevisions(function(err, limit) {
^
TypeError: db.maxRevisions is not a function
at Object.<anonymous> (/home/tkr/bluemix-cloudant-coding-patterns/r05_cradle_get_revisions_limit.js:31:4)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Function.Module.runMain (module.js:441:10)
at startup (node.js:139:18)
at node.js:968:3
ビュー
ビューの保存 (検索条件の保存)
検索(フィルター)条件を予め保存しておき、タグを指定する事で結果を得られます。 この条件を保存する方法です。
// デザインドキュメント
ddoc = {
all: {
map: function(doc) {
if (doc.name) emit(doc.name,doc);
}
},
sniper: {
map: function(doc) {
if (doc.name && doc.occupation == 'スナイパー') {
emit(null,doc)
}
}
}
};
// 保存
db.save('_design/person', ddoc, function(err, res) {
console.log("err = ", err);
console.log("res = ", res);
});
ビューを使った結果の取得
ALL指定
デザインドキュメントの名前の後に、ダグを付与してコールします。 次のコードは全件取得できます。
var view_db = 'person/all'
db.view(view_db, function (err, res) {
console.log("err = ", err);
res.forEach(function (row) {
console.log(row.name);
});
});
実行結果
err = null
ゴルゴ斎藤
サイトー
スパイク・スピーゲル
次元大介
特定条件
このサンプルは、doc.occupation == 'スナイパー' に一致するものをリストします。
var view_db = 'person/sniper'
db.view(view_db, function (err, res) {
console.log("err = ", err);
res.forEach(function (row) {
console.log(row.name);
});
});
実行結果
err = null
ゴルゴ斎藤
サイトー
テンポラリ・ビューの利用(失敗)
Cloudantでは、この機能が停止されていました。調べた処によると、テンポラリ・ビューは計算コストが高いため、本番環境には適用するべきではないとの記述がありました。 参照先 O'REILLY Nodeクックブック P118
db.temporaryView({
map: function (doc) {
if (doc.name && doc.age > 30) emit(doc.name, doc);
}
}, function (err, res) {
if (err) console.log(err);
console.log(res);
});
実行結果
{ [CouchError: forbidden: temp views are disabled on Cloudant]
name: 'CouchError',
message: 'forbidden: temp views are disabled on Cloudant',
error: 'forbidden',
reason: 'temp views are disabled on Cloudant',
headers:
{ 'cache-control': 'must-revalidate',
'content-length': '69',
'content-type': 'application/json',
date: 'Sun, 14 May 2017 11:59:28 GMT',
server: 'CouchDB/2.0.0 (Erlang OTP/17)',
'x-couch-request-id': '6b9df37fa7',
'x-couchdb-body-time': '0',
'x-content-type-options': 'nosniff',
'x-cloudant-backend': 'bm-cc-dal-02',
via: '1.1 lb1.bm-cc-dal-02 (Glum/1.34.0)',
'strict-transport-security': 'max-age=31536000',
status: 403 } }
undefined
以上です。