Node.jsアプリからMongoDBを操作するためのライブラリであるmongooseについて、その使い方をまとめる。
試したバージョン
- Node.js: v0.10.40
- mongoose: 4.4.9
- MongoDB: 3.0.7
前提
- MongoDBが
localhost:27017
で動いている - Node.jsがインストールされている
- Mongooseがインストールされている
スキーマ
登録するドキュメントは日本酒データ。
日本酒の銘柄brand
、特定名称type
および飲んだ感想impressions
を複数登録できるようにする。
特定名称および飲んだ時の温度帯temperature
については別コレクションを参照するようにする。
日本酒
var Sake = new Schema({
brand: String,
type: { type: Number, ref: 'SakeType' },
impressions: [{
temperature: { type: Number, ref: 'Temperature' },
impression: String
}]
});
特定名称マスタ
var SakeType = new Schema({
_id: Number,
type: String
});
温度帯マスタ
var Temperature = new Schema({
_id: Number,
temperature: String
});
マスタデータのインポート
特定名称マスタと温度帯マスタを先に登録する。
特定名称は"純米酒"や"吟醸酒"など9種類。
飲んだ時の温度帯は"常温"や"熱燗"など10種類。
[
{ "_id": 1, "type": "普通酒" },
{ "_id": 2, "type": "本醸造酒" },
{ "_id": 3, "type": "特別本醸造酒" },
{ "_id": 4, "type": "吟醸酒" },
{ "_id": 5, "type": "大吟醸酒" },
{ "_id": 6, "type": "純米酒" },
{ "_id": 7, "type": "特別純米酒" },
{ "_id": 8, "type": "純米吟醸酒" },
{ "_id": 9, "type": "純米大吟醸酒" }
]
[
{ "_id": 1, "temperature": "飛び切り燗(55℃以上)" },
{ "_id": 2, "temperature": "熱燗(50℃前後)" },
{ "_id": 3, "temperature": "上燗(45℃前後)" },
{ "_id": 4, "temperature": "ぬる燗(40℃前後)" },
{ "_id": 5, "temperature": "人肌燗(35℃前後)" },
{ "_id": 6, "temperature": "日向燗(30℃前後)" },
{ "_id": 7, "temperature": "常温" },
{ "_id": 8, "temperature": "涼冷え(15℃前後)" },
{ "_id": 9, "temperature": "花冷え(10℃前後)" },
{ "_id": 10, "temperature": "雪冷え(5℃前後)" }
]
MongoDBがlocalhost:27017
で待ち受けていると仮定し、以下のコマンドでインポートする。
mongoimport -h localhost:27017 --db sake --collection temperatures --drop --jsonArray --file temperatures.json
mongoimport -h localhost:27017 --db sake --collection saketypes --drop --jsonArray --file sake-types.json
MongoDBに接続し、スキーマからモデルをコンパイルする
以下の内容でmodel.js
を作成する。
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// スキーマ
var SakeType = new Schema({
_id: Number,
type: String
});
var Temperature = new Schema({
_id: Number,
temperature: String
});
var Sake = new Schema({
brand: String,
type: { type: Number, ref: 'SakeType' },
impressions: [{
temperature: { type: Number, ref: 'Temperature' },
impression: String
}]
});
// MongoDBへの接続
mongoose.connect('mongodb://localhost/sake');
// スキーマからモデルをコンパイルし、モデルをエクスポートする
exports.Temperature = mongoose.model('Temperature', Temperature);
exports.SakeType = mongoose.model('SakeType', SakeType);
exports.Sake = mongoose.model('Sake', Sake);
ドキュメント操作まとめ
以降のコードはmodel.js
をインポートすることで実行可能。
var model = require('./model');
var Sake = model.Sake;
var SakeType = model.SakeType;
var Temperature = model.Temperature;
... // ここにコードを書く
ドキュメントの保存
"醸し人九平次"の"純米大吟醸"を飲んだとしよう。あれはめちゃくちゃ美味かった。ここでは"雪冷え"と"常温"で飲んだことにする。
ドキュメントはモデルクラスをnewして作成し、save()
メソッドで保存する。
// ドキュメントの作成
var kuheiji = new Sake({
brand: '醸し人九平次',
type: 9,
impressions: [
{ temperature: 7, impression: 'めちゃうま' },
{ temperature: 10, impression: '激うま' }
]
});
// ドキュメントの保存
kuheiji.save(function(err) {
if (err) throw err;
});
最近飲んだ"上喜元"も美味かった。これも登録しておこう。
var jokigen = new Sake({
brand: '上喜元',
type: 9,
impressions: [
{ temperature: 7, impression: 'フルーティ' },
{ temperature: 9, impression: 'フレッシュ' }
]
});
jokigen.save(function(err) {
if (err) throw err;
});
ドキュメントの検索
まずは基本のfind()
。第1引数にMongoDBのセレクタを指定し、第2引数にコールバック関数を指定する。
コールバック関数には引数が2つ渡され、クエリ実行時にエラーが起きた場合はerr
にエラーオブジェクトが格納され、result
はnullになる。クエリが成功した場合はerr
がnullになり、result
にクエリの実行結果が格納される。
Sake.find({ brand: '醸し人九平次' }, function(err, result) {
if (err) throw err;
console.log(result[0].brand); // '醸し人九平次'が出力される
console.log(result[1].brand); // '上喜元'が出力される
});
次はfindOne()
。こちらはヒットしたドキュメントのうちの最初のドキュメントを返してくれる。セレクタには特定名称"純米大吟醸酒"を指定してみる。
Sake.findOne({ type: 9 }, function(err, result) {
if (err) throw err;
console.log(result.brand); // 登録順の関係でこちらも'醸し人九平次'が出力される
console.log(result.type); // '9'が出力される
});
他にも_idフィールドで検索するfindById()
や検索と同時に更新を行うfindOneAndUpdate()
などがある。
Population
MongoDBにはJOINがないが、Populationという機能を使うと他のコレクションのドキュメントを参照することができる。
先ほどの検索の例では、type
は数値として取得していた。スキーマを確認するとtype
はSakeType
スキーマを参照している。ここではPopulationを行うことで特定名称マスタと温度帯マスタを参照し、"純米大吟醸酒"や"常温"などの具体的な情報を取得する方法を紹介する。
Sake.find({ brand: '醸し人九平次' })
.populate('type impressions.temperature')
.exec(function(err, result) {
if (err) throw err;
console.log(result[0].type.type); // '純米大吟醸酒'が出力される
console.log(result[0].impressions[0].temperature.temperature); // '常温'が出力される
});
ドキュメントの更新
update()
を使ってドキュメントの更新を行う。"醸し人九平次"の銘柄名を"獺祭"に変更してみよう。
Sake.update(
{ brand: '醸し人九平次' },
{ $set: { brand: '獺祭' } },
function(err) {
if (err) throw err;
}
);
第1引数にはセレクタ、第2引数にはMongoDBのシェルと同じ表記で更新するフィールドの指定をすることができる。update()
は更新したオブジェクトの取得は行わないため、最後のコールバック関数にはエラーオブジェクトのみが渡される。ドキュメントの取得もしたければfindOneAndUpdate()
などを使う。
今度は"上喜元"に感想を追加してみる。
Sake.update(
{ brand: '上喜元' },
{ $push: { impressions: { temperature: 1, impression: 'ツンてする' } } },
function(err) {
if (err) throw err;
}
);
ドキュメントの削除
最後にドキュメントの削除方法について。
Sake.remove({ brand: '獺祭' }, function(err) {
if (err) throw err;
});
他にもfindOneAndRemove()
などがある。