mongoose官方文档总结
条评论更新说明:对文章目录排版做了调整。
更新时间:2022-05-04
一、mongoose
- 安装:npm install mongoose
1 | // 1,引入mongoose |
mongoose里,一切始于Schema:
1 | let tomSchema = mongoose.Schema({ |
二、Schema-模式
- 每个Schema都会映射到MongoDB 的collection,并定义这个collection里的文档构成
语法:
- const shcema = mongoose.Schema({})
允许使用的Schematypes有:
- String
- Boolean
- Date
- Number
- Array
- Buffer
- Mixed
- ObjectId
除了映射collection外,还可以定义
- document的instance methods
- model的static Model methods
- 复合索引
- 文档的生命周期钩子,也成为中间件
model
我们要把一个Schema转化为一个model,要使用
- let model = mongoose.model(modelName,schema) 函数
collection和document
- collection相当于关系型数据库中的表
- document相当于一条数据,在这里有特别需要注意的一点是:
- collection不要求文档有相同的结构,在一个collection文档中不必具有相同的fileds,对于单个field在一个collection中的不同文档中可以是不同的数据类型
实例方法methods
- documents是model的实例,document有自带的实例方法,当然也可以自定义我们自己的方法。
1 | const animalSchema = mongoose.Schema({type:String,name:String}) |
静态方法
- 静态方法与实例方法的区别是,实例方法是在每个model的实例中可以访问,而静态方法是每个model直接访问
1 | animalSchema.statics.findByName = function(name,cb) { |
查询助手
- 查询助手作用于query实例,方便定义自己的查询扩展
1 | animalSchema.query.byName = function(name) { |
索引
- Mongodb支持secondary indexes,在mongoose中,我们在Schema中定义索引,索引字段级别和shcema级别
1 | var animalSchema = new Schema({ |
虚拟值 Virtual
- [ ] Virtual是document的属性,但是不会保存到MongoDB,getter可以用于格式化和组合字段数据,setter可以很方便的分解一个值到多个字段。
1 | // define a schema |
- 如果你要log出全名,可以这么做:
1 | console.log(axl.name.first + ' ' + axl.name.last); // Axl Rose |
- 但是每次都这么拼接实在太麻烦了, 推荐你使用virtual property getter, 这个方法允许你定义一个 fullName 属性,但不必保存到数据库。
1 | personSchema.virtual('fullName').get(function () { |
- 现在, mongoose 可以调用 getter 函数访问 fullName 属性:
1 | console.log(axl.fullName); // Axl Rose |
- 如果对 document 使用 toJSON() 或 toObject(),默认不包括虚拟值, 你需要额外向 toObject() 或者 toJSON() 传入参数** { virtuals: true }**。
你也可以设定虚拟值的 setter ,下例中,当你赋值到虚拟值时,它可以自动拆分到其他属性:
1 | personSchema.virtual('fullName'). |
- 再次强调,虚拟值不能用于查询和字段选择,因为虚拟值不储存于 MongoDB。
选项
- Schema有很多可配置选项,你可以在构造时传入或者直接set,选项较多,暂不学习整理。
1 | new Schema({..}, options); |
三、SchemaTypes-模式类型
以下是mongoose的所有合法SchemaTypes:
- String
- Boolean
- Number
- Array
- Buffer
- Date
- Schema.Types.ObjectId
- Schema.Types.Mixed
- Schema.Types.Decimal128
SchemeType选项
- 你可以直接声明schema type为某一种type,或者赋值一个含有type属性的对象
1 | var schema1 = new Schema({ |
- 除了type属性,还可以对这个字段路径指定其它属性,比如在保存之前全部转换为小写
1 | var shema2 = new Schema({ |
全部可用
- required:布尔值或者函数 如果值为真,为此属性添加require验证器
- default: 任何值或函数 设置此路径默认值,如果是函数m,函数返回值为默认值
- select: 布尔值 指定query的默认projections
- validate: 函数校验
- get:函数,使用Object.defineProperty()定义自定义getter
- set:同上
- alias:别名
索引相关
可以使用 schema type定义索引相关
- index:布尔值 是否对这个属性创建索引
- unique:布尔值 是否对这个属性创建唯一索引
- sparse:布尔值 是否对这个属性创建稀疏索引
四、Connections-连接
- 可以使用 mongoose.connect()连接MongoDB,默认端口27017
操作缓存
就是说不必等待上面的connect连接成功后,就可以使用创建的 Mongoose models
禁用缓存,要修改 bufferCommands配置,mongoose.set(‘bufferCommands’,fasle)
选项
connect 方法也接受 options 参数,这些参数会传入底层 MongoDB 驱动。
回调
connect()函数接受回调函数,或返回一个Promise
keepAlive
对于长期运行的后台应用,启用毫秒级 keepAlive 是一个精明的操作。不这么做你可能会经常 收到看似毫无原因的 “connection closed” 错误。
mongoose.connect(uri,{keepAlive:120})
五、models-模型
1 | var schema = new mongoose.Schema({ name: 'string', size: 'string' }); |
上面的参数 Tank是跟model对应的集合(collection)对应的单数形式。
Mongoose会自动找到名称是model的名字的复数形式。
比如上例,Tank这个model对应数据库中tanks这个collection
.model()这个函数是对 schema做了拷贝
确保在调用.model()之前把所有需要的东西都加进shema里。
构造documents
- documents是model的实例,创建谈并保存到数据库非常简单:
1 | const Tank = mongoose.model('Tank',TankSchema) |
查询
- 查询文档可以用model的find、findbyId,findOne,和where这些静态方法。
删除
- model的remove方法可以删除所有匹配查询条件(condition)的文档
1 | Tank.remove({size:small},function(err){ |
更新
model
的update
方法可以修改数据库中的文档,不过不会把文档返回给应用层。- 如果想更新单独一条文档并且返回给应用层,可以使用 findOneAndUpdate 方法。
六、文档-Documents
- Mongoose document代表着MongoDB文档的一对一映射。每个document都是他的Model的实例。
更新
使用findById:
1 | Tank.findById(id,function(err,tank){ |
- 若仅仅需要更新数据,而不需要获取数据再去更新:
Tank.update({_id:id},{$set:{size:‘large’}},callback)
- 更新后我们还需要返回这个文档:findByIdAndUpdate
1 | Tank.findByIdAndUpdate(id,{$set:{size:'large'}},{new:true},function(err,tank){ |
七、子文档-SubDocuments
子文档是指嵌套在另一个文档中的文档。
在Mongoose中,意味着你可以在里嵌套另一个schema。
Mongoose子文档有两种不同的概念:子文档数组和单个嵌套子文档
1 | const chidlSchema = new Schema({name:String}) |
子文档与文档的区别是 子文档不能单独保存,他们会在他们的顶级文档保存时保存。
1 | const Parent = mongoose.model('Parent',parentSchema) |
八、Queries 查询
- Model的多个静态辅助方法都可以查询文档
- Query实例有一个.then()函数,用法类似Promise
我们看一下demo,查询persons表中name中属性last为Ghost值的文档,只查询 name和occupation两个字段
1 | const Person = mpngoose.model('Pseron',PersonSchema); |
查询结果的格式取决于做什么操作:
- findOne()是单个文档
- find() 是文档列表
- count() 是文档数量
- update() 是更新的文档数量
九 中间件–Middleware
- 中间件(pre 和 post 钩子)是在异步函数执行时函数传入的控制函数。
- Middleware is specified on the shema.
- Mongoose4.x有四种中间件:doucument中间件、model中间件、aggregate中间件、query中间件。
- document 中间件支持以下document操作:
- init
- validate
- save
- remove
- query 中间件支持以下 Model 和 Query 操作
- count
- find
- findOne
- findOneAndUpdate
- findOneAndRemove
- updade
- aggregate 中间件作用于MyModel.aggregate(),他会在你对 aggregate 对象调用 exec()时执行
- aggregate
- Model中间件支持以下操作:
- insertMany
- 所有中间件支持 pre 和 post 钩子。
- Query 没有 remove()钩子,只有 docuemnt 有,如果设定了remove钩子,他将会在你调用 myDoc.remove()触发,而不是 myModel.remove(),另外,create()函数会触发 save()钩子。
pre
pre钩子分为『串行』和『并行』两种
- 串行:
串行中间件一个接一个的执行。具体来说,上一个中间件调用 next 的时候下一个执行
1 | const schema = new Schema(..); |
在 mongoose5.x 中,除了手动调用 next 函数,还可以返回一个 Promise,甚至是 async/await。
1 | schema.pre('save',function(){ |
- 并行
并行中间件提供细粒度流控制。
1 | const schema = new Schema(..) |
在这个例子中,save 方法将在所有中间件都调用了 done 方法的时候才会执行。
使用场景:
- 复杂的数据校验
- 删除依赖文档(删除用户后删除他的所有文档)
- asynchronous defaults
- asynchronous tasks that a certain action triggers
Post
Post中间件在方法执行之后调用,这个时候每个 pre 中间件都已完成
1 | schema.post('init',function(doc){ |
- 异步 Post 钩子
如果你给 post 钩子的回调函数传入两个参数,mongoose 会认为第二个参数是 next()函数,可以通过 next 触发下一个中间件
1 | schema.post('save',function(doc,next){ |
- Save/Validate钩子
save()函数触发 validate()钩子,mongoose validate()钩子其实就是 pre(‘save’)钩子,这意味着所有pre(‘validate’)和 post(‘validate’)钩子都会在 pre(‘save’)之前调用。
- findAndUpdate() 和 Query 中间件使用注意
pre 和 post save()钩子都不执行于 update()、 findOneAndUpdate()等情况
mongoose4.x为这些函数制定了新钩子
1 | schema.pre('find',function(){ |
错误处理中间件
next()
执行错误时,中间件执行立即停止。但是我们有特殊的 post 中间件技巧处理这个问题 —— 错误处理中渐渐,它可以在出错后执行你指定的代码。
错误处理中间件比普通中间件多一个error
参数,并且err
作为第一个参数传入。 而后错误处理中间件可以让你自由地做错误的后续处理
1 | const schema = new Schema({ |
十、填充–Populate
demo
MongoDb 在 3.2之后,也有像 sql 中的 join 聚合操作,那就死$lookup,而 mongoose 拥有更强大的 populate,可以让你在别的 collection 中引用 document。
Populate 可以自动替换 document 中的指定字段,替换内容从其他 collection 获取,我们填充(populate)单个或者多个 document、单个或者多个对象,甚至是 query 返回的一切对象:
1 | const mongoose = require('mongoose') |
我们创建了两个model,Person model中的 stories 字段为 ObjectID 数组,ref 选项告诉mongoose 在填充的时候使用哪个 model,上面的例子就是指 Story 的 model。所有储存在此的_id 都必须是 Story model 中的 document 的 _id
保存 refs
保存 refs 与保存普通属性一样,把_id的值赋给他就好了
1 | const author = new Person({ |
Population
1 | Story. |
设置被填充字段
mongoose4.0之后,你可以手动填写一个字段
1 | Story.findOne({title:'my book'},function(err,story){ |
十一、鉴别器–Discriminator
Discriminator是一种 schema 继承机制。它允许你在相同的底层MongoDb collection上使用部分重叠的 schema 建立多个 model。
本文标题:mongoose官方文档总结
文章作者:六个周
发布时间:2021-03-12
最后更新:2022-05-04
原始链接:https://blog.liugezhou.online/mongoose%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3%E6%80%BB%E7%BB%93/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!