mongoose学习笔记

上手学习Mongoose,用来记录和梳理内容。

起步

名词

  • Schema: 一种以文件形式存储的数据库模型骨架,相当于表
  • Model: 由Schema生成的模型,具有抽象属性和行为的数据库操作对
  • Entity: 由Model创建的实体

开工

1.连接数据库

1
2
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'test');

2.回调

1
2
3
db.on('error', console.error.bind(console, '连接错误:'));
//一次打开
db.once('open', function(){ ... })

3.HelloWorld

1.定义Schema

1
2
3
4
//创建Schema并定义其属性及类型
var messageSchema = new mongoose.Schema({
content: String
})

2.将Schema创建为Model

1
2
//定义Schema索引为‘Message’
var messageModel = db.model('Message', messageSchema);

3.创建Entity

1
2
3
var messageEntity = new messageModel({
content: 'Hello World!'
});

4.CRUD操作

1
messageEntity.save(); //插入数据库

5.使用Model进行查询

1
2
3
4
5
messageModel.find(function(err, persons){
//err承接错误
if(err){ console.error(err); return; };
...
})

Schema

纯洁的数据库原型,我理解为相当于定义一张表

基础

1.定义Schema

1
2
3
4
5
var personSchema = new mongoose.Schema({
name: String,
age: Number,
...
})

2.Type基本数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var ExampleSchema = new Schema({
name:String,
binary:Buffer,
living:Boolean,
updated:Date,
age:Number,
mixed:Schema.Types.Mixed, //该混合类型等同于nested
_id:Schema.Types.ObjectId, //主键
_fk:Schema.Types.ObjectId, //外键
array:[],
arrOfString:[String],
arrOfNumber:[Number],
arrOfDate:[Date],
arrOfBuffer:[Buffer],
arrOfBoolean:[Boolean],
arrOfMixed:[Schema.Types.Mixed],
arrOfObjectId:[Schema.Types.ObjectId]
nested:{
stuff:String,
}
});

3.Mixed

没有特定约束的混合类型,可以任意修改

一旦修改了原型,必须调用markModified()

1
2
3
4
5
var personSchema = new Schema({ any: {} }) //or Schema.Types.Mixed
person.any = [1,2,3];
person.markModified('any');
person.save();

4.Array

Array在JavaScript编程语言中并不是数组,而是集合,因此里面可以存入不同的值,以下代码等价:

1
2
3
4
var ExampleSchema1 = new Schema({array:[]});
var ExampleSchema2 = new Schema({array:Array});
var ExampleSchema3 = new Schema({array:[Schema.Types.Mixed]});
var ExampleSchema4 = new Schema({array:[{}]});

扩展

1.实例方法

提供共用方法,方便Entity使用。例:

1
2
3
4
5
var PersonSchema = new Schema({name:String,type:String});
//查询类似数据
PersonSchema.methods.findSimilarTypes = function(cb){
return this.model('Person').find({type:this.type},cb);
}

使用方法:

1
2
3
4
5
6
7
8
9
var PersonModel = db.model('Person', PersonSchema);
var perter = new PersonModel({
name: 'Peter',
type: '前端工程师'
});
perter.findSimilarTypes(function(err, persons){
//可查询到其他前端工程师
})

2.静态方法

提供Model层使用

1
2
3
4
5
6
PersonSchema.statics.findByName = function(name, cb){
this.find({ name: name }, cb);
};
//找到所有name为'Peter'的人
PersonModel.findByName('Peter', function(err, persons){...});

3.索引

更加高效,专门讲解

4.虚拟属性

设置虚拟属性,方便操作,该属性不会被写入数据库
如:

Schema中如果定义了虚拟属性,那么该属性将不写入数据库,例如:

1
2
3
4
5
6
7
8
9
10
var PersonSchema = new Schema({
name:{
first:String,
last:String
}
});
var PersonModel = mongoose.model('Person',PersonSchema);
var krouky = new PersonModel({
name:{first:'krouky',last:'han'}
});

如果每次想使用全名就得这样

1
console.log(krouky.name.first + ' ' + krouky.name.last);

显然这是很麻烦的,我们可以定义虚拟属性:

1
2
3
4
5
6
7
8
9
PersonSchema.virtual('name.full').get(function(){
return this.name.first + ' ' + this.name.last;
});
PersonSchema.virtual('name.full').set(function(name){
var split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});

那么就能用krouky.name.full来调用全名了,反之如果知道full,也可以反解first和last属性

1
2
3
4
var PersonModel = mongoose.model('Person',PersonSchema);
var krouky = new PersonModel({});
krouky.name.full = 'krouky han';//会被自动分解
console.log(krouky.name.first);//krouky

CRUD

1.增

1
2
3
4
5
6
//使用Model新增
PersonModel.create({name: 'Peter'}, cb);
//使用Entity新增
var perter = new PersonModel({name: 'Peter'});
perter.save(cb);

2.删

1
perter.remove();

3.改

1.使用entity修改

1
2
3
4
PersonModel.findById(id, function(err, person) {
person.name = 'Marry';
person.save(cb);
})

2.使用Model操作,需要将主键_id进行删除

1
2
3
4
5
6
7
8
PersonModel.findById(id, function(err, person){
person.name = 'Marry';
var _id = person._id;
delete person._id;
PersonModel.update({_id: _id}, person, cb);
})

3.$set

1
PersonModel.update({_id:_id},{$set:{name:'MDragon'}},function(err){});

4.findByIdAndUpdate
5.findByIdAndRemove

4.查

1.普通查询

1
2
3
PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){
});

2.链式查询

1
2
3
4
5
6
7
8
9
Person
.find({ occupation: /host/ })
.where('name.last').equals('Ghost')
.where('age').gt(17).lt(66)
.where('likes').in(['vaporizing', 'talking'])
.limit(10)
.sort('-occupation')
.select('name occupation')
.exec(callback);

验证器

  • required 非空验证
  • min/max 范围验证(边值验证)
  • enum/match 枚举验证/匹配验证
  • validate 自定义验证规则

以下是综合案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var PersonSchema = new Schema({
name:{
type:'String',
required:true //姓名非空
},
age:{
type:'Nunmer',
min:18, //年龄最小18
max:120 //年龄最大120
},
city:{
type:'String',
enum:['北京','上海'] //只能是北京、上海人
},
other:{
type:'String',
validate:[validator,err] //validator是一个验证函数,err是验证失败的错误信息
}
});

####7.2 验证失败

如果验证失败,则会返回err信息,err是一个对象该对象属性如下

1
2
3
4
5
6
7
err.errors //错误集合(对象)
err.errors.color //错误属性(Schema的color属性)
err.errors.color.message //错误属性信息
err.errors.path //错误属性路径
err.errors.type //错误类型
err.name //错误名称
err.message //错误消息

一旦验证失败,Model和Entity都将具有和err一样的errors属性

中间件

一种控制函数,类似插件,能控制流程中的init、validate、save、remove方法

1.Serial串行中间件

1
2
3
4
5
var schema = new Schema(...);
schema.pre('save',function(next){
//做点什么
next();
});

2.Parallel并行中间件

1
2
3
4
5
6
var schema = new Schema(...);
schema.pre('save',function(next,done){
//下一个要执行的中间件并行执行
next();
doAsync(done);
});

3.使用范畴

  • 复杂的验证
  • 删除有主外关联的doc
  • 异步默认
  • 某个特定动作触发异步任务,例如触发自定义事件和通知