Mongoose学习
1. 说明
因为网上关于mongoose的文档有很多,但是都是分块的没有系统的总结,所以我总结网上的文章理出来一条思路,文档中的很多内容都是从其他文章中复制过来的,只作为个人知识的学习,不会用于商用,在文章多处我也注明了出处
2. Mongoose介绍
2.1. 什么是MongoDB?
MongoDB是一个开源的NoSQL数据库,相比MySQL那样的关系型数据库,它更为轻巧、灵活,非常适合在数据规模很大、事务性不强的场合下使用。
2.2. 什么是Mongoose?
Mongoose是封装了MongoDB的操作的一个对象模型库,为nodejs而生。就好像我们嫌原生javascript难写,代码量多,于是用jQuery库一样,因为MongoDB的操作接口复杂,不人性,所以有了Mongoose。这个库完全是可选的。
2.3. 名词解释
- Schema:一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力
- Model:由Schema发布生成的模型,具有抽象属性和行为的数据库操作对
- Entity:由Model创建的实体,他的操作也会影响数据库
2.4. 注意
- Schema、Model、Entity的关系请牢记,Schema生成Model,Model创造Entity,Model和Entity都可对数据库操作造成影响,但Model比Entity更具操作性。
2.5. 学习文章
- 官网:http://mongoosejs.com/docs/populate.html
- github:https://github.com/Automattic/mongoose
- 博客园系列教程:http://www.cnblogs.com/surahe/category/789274.html(必看)
- 其他杂文
- nodejs:https://cnodejs.org/topic/504b4924e2b84515770103dd
- 操作实例:http://blog.csdn.net/hsany330/article/details/51970783
- 文章:http://blog.csdn.net/cengjingcanghai123/article/details/38353879
- http://www.tuicool.com/articles/77V3maV
- http://mongoosejs.com/docs/populate.html
- https://cnodejs.org/topic/55b9816635ce2ac164f51912
- https://github.com/moajs/mongoosedao
- http://www.jianshu.com/p/9b20c1e2f373
- http://www.jb51.net/article/77435.htm
- http://www.cnblogs.com/sword-successful/p/5098327.html?utm_source=tuicool&utm_medium=referral
3. Schema
3.1. Schema.Type
-
内置类型
String Number Date Buffer Boolean Mixed Objectid Array
-
用法实例
var schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: { type: Date, default: Date.now },
age: { type: Number, min: 18, max: 65 },
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
array: [],
ofString: [String],
ofNumber: [Number],
ofDates: [Date],
ofBuffer: [Buffer],
ofBoolean: [Boolean],
ofMixed: [Schema.Types.Mixed],
ofObjectId: [Schema.Types.ObjectId],
nested: {
stuff: { type: String, lowercase: true, trim: true }
}
})
// example use
var Thing = mongoose.model('Thing', schema);
var m = new Thing;
m.name = 'Statue of Liberty';
m.age = 125;
m.updated = new Date;
m.binary = new Buffer(0);
m.living = false;
m.mixed = { any: { thing: 'i want' } };
m.markModified('mixed');
m._someId = new mongoose.Types.ObjectId;
m.array.push(1);
m.ofString.push("strings!");
m.ofNumber.unshift(1,2,3,4);
m.ofDates.addToSet(new Date);
m.ofBuffer.pop();
m.ofMixed = [1, [], 'three', { four: 5 }];
m.nested.stuff = 'good';
m.save(callback);
3.2. Validation(验证)
- Validation是在SchemaType定义
- Validation是中间件的内部组件
- Validation发生在document试图在默认值应用之后保存的时候。
- Validation不在未定义的值运行,唯一例外是必要的验证器。
- Validation是异步递归的,当你调用Model#save,子文档验证也会执行。如果发生错误,Model#save回调会接收它。
- 验证支持完全定制
3.2.1. 内置校验器
- 所有SchemaTypes都有内置requiredValidation。
- 数字有最大值和最小值验证。
- 字符串有枚举、匹配、最大长度和最小长度验证。
- 用法实例
var schema = new mongoose.Schema({
name:{
type:'String',
required: true,
maxlength: 10,
match: /^a/
},
date: {
type: Date,
default: Date.now
},
age:{
type:'Number',
min:18, //年龄最小18
max:120 //年龄最大120
},
city:{
type:'String',
enum:['北京','上海'] //只能是北京、上海人
},
});
3.2.2. 自定义校验器
- 如果内置验证器不够,validation能够完全可以满足你的需求
- 用法实例
var userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v) {
return /d{3}-d{3}-d{4}/.test(v);
},
message: '{VALUE} is not a valid phone number!'
}
}
});
var User = mongoose.model('user', userSchema);
var u = new User();
u.phone = '555.0123';
// Prints "ValidationError: 555.0123 is not a valid phone number!"
console.log(u.validateSync().toString());
u.phone = '201-555-0123';
// Prints undefined - validation succeeded!
console.log(u.validateSync());
3.3. 创建
- 语法:
mongoose.model(modelName, schema)
- 用法实例
var Blog = mongoose.model('Blog', blogSchema)
3.4. 实例方法
- 用法实例
// define a schema
var animalSchema = new Schema({ name: String, type: String });
// assign a function to the "methods" object of our animalSchema
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); // woof
});
3.5. 静态方法
- 用法实例
// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
return this.find({ name: new RegExp(name, 'i') }, cb);
}
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
console.log(animals);
});
3.6. 索引
- MongoDB支持二级索引。在mongoose,at the path level或schema层次定义Schema的索引。当创建复合索引时,在shema定义索引是必要的。
var animalSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true } // field level
});
animalSchema.index({ name: 1, type: -1 }); // schema level
- 当程序启动时,Mongoose为每个在schema定义的索引自动地调用ensureIndex 。Mongoose会连续为每个索引调用ensureIndex,当所有ensureIndex调用成功或发生错误在model发出index事件。建议在生产中禁止这种行为因为索引创建能够导致显著的性能影响。通过给schema设置autoIndex选项为false来禁用行为,或者在connection全局设置选项config.autoIndex为false。
animalSchema.set('autoIndex', false);
// or
new Schema({..}, { autoIndex: false });
3.7. Virtuals(虚拟属性)
- virtual是你能 get 和 set 但不能保存到MongoDB的document属性。getter用于格式化或符合的field,而setter用于de-composing一个单值到多值存储。
- 用法实例
// define a schema
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
personSchema.virtual('name.full').get(function () {
return this.name.first + ' ' + this.name.last;
});
// compile our model
var Person = mongoose.model('Person', personSchema);
// create a document
var bad = new Person({
name: { first: 'Walter', last: 'White' }
});
console.log('%s is insane', bad.name.full); // Walter White is insane
bad.name.full = 'Breaking Bad';
personSchema.virtual('name.full').set(function (name) {
var split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});
...
mad.name.full = 'Breaking Bad';
console.log(mad.name.first); // Breaking
console.log(mad.name.last); // Bad
3.8. option(扩展选项)
- 用法实例
new Schema({..}, options);
// or
var schema = new Schema({..});
schema.set(option, value);
- 有效选项
- https://cnodejs.org/topic/504b4924e2b84515770103dd
- autoIndex
- capped
- collection
- emitIndexErrors
- id
- _id
- minimize
- read
- safe
- shardKey
- strict
- toJSON
- toObject
- typeKey
- validateBeforeSave
- versionKey
- skipVersioning
- timestamps
3.9. Middleware(中间件)
- 中间件(也称为pre and post hook)是执行异步函数期间传递控制权的函数。中间件在schema级别上被指定并对于编写插件非常有用
3.9.1. document中间件和query中间件
-
document中间件支持以下document函数
- init
- validate
- save
- remove
-
query中间件支持以下model和query函数
- count
- find
- findOne
- findOneAndRemove
- findOneAndUpdate
- update
- 注意:没有对remove()的query hook,只有对document的。如果你设置了一个remove hook,执行myDoc.remove()时它会激活,而不是执行MyModel.remove()。
3.9.2. pre类型中间件
3.9.3. post类型中间件
3.10. Plugins(插件)
- schema是可插入的,即,它们可以应用预包装的能力,从而扩展其功能。这是一个非常强大的功能。
4. Model
5. Document
5.1. Population(join)
- http://www.cnblogs.com/surahe/p/5188211.html
- population是自动将document中指定path替换为其他collection的document的过程。我们能迁移document、多个document、简单对象、多个简单对象或者是查询返回的所有对象。
5.2. Sub-Docs(子文档)
- https://cnodejs.org/topic/504b4924e2b84515770103dd
- http://www.cnblogs.com/surahe/p/5180870.html
- sub-document享有所有与普通document相同的特征。唯一不同的是它们不单独保存,当它们的顶层父document保存时它们才被保存。
- 如果在子文档中间件发生错误,它冒泡到父的save()回调,因此错误处理是小事一桩。
6. CURD
6.1. 连接
- http://www.cnblogs.com/surahe/p/5191598.html
- 在项目只能够创建一个数据库连接
var mongoose = require('mongoose'); //引用mongoose模块
var db = mongoose.createConnection('localhost','test'); //创建一个数据库连接
- 打开本机localhost的test数据库时,我们可以监测是否有异常
db.on('error',console.error.bind(console,'连接错误:'));
db.once('open',function(){
//一次打开记录
});
6.2. 创建
6.2.1. document保存
var Tank = mongoose.model('Tank', yourSchema);
var small = new Tank({ size: 'small' });
small.save(function (err) {
if (err) return handleError(err);
// saved!
})
6.2.2. model保存
- 语法
- 该方法既可以存储一条数据也可以存储多条数据
- 如果有回调函数,那么在存储多条数据的时候如果有一条报错了,那么不会走promise的then方法,但是其他没有报错的数据已经被插入了
- 如果没有回调函数,那么在存储多条数据的时候如果有一条报错了,那么会走promise的then方法,但是其他没有报错的数据已经被插入了
Model.create({ size: 'small' }, function (err, doc) {
if (err) return handleError(err);
// saved!
})
const promise= Model.create(Doc)
promise.then(function (result) {
console.log(1111111111)
console.log(result)
},function (err) {
console.log(2222222222)
console.log(err)
})
6.3. 修改
6.3.1. 修改不返回document
- 语法:
Model.update(conditions, doc, [options], [callback])
- 用法实例
Tank.update({ _id: id }, { $set: { size: 'large' }}, function (err, raw) {
if (err) return handleError(err);
console.log('The raw response from Mongo was ', raw);
});
6.3.2. 修改返回document
- 写法一
Tank.findById(id, function (err, tank) {
if (err) return handleError(err);
tank.size = 'large';
tank.save(function (err) {
if (err) return handleError(err);
res.send(tank);
});
});
- 写法二(推荐)
Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, function (err, tank) {
if (err) return handleError(err);
res.send(tank);
});
6.4. 删除
Tank.remove({ size: 'large' }, function (err) {
if (err) return handleError(err);
// removed!
});
6.5. 查询
6.5.1. 直接查询
- 在查询时带有回调函数的,称之为直接查询,查询的条件往往通过API来设定
- 用法实例
PersonModel.findOne({'name.last':'dragon'},'some select',function(err,person){
//如果err==null,则person就能取到数据
});
6.5.2. 链式查询
6.5.2.1. 写法一
- 这种方式相对直接查询,分的比较明细,如果不带callback,则返回query,query没有执行的预编译查询语句,该query对象执行的方法都将返回自己,只有在执行exec方法时才执行查询,而且必须有回调。
- 用法实例
var query = PersonModel.findOne({'name.last':'dragon'});
query.select('some select');
query.exec(function(err,pserson){
//如果err==null,则person就能取到数据
});
6.5.2.2. 写法一
- 用法实例
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);
免责说明
1、本博客中的文章摘自网上的众多博客,仅作为自己知识的补充和整理,并分享给其他需要的coder,不会用于商用。
2、因为很多博客的地址看完没有及时做保存,所以很多不会在这里标明出处,非常感谢各位大牛的分享,也希望大家理解。