Node.jsのMongooseで、MongoDBにアクセス

MongoDBデータベースへNode.jsプログラムからアクセスする方法のまとめ。
ほとんどの人はNode.jsモジュールであるMongooseを使うようなので、基本的な使い方に関するメモ。

初期処理

var mongoose = require("mongoose");

DB接続

  • localhostのtestデータベースへ接続
    接続文字列のフォーマットは、mongodb://[server name]:[port]/[database name]
mongoose.connect('mongodb://localhost/test');
  • 認証付きの接続
    接続文字列のフォーマットは、mongodb://[username]:[password]@[server name]:[port]/[database name]

※ほかにもオプションを付けることもできるが、とりあえず省略

mongoose.connect('mongodb://user:password@localhost/test');

DB接続時のコールバック設定

mongoose.connection.on( 'connected', function(){
    console.log('connected.');
});
 
mongoose.connection.on( 'error', function(err){
    console.log( 'failed to connect a mongo db : ' + err );
});
 
// mongoose.disconnect() を実行すると、disconnected => close の順番でコールされる
mongoose.connection.on( 'disconnected', function(){
    console.log( 'disconnected.' );
});
 
mongoose.connection.on( 'close', function(){
    console.log( 'connection closed.' );
});

MongoDBデータの検索

RDBMSのSELECT文に相当する処理に関するメモ。すでに格納されているデータの取得してみる。あらかじめ以下のデータを追加しておく。
(データは、「MongoDBの薄い本」より)

データベースtest
コレクション名unicorns
スキーマ{ name : String, dob : Date, loves : [String], weight : Number, gender : String, vampires : Number }
db.unicorns.insert({name: 'Horny', dob: new Date(1992,2,13,7,47),
loves: ['carrot','papaya'], weight: 600,
gender: 'm', vampires: 63});
db.unicorns.insert({name: 'Aurora', dob: new Date(1991, 0, 24, 13, 0),
loves: ['carrot', 'grape'], weight: 450,
gender: 'f', vampires: 43});
db.unicorns.insert({name: 'Unicrom', dob: new Date(1973, 1, 9, 22, 10),
loves: ['energon', 'redbull'], weight: 984,
gender: 'm', vampires: 182});
db.unicorns.insert({name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44),
loves: ['apple'], weight: 575,
gender: 'm', vampires: 99});
db.unicorns.insert({name: 'Solnara', dob: new Date(1985, 6, 4, 2, 1),
loves:['apple', 'carrot', 'chocolate'], weight:550,
gender:'f', vampires:80});
db.unicorns.insert({name:'Ayna', dob: new Date(1998, 2, 7, 8, 30),
loves: ['strawberry', 'lemon'], weight: 733,
gender: 'f', vampires: 40});
db.unicorns.insert({name:'Kenny', dob: new Date(1997, 6, 1, 10, 42),
loves: ['grape', 'lemon'], weight: 690,
gender: 'm', vampires: 39});
db.unicorns.insert({name: 'Raleigh', dob: new Date(2005, 4, 3, 0, 57),
loves: ['apple', 'sugar'], weight: 421,
gender: 'm', vampires: 2});
db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53),
loves: ['apple', 'watermelon'], weight: 601,
gender: 'f', vampires: 33});
db.unicorns.insert({name: 'Pilot', dob: new Date(1997, 2, 1, 5, 3),
loves: ['apple', 'watermelon'], weight: 650,
gender: 'm', vampires: 54});
db.unicorns.insert({name: 'Nimue', dob: new Date(1999, 11, 20, 16, 15),
loves: ['grape', 'carrot'], weight: 540,
gender: 'f'});
db.unicorns.insert({name: 'Dunx', dob: new Date(1976, 6, 18, 18, 18),
loves: ['grape', 'watermelon'], weight: 704,
gender: 'm', vampires: 165});

Mongooseでは、データの検索や挿入、更新はModelクラスを使って操作する。ModelがMongoDBのコレクション内のドキュメントを取得したり、挿入・更新・削除などを行うためのオペレータに相当する。(Model=コレクション・・とも思ったが、Modelはスキーマを定義して生成するのに対し、コレクション自体はスキーマレスなのでちょっと違う)

このModelを作成するためには、ドキュメントのフィールド情報を定義するSchemaクラスが必要になる。

上記データベースから、女性の名前(name)と好物?(loves)を検索してみる。

// スキーマの定義
// 実際のドキュメントのフィールドを過不足なく定義する必要はない
// (もちろん存在しないフィールドを定義しても意味はない)
// mongooseを使って取得・設定したいフィールドを定義する
// ここで定義したフィールドがオブジェクトの変数となる
var unicornSchema = mongoose.Schema({
  name : String
  ,loves : [String]
});
 
// コレクション名とスキーマを入力として、モデルを取得する
var unicorns = mongoose.model('unicorns', unicornSchema);
 
// find()を使って検索する(検索条件gender = "f")
unicorns.find( { gender : "f" }, function( err, docs ){
 
    // 検索結果がdocsとして入力されるので、ループで取り出す
    docs.forEach( function( element ){
         
        // Schemaで定義した変数のアクセス方法は、element.<フィールド名>
        // ※genderはSchema定義してないので、element.genderでは参照できない
        console.log( element.name + " " + element.loves.join(",")  );
         
        // Schemaで定義していない変数への暗黙のアクセス方法は、element._doc.<フィールド名>
        // ※element._docには取得したドキュメントの全情報が格納されている。
        // Schemaで定義してなくても、_doc.<フィールド名>で、ドキュメントの中身を知っていれば
        // アクセスできる。
        console.log( element._doc.name + " " + element._doc.gender + " "+
                    element._doc.loves.join(","));
 
    },this);
});

注意!コレクション名が勝手に複数形にされる

Mongooseのデフォルトの仕様だと、Model作成時に指定するコレクション名勝手に複数形にされる。例えば、Mongooseのsaveメソッドでコレクションが自動生成されると、コレクション名が複数形になって作成される。

var sch = mongoose.Schema({name:String});
var Car = mongoose.model('car', sch);  // 'car'ではなく、内部では'cars'になっている
var car_model = new Car({name:"test"});
car_model.save(function (err){ // MongoDB内には'cars'でコレクションが作成される
  if(err)
  {
    return console.error(err);
  }
  console.log("ok");
});

上記のcar_modelを使ってcar_model.find()した場合は問題ないが、外部でMongoDBに直接コレクション名’car’でコレクションを作成し、Mongooseでデータをfindするとデータが取得できない。

var sch = mongoose.Schema({name:String});
var Car = mongoose.model('car', sch);
// Car.findはコレクション名'cars'で検索する
Car.find( {}, function(err, docs) {
  console.log(docs.length); // 件数0件になる
});

この余計なお世話機能を無視するためには、素直に複数形の名称でコレクションを作成するか、スキーマ作成時にオプションcollectionで名称を指定する。

var sch = mongoose.Schema({name:String},{collection:"car"});

接続解除

mongoose.disconnect();