Mongoose
Mongoose 是在 node 环境下对 MongoDB 进行便捷操作的对象模型工具。
创建数据库连接
利用 Mongoose 的 connect 创建数据库连接。
mongoose.connect(url, options)
1 2
| var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/blog');
|
你可以监听连接数据库的各种事件:
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 29
| var mongoose = require('mongoose'); var DB_URL = 'mongodb://localhost:27017/blog'; mongoose.connect(DB_URL);
mongoose.connection.on('connected', function () { console.log('Mongoose connection open to ' + DB_URL); });
mongoose.connection.on('error',function (err) { console.log('Mongoose connection error: ' + err); });
mongoose.connection.on('disconnected', function () { console.log('Mongoose connection disconnected'); });
mongoose.connect('mongodb://localhost:27017/blog', function(err) { if (err) { console.log('Mongoose connection error: ' + err); } else { console.log('Mongoose connection is successful'); } });
|
指定用户连接:
1
| mongoose.connect('mongodb://username:password@127.0.0.1:27017/database');
|
连接多个数据库:
1 2 3
| mongoose.connect('urlA, urlB, ...', { mongos: true })
|
关闭数据库连接
Schema
Schema 是一种以文件形式存储的数据库模型骨架。Schema 主要用于定义 MongoDB 中集合 Collection 里文档 Document 的结构(不仅能够定义文档的结构和属性,和可以定义文档的实例方法、静态模型方法、复合索引等),每个 Schema 会映射到 MongoDB 中的一个 Collection,它不具备数据库的操作能力。
1 2 3 4 5 6 7
| var Schema = mongoose.Schema; var UserSchema = new Schema({ username: String, userpwd: String, age: Number, sex: String })
|
Schema 中字段的类型,可取值为:
- String 字符串
- Number 数字
- Date 日期
- Buffer 二进制
- Boolean 布尔值
- Mixed 混合类型
- ObjectId 对象ID
- Array 数组
Model
Model 是由 Schema 生成的的模型,可以对数据库进行操作。Model 的每一个实例,就是一个 Document ,Document 可以保存到数据库和对数据库进行操作。
1
| var UserModel = mongoose.model('users', UserSchema);
|
实例化文档及保存
1 2 3 4 5 6 7 8 9 10 11 12 13
| var userDoc = new UserModel({ username: 'node', userpwd: '123456', age: 8, sex: 'male' })
userDoc.save(function(err, doc) { if (err) { } console.log(doc); })
|
常用数据库操作
对于数据库操作而言,平时最为平常莫非 CRUD 了。
文档新增
- save()
- 使用模型的create()
- 使用模型的insertMany()
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| var userDoc = new UserModel({ username: 'node', userpwd: '123456', age: 8, sex: 'male' })
userDoc.save(function(err, doc) { if (err) { } console.log(doc); })
UserModel.create({ username: 'node', userpwd: '123456', age: 8, sex: 'male' }, { username: 'vue', userpwd: '123456', age: 3, sex: 'male' }, function(err, doc1, doc2) { if (err) { } console.log(doc1); console.log(doc2); })
UserModel.insertMany([{ username: 'node', userpwd: '123456', age: 8, sex: 'male' }, { username: 'vue', userpwd: '123456', age: 3, sex: 'male' }], function(err, docs) { if (err) { } console.log(docs); })
|
文档查询
Mongoose 提供了三种方法来查询文档:
- find()
- findeById()
- findOne()
find
Model.find(condtions, [fields], [options], [callback])
- conditions
- [fields]
- [options]
- [callback]回调函数
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 29 30 31 32 33 34 35
| UserModel.find(function(err, docs) { if (err) { console.log(err); } console.log(docs); })
UserModel.find({age: {$gte: 18}}, function(err, docs) { if (err) { console.log(err); } console.log(docs); })
UserModel.find({age: {$gte: 18}, name: /node/}, function(err, docs) { ... })
UserModel.find({name: /node/}, 'name', function(err, docs) { ... })
UserModel.find(null, {name: 1, sex: 1, _id: 0}, function(err, docs) { ... })
UserModel.find(null, null, {skip: 2}, function(err, docs) { ... })
|
findById
Model.findById(id, [fields], [options], [callback])
- id
- [fields]
- [options]
- [callback]回调函数
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 29 30
| var result = []; UserModel.find(function(err, docs) { docs.forEach(function(item, index, arr) { result.push(item._id); }) UserModel.findById(result[0], function(err, doc) { ... }) })
var result = []; UserModel.find(function(err, docs) { docs.forEach(function(item, index, arr) { result.push(item._id); }) UserModel.findById(result[0]).exec(function(err, doc) { .... }) })
UserModel.findById(result[0],{name: 1, _id: 0}).exec(function(err, doc) { .... })
UserModel.findById(result[0],{lean: true}).exec(function(err, doc) { .... })
|
findOne
该方法找出符合条件的所有实例的第一个。
Model.findOne(condtions, [fields], [options], [callback])
- conditions
- [fields]
- [options]
- [callback]回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| UserModel.findOne({age: {$gt: 20}}, function(err, doc) { ... }) UserModel.findOne({age: {$gt: 20}}).exec(function(err ,doc) { ... })
UserModel.findOne({age: {$gt: 20}}, {name: 1, _id: 0}, function(err, doc) { ... }) UserModel.findOne({age: {$gt: 20}}, {name: 1, _id: 0}).exec(function(err, doc) { ... })
UserModel.findOne({age: {$gt: 20}}, "name", {lean: true}, function(err, doc) { ... }) UserModel.findOne({age: {$gt; 20}}, "name", {lean: true}).exec(function(err, doc){ ... })
|
$where
如需更复杂的查询,可以使用$where 操作符。$where 操作符可以使用javascript表达式字符串或者javascript函数。
- 使用字符串
1 2 3 4 5 6
| Model.find({$where: 'this.x === this.y'}, function(err, docs){ ... }) Model.find({$where: 'obj.x === obj.y'}, function(err, docs){ ... })
|
- 使用函数
1 2 3 4 5 6 7 8 9 10
| Model.find({$where: function() { return obj.x !== obj.y; }}, function(err, docs} { ... }); Model.find({$where: function() { return this.x !== this.y; }}, function(err, docs) { ... })
|
文档更新
- update
- updateMany
- find() + save()
- updateOne()
- finedOne() + save()
- findByIdUpdate()
- findOneAndUpdate()
update
Model.update(condtions, update, [options], [callback])
options选项如下:
- safe(boolean) 默认为 true, 安全模式
- upsert(boolean) 默认为false,如果文档不存在,是否创建
- multi(boolean) 默认为false,是否更新多个查询记录
- runValidators 如果为true,执行 Validation 验证
- setDefaultsOnInsert 如果upsert选项为true, 在新建时插入文档定义的默认值
- strict(boolean) 以strict模式进行更新
- overwrite(boolean) 默认为false, 禁用update-only模式,允许覆盖文档。
1 2 3 4 5 6 7 8 9
| UserModel.update({"sex": "female"}, {"sex": "male"}, function(err, res) { ... })
UserModel.update({'sex': 'female'}, {'sex': 'male'}, {multi: true}, function(err, res) { ... })
|
updateMany
updateMany 与 update 方法唯一区别是默认更新多个文档,即使设置 {multi: ‘false’} 也无法只更新第一个文档。
Model.updateMany(conditions, update, [options], [callback])
1 2 3 4
| UserModel.updateMany({name: /xiao/}, {'sex': 'female'}, function(err, res) { ... })
|
find() + save()
1 2 3 4 5 6 7 8 9
| UserModel.find({name: /xiao/}, function(err, docs) { if (err) { console.log(err); } docs.forEach(function(item, index, arr) { item.sex = 'male'; item.save(); }) })
|
updateOne
updateOne 只能更新找到的第一个文档,即使设置 {multi: true} 也无法更新多个文档。
1 2 3
| UserModel.updateOne({name: /xiao/}, {'sex': 'female'}, function(err, res) { ... })
|
findOne() + save()
1 2 3 4
| UserModel.findOne({name: /xiao/}, function(err, doc) { ... doc.save(); })
|
findOneAndUpdate
注意回调函数的参数
1 2 3
| UserModel.findOneAndUpdate({name: /xiao/}, function(err, doc) { ... })
|
findByIdAndUpdate
注意回调函数的参数
1 2 3
| UserModel.findByIdAndUpdate('5a73194a2fd8c96fc87657aa', function(err, doc){ ... })
|
文档删除
- remove()
- findOneAndRemove()
- findByIdAndRemove()
remove
remove 有两种形式,一种是 Model 的 remove 方法,一种是 Document 的 remove 方法。
remove 方法的回调函数不能省略,否则会无法删除数据。
1 2 3 4 5 6 7
| UserModel.remove({name:/xiao/}, function(err) { ... })
document.remove(function(err) { ... })
|
findOneAndRemove
Model.findOneAndRemove(conditions, [options], [callback])
1 2 3
| UserModel.findOneAndRemove({sex: 'female'}, function(err ,doc) { ... })
|
findByIdAndRemove
Model.findByIdAndRemove(id, [options], [callback])
关于自定义方法
Model 实例是 Document,实例内置的方法有很多,比如 save。
通过 Schema 对象的 methods 属性给实例自定义扩展方法
通过 Schema 对象的 statics 属性给 Model 添加静态方法。
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
| var schema = new mongoose.Schema({num: Number, name: String, age: Number}); schema.methods.findSimilarAges = function(cb) { return this.model('MyModel').find({age: this.age}, cb); } schema.statics.findByName = function(name, cb) { return this.find({age: new RegExp(name, 'i')}, cb); } var MyModel = mongoose.model('MyModel', schema); var doc1 = new MyModel({num: 0, name: 'node', age: 8}); var doc2 = new MyModel({num: 1, name: 'vue', age: 8}); var doc3 = new MyModel({num: 2, name: 'react', age: 8}); doc1.save(); doc2.save(); doc3.save(); setTimeout(function() { doc1.findSimilarAges(function(err, docs) { docs.forEach(function(item, index, arr) { ... }) }) }, 0) setTiemout(function() { MyModel.findByAge('node', function(err, docs) { ... }) })
|
前后钩子
前后钩子即 pre() 和 post() 方法,又称中间件,实在执行某些操作的时候,可以执行的函数。中间件在 Schema 上指定,类似于静态方法和实例方法。
可以在数据库执行下列操作时,设置前后钩子
- init
- validate
- save
- remove
- count
- find
- findOne
- findOneAndRemove
- fineOneAndUpdate
- insertMany
- update
在执行上述操作之前,执行 pre 方法。
在执行上述操作之后,执行 post 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var schema = new mongoose.Schema({ age: Number, name: String, x: Number, y: Number}); schema.pre('find', function(next) { console.log('pre'); next(); }) schema.post('find', function(next) { console.log('post'); next(); })
var temp = new mongoose.model('temp', schema);
temp.find(function(err, docs) { console.log('find'); })
|
查询后处理
常用查询后处理的方法如下:
- sort 排序
- skip 跳过
- limit 限制
- select 显示字段
- exect 执行
- count 计数
- distinct 去重
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 29 30 31 32 33 34 35 36 37 38 39
| UserModel.find().sort('age').exec(function(err, docs) { console.log(docs) })
UserModel.find().sort('x -age').exec(function(err, docs) { ... })
UserModel.find().skip(1).exec(function(err, docs) { ... })
UserModel.find().limit(10).exec(function(err, docs) { ... })
UserModel.find().select('name age -_id').exec(function(err, docs) { ... }) UserModel.find().select({name: 1, age: 1, _id: 0}).exec(function(err, docs) { ... })
UserModel.find().count(function(err, count) { ... })
UserModel.find().distinct('age', function(err, distinct) { ... })
|
文档验证
为什么要进行文档验证呢,以下面例子进行说明, Schema 进行如下定义:
1
| var schema = new mongoose.Schema({ age: Number, name: String, x: Number, y: Number});
|
如果不进行文档验证,保存文档时,就可以不按照 Schema 设置的字段进行设置,分下面几种情况:
- 缺少字段的文档可以保存成功
1 2 3 4 5
| var UserModel = mongoose.model('user', schema); new UserModel({age: 0}).save(function(err, doc) { ... })
|
- 包含未设置字段的文档可以保存成功,未设置字段不被保存
1 2 3
| new UserModel({age: 10, abc: 'abc'}).save(function(err, doc) { ... })
|
- 包含字段类型与设置不同字段的文档也可以保存成功,不同字段类型的字段被保存为设置的字段类型
1 2 3 4
| new UserModel({age: true, name: 10}).save(function(err, doc) { .... })
|
通过文档验证,可以避免以上几种发生。
{name: {type: Stirng, validator: value}}
常用验证有以下几种:
- default: 默认值
- validate: 自定义匹配
- min: 最小值,只适用数字
- max: 最大值,只适用数字
- match: 正则匹配,只适用字符串
- enum: 枚举匹配
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
var schemaa = new mongoose.Schema({age: {type: Number, required: true}, name: String, x: Number, y: Number}); var UserModel = mongoose.model('user', schema); new UserModel({name: 'abc'}).save(function(err, doc) { console.log(err); })
var schema = new mongoose.Schema({age: {type: Number, default: 18}, name: String, x: Number, y: Number}); var UserModel = mongoose.model('user', schema); new UserModel({name: 'node'}).save(function(err, doc) { ... })
var schema = new mongoose.Schema({age: {type: Number, min: 0, max: 10}, name: String, x: Number, y: Number}); var UserModel = mongoose.model('user', schema); new UserModel({age: 20}).save(function(err, doc) { ... })
var schema = new mongoose.Schema({ age: Nuber, name: {type: String, match: /a/}, x: Number, y: Number}); var UserModel = mongoose.model('user', schema); new UserModel({name: 'bbb'}).save(function(err, doc) { ... }
var schema = new mongoose.Schema({ age: Number, name: {type: String, enum: ['a', 'b', 'c']}, x: Number, y: Number}); var UserModel - mongoose.model('user', schema); new UserModel({ name: 'bbb'}).save(function(err, doc) { })
var validateLength = function(arg) { if (arg.length > 4) { return true; } return false; } var schema = new mongoose.Schema({name: {type: String, validate: validateLenth}, age: Number, x: Number, y: Number}); var UserModel = mongoose.model('user', schema); new UserModel({name: 'abc'}).save(function(err, doc) { ... })
|
参考