在Nodejs开发中,我们一般使用mongoose来操作mongoDB数据库。下面我么来学习下mongoose常用的用法。
准备工作
那要使用它,首先你得装上node.js和mongodb并启动mongodb数据库
Github地址:https://github.com/Automattic/mongoose
API Docs:http://mongoosejs.com/docs/guide.html
概念理解
要学习mongoose首先要了解三个重要的概念,他们是Schema、Model、Document。它们的关系是Schema生成Model、Model创造Document。
Schema类似于定义表结构,但并不完全准确。它用于创建表时的数据定义(不仅仅可以定义文档的结构和属性,还可以定义文档的实例方法、静态模型方法、复合索引等),每个Schema会映射到mongodb中的一个collection。Schema不具备操作数据库的能力,主要用于定义结构。
Model是由Schema编译而成的构造器,具有抽象属性和行为,可以对数据库进行增删查改。Model所能增删查改的具体值是由Schema定义的,Schema定义以外的值则没有效果。在mongoDB中一个数据库有多个collections(由Schema定义结构),而每个collections有多个document(类似js对象一般的键值对)。
Model的每一个实例(instance)就是一个文档document。Document是由Model创建的实体,它的操作也会影响数据库。
连接数据库
在使用前首先连接数据库(若没有则创建之)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
//已连接
mongoose.connection.on('connected', function () {
console.log('已连接');
});
//连接错误
mongoose.connection.on('error',function (err) {
console.log('出错');
});
//断开连接
mongoose.connection.on('disconnected', function () {
console.log('断开连接');
});
其它事件可以自行查看:http://mongoosejs.com/docs/api.html#connection_Connection
定义Schema
Schema是mongoose中需要事先定义的数据模式,类似于表结构,不过mongoDB中不叫表,叫collections。
模型可以看作mysql中的数据表,属性可以看作是字段,当然这个类比并不十分正确。
每个Schema对应当前连接的数据库的一个collection,若不存在则会自动创建。
Schema不具备操作数据库的能力。
下面我们来看下如何创建Schema
1 | var mongoose = require('mongoose'); |
type有下面这些,String
、Number
、Boolean
、Array
、Buffer
、Date
、ObjectId
、Mixed
类型
生成Model
模型Model是根据Schema编译出的构造器,或者称为类,通过Model可以实例化出文档对象document。
下面根据Schema构建一个Model1
var Blog = mongoose.model('Blog', blogSchema);
Model实例化之后为document。1
2
3var blogInstance = new Blog({
...
});
常用数据库操作
新增
document.save()
model实例化后的document可用save新增文档1
2
3
4
5
6
7
8
9
10
11
12
13//实例化model
var blogInstance = new Blog({
...
});
//调用save方法即插入一个新的document
user.save(function (err, res) {
if (err) {
console.log("Error:" + err);
}
else {
console.log("Res:" + res);
}
});
model.create()
使用save()方法,需要先实例化为文档,再使用save()方法保存文档。而create()方法,则直接在模型Model上操作,并且可以同时新增多个文档
1 | Blog.create({name:"xiaowang"},{name:"xiaoli"},function(err,doc1,doc2){ |
查询
查询文档有以下三种方法1
2
3find()
findById()
findOne()
find()
第一个参数表示查询条件,第二个参数用于控制返回的字段,第三个参数用于配置查询参数,第四个参数是回调函数,回调函数的形式为function(err,docs){}1
Model.find(conditions, [projection], [options], [callback])
findById()1
Model.findById(id, [projection], [options], [callback])
findOne()
该方法返回查找到的所有实例的第一个1
Model.findOne([conditions], [projection], [options], [callback])
更新
更新文档有以下两种方法1
2update()
updateMany()
update()
第一个参数conditions为查询条件,第二个参数doc为需要修改的数据,第三个参数options为控制选项,第四个参数是回调函数1
Model.update(conditions, doc, [options], [callback])
updateMany()
updateMany()与update()方法唯一的区别就是默认更新多个文档,即使设置{multi:false}也无法只更新第一个文档1
Model.updateMany(conditions, doc, [options], [callback])
删除
更新文档有以下三种方法1
2
3remove()
findOneAndRemove()
findByIdAndRemove()
remove()
remove有两种形式,一种是文档的remove()方法,一种是Model的remove()方法。
下面介绍Model的remove()方法,该方法的第一个参数conditions为查询条件,第二个参数回调函数的形式如下function(err){}。
1 | model.remove(conditions, [callback]) |
下面介绍文档的remove()方法,该方法的参数回调函数的形式如下function(err,doc){}1
document.remove([callback])
findOneAndRemove()
model的remove()会删除符合条件的所有数据,如果只删除符合条件的第一条数据,则可以使用model的findOneAndRemove()方法1
Model.findOneAndRemove(conditions, [options], [callback])
findByIdAndRemove()1
Model.findByIdAndRemove(id, [options], [callback])
前后钩子
前后钩子即pre()和post()方法,又称为中间件,是在执行某些操作时可以执行的函数。中间件在schema上指定,类似于静态方法或实例方法等
可以在数据库执行下列操作时,设置前后钩子1
2
3
4
5
6
7
8
9
10
11init
validate
save
remove
count
find
findOne
findOneAndRemove
findOneAndUpdate
insertMany
update
pre()
以find()方法为例,在执行find()方法之前,执行pre()方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var schema = new mongoose.Schema({ age:Number, name: String,x:Number,y:Number});
schema.pre('find',function(next){
console.log('我是pre方法1');
next();
});
schema.pre('find',function(next){
console.log('我是pre方法2');
next();
});
var temp = mongoose.model('temp', schema);
temp.find(function(err,docs){
console.log(docs[0]);
})
/*
我是pre方法1
我是pre方法2
{ _id: 5972ed35e6f98ec60e3dc886,name: 'huochai',age: 27,x: 1,y: 2 }
*/
查询后处理
常用的查询后处理的方法如下所示1
2
3
4
5
6
7sort 排序
skip 跳过
limit 限制
select 显示字段
exect 执行
count 计数
distinct 去重
例子1
2
3
4//按照age从小到大排列
temp.find().sort("age").exec(function(err,docs){
console.log(docs);
});
文档验证
如果不进行文档验证,保存文档时,就可以不按照Schema设置的字段进行设置,分为以下几种情况
- 缺少字段的文档可以保存成功
- 包含未设置的字段的文档也可以保存成功,未设置的字段不被保存
- 包含字段类型与设置不同的字段的文档也可以保存成功,不同字段类型的字段被保存为设置的字段类型
而通过文档验证,就可以避免以上几种情况发生
文档验证在SchemaType中定义,格式如下
1 | { name: {type:String, validator:value} } |
常用验证包括以下几种1
2
3
4
5
6
7required: 数据必须填写
default: 默认值
validate: 自定义匹配
min: 最小值(只适用于数字)
max: 最大值(只适用于数字)
match: 正则匹配(只适用于字符串)
enum: 枚举匹配(只适用于字符串)