LoginSignup
2
4

More than 5 years have passed since last update.

Cradle から Cloudant をアクセスするメモ

Last updated at Posted at 2017-05-14

ハイレベルなキャッシング CouchDB クライアント の Node.js ライブラリ Cradle を利用して、Cloudant をアクセスする方法の備忘録です。 Cloudant は CouchDB の DB as Service で、独自拡張した Cloudant API ライブラリもありますが、CouchDB のAPIライブラリも、同様に利用できます。

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 サービス資格情報をコピペするだけで作成できます。

cloudant_credentials.json参考例
{
  "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

以上です。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4