Mongoose 是一款优雅的基于node.js的monodb对象模型库。
它帮助开发者摆脱了MongoDB繁琐的collection、document操作。提高了开发效率和灵活性。

Schemas

Schema是Mongoose所有行为的基础,它定义了一个模型的骨架,它不能直接操作数据库。
Mongoose中schemas对应MongoDB中的cellection。schemas中定义的对象和字段类型就是MongoDB中的document形式。

创建Schemas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{body: String, date: Date}],
date: {type: Date, default: Date.now},
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
})

上面例子定义了一个 blogSchema模型骨架。定义Schema模型骨架时,每个字段需声明类型, Schema 支持的类型有: String、Number、Date、Buffer、Boolean、Mixed、ObjectId、Array。
通过Schema#add方法你可以向Schema追加字段。

1
2
var ToySchema = new Schema;
ToySchema.add({name: 'string', color: 'string', price:'number'})

注意: schema.add 方法应该在 .model()之前进行!

创建 model

定义Schema是为了创建model。model是可以Schema生产的模型,它是可以直接操作数据库。

var Blog = mongoose.model('Blog',blogSchema);

定义实例方法(Instance methods)

Schema上可以定义实例方法被后续的Model使用。

1
2
3
4
5
6
7
8
9
10
11
12
	
var animalSchema = new Shema({name: String, type: String});

animalSchema.methods.findSimilarTypes = function (cb) {
return this.model('Animal').find({type: this.type},cb);
}

var Animal = mongoose.model('Animal', animalSchema);
var dog = new Animal({type: 'dog'});
dog.findSimilarTypes(function (err,dogs) {
console.log(dogs);
});

注意在回掉函数中不要使用ES6的箭头函数写法,会改变 ‘this’指向。
实例方法并不常用,因为它不能被model直接使用。静态方法可以被model拿来直接调用。

静态方法(Statics)

1
2
3
4
5
6
7
8
var animalSchema = new Shema({name: String, type: String});
animalSchema.statics.findByName = function (name, cb) {
return this.find({name: RegExp(name,'i')},cb);
}
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
console.log(animals);
})

虚拟字段(Virtuals)

在开发过程中,前端需要的字段往往是数据库中两个字段的结合,如果我们单独创建一个字段保存又有些浪费。Mongoss提供了虚拟字段解决这个问题。
虚拟字段不是MongoDB中真实的存在的字段,而只是Mongooss存创建的字段,方便我们做字段的合并和处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var personSchema = new Schema({
name : {
first: String,
last: String
}
});
personSchema.virtual('fullName').get(function () {
return this.name.first+' '+this.name.last;
}).
set(function (v) {
this.name.first = v.substr(0,v.indexOf(' '));
this.name.last = v.substr(v.indexOf(' ')+1);
})

var Person = mongoose.model('Person', personSchema);
var axl = new Person({
name :{
first: 'Axl',
last: 'Rose'
}
});
console.info(axl.fullName); // Axl Rose
axl.fullName = 'William Rose'; // axl.name.first === 'William' alx.name.last === 'Rose'

ES6 Classes

Mongoose 提供loadClass 方法让开发中用ES6 Classes方式创建 schemas。
Classes中的methods对应Schemas中的methods,statics对应statics,getters/setters对应virtuals.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cosnt schema = new Schema({firstName: String, lastName: String});

class PersonClass {
// `fullName` becomes a virtual
get fullName () {
return `${this.firstName} ${this.lastName}`
}
set fullName (v) {
const firstSpace = v.indexOf(' ');
this.firstName = v.split(' ')[0];
this.lastName = firstSpace === -1 ?'':v.substr(firstSpace+1);
}

// `getFullName()` becomes a document method
getFullName () {
return `${this.firstName} ${this.lastName}`
}

// `findeByFullName()` becomes a static
static findByFullName (name) {
cosnt firstSpace = name.indexOf(' ');
const firstName = name.split(' ')[0];
const lastName = firstSpace === -1?'':name.substr(firstSpace+1);
return this.findeOne({firstName,lastName});
}
};

schema.loadClass(PersonClass);

创建id

每个表(集合)一般都会定义一个’id’字段,以便追踪每天数据。MongoDB会每条数据自动生产一个’_id’字段,id字段就可以使用这个字段:

1
2
3
4
5
6
7
8
9
let Schema = mongoose.Schema;
var newPlatfSchema = new Schema({
name: {type:String,unique: true, index: true},
codeName: {type:String,unique: true, index: true},
randomWord: {type:String,unique: true, index: true},
fields: [String],
createTime: {type: Date, default: Date.now},
id:{type:Schema.Types.ObjectId,auto:true}
});

Models

Models 是Schema抽象构造函数。它的实例是documents;

1
2
var schema = new mongoose.Schema({name:String,size:String});
var Tank = mongoose.model('Tank',schema);

第一个参数是Model单数形式的名字,它对应MongoDB中的collections名字。Mongoose会将Model小写复数化为MongoDB的collections名字。

实例话 documents

Documents 是 model的实例化。可以使用它对数据库进行操作。

1
2
3
4
5
6
7
8
9
10
11
var Tank = mongoose.model('Tank',Schema);

var small = new Tank({size: 'small'}); // document
small.save(function (err) {
if(err) return handleError(err);
})

// or
Tank.create({size:'big'},function (err, small) {
if (err) return handleError(err);
})

如果是document使用save保存数据,如果是model则使用create
model会使用之前创建好的mongoose链接。如果你希望使用另外的链接,可以connections's model()方法代替:

1
2
var connection = mongoose.createConnection('mongodb://localhost:27017/test');
var Tank = connection.model('Tank', yourSchema);

Documents

Mongoose documents 对应 MongoDB documents。

更新

1
2
3
4
5
6
7
8
9
Tank.findeById(id, function (err, tank) {
if(err) return handleError(err);

tank.size='big';
tank.save(function (err, updatedTank) {
if(err) return handleError(err);
res.send(updatedTank);
})
})

上面例子,首先找到目标tank,然后更改它的大小,最后保存更改并返回前端。如果我们不想获得document则可以使用下面方式:

1
Tank.update({_id:id},{$set:{size:'big'}}, callback);

如果需要获得更新后的值,可以使用findByIdAndUpdate

1
2
3
4
Tank.findByIdAndUpdate(id, {$set:{ size:'large'}},{new:true},function (err, tank) {
if(err) return handleError(err);
res.send(tank);
})

Sub Docs

documents 可以嵌套进另外的 documents。在Mongoose中,你可以将Schemas嵌套进另外的Schemas中。Mongoose中有两种表示subdocuments方式:数据型subdocuments,单个subdocuments。

1
2
3
4
5
6
var childSchema = new Schema({name: String});

var parentSchema = new Schema({
children: [childSchema],
child: childSchema
});

无法直接保存subdocuments,需调用它父documentssave(callback)
Subdocuments 拥有 savevalidate中间件。当你调用父级save()时,会触发所有subdocuments的中间件。

childSchema.pre('save', function (next) {
    if({% codeblock lang:javascript %}'invalid'== this.name) {
			return next(new Error('#sadpanda'))
		};
		next();
	})

	var parent = new Parent({children: [{name:'invalid}]});
	parent.save(function (err) {
		console.log(err.message)
	});
{% endcodeblock %}        

Subdocuments pre('save')pre('validate')中间件触发在 最高级documents的 pre('save')方法之前,但在最高级documentpre('validate')中间件之后。
查找sub-document
每个subdocument 都有默认拥有一个 _id字段,document 拥有id()方法来查找subdocument。

1
var doc = parent.children.id(_id);

连接Mongodb

使用Mongoose第一步就是链接到Mongodb。

1
2
3
4
5
Mongoose.connect('mongodb://userName:pwd@host:port/db',{
useMongoClient: true,
}, function (error) {
console.log('mongodb connect error: ',error);
});