基于Node的mongoose基础操作

mongoose是一个操作mongodb的ORM(Object Relational Mapping对象关系映射)工具,让我们可以很简便的操作mongodb的增删改查,本次学习使用的mongodb版本是4.0.3

  • 使用mongoose连接mongodb,导出的connect方法只需要在需要连接数据库的地方引入调用即可,一般推荐在程序初始化的时候,比如koa2的app.js程序入口处调用一次即可
const mongoose = require('mongoose')
const glob = require('glob')
const { resolve } = require('path')

exports.connect = ()=>{
  //连接数据库
  connectMongo()
  //设置最大连接数
  let maxConnectTimes = 0
  return new Promise((resolve, reject) => {
    //增加数据库监听事件
    mongoose.connection.on('disconnected', () => {
      console.log('***********数据库断开***********')
      if(maxConnectTimes < 3) {
        maxConnectTimes++
        connectMongo() 
      } else {
        reject()
        throw new Error('数据库出现问题,程序无法搞定,请人为修理......')
      }
    })
    mongoose.connection.on('error', err => {
      console.log('***********数据库错误***********')
      if(maxConnectTimes < 3){
        maxConnectTimes++
        connectMongo()
      }else{
        reject(err)
        throw new Error('数据库出现问题,程序无法搞定,请人为修理......')
      }
    })
    //链接打开的时
    mongoose.connection.once('open',()=>{
      resolve()   
    })
  })
}

function connectMongo() {
  mongoose.connect('mongodb://127.0.0.1:27017/shop', { useCreateIndex: true, useNewUrlParser: true }, (err) => {
    if (err) {
      console.log('Connecttion Error' + err)
    } else {
      console.log('MongoDB connected successfully') 
    }
  })
}

// 一次性引入所有Schema文件
exports.initSchemas = () =>{
  // resolve(__dirname,'./schema/','**/*.js') 返回值是一个由传入的多个参数组成的路径,这里代表的意思就是schema中所有文件,除了以.点开头的
  // __dirname在node中就是指文件当前路径的意思
  // glob的作用就是返回一个由匹配规则匹配到的所有文件
  glob.sync(resolve(__dirname,'./schema/','**/*.js')).forEach(require)
}
  • mongoose的数据库增删改查操作是基于Schema和model进行操作的
  • Schema用于定义表字段,modle是表操作对象,mongoose中没有表的概念,但是这样说却更好理解,mongoose中表的概念叫集合,定义字段一般会使用一个单独的js文件来保存,方便进行管理,文件名同于modle名那就更舒服了,创建好字段Schema就可以注册modle了,注册modle的好处在于我们可以使用mongoose在程序任意地方拿到这个modle然后进行增删改查操作
  • 为了便于在数据连接时就把所有的Schema和modle都注册好,可以使用node的glob模块和path模块,引入所有schema和model,详细见项目代码
// 定义字段结构,并且注册到model方便到时候直接使用
const mongoose = require('mongoose')
const Schema = mongoose.Schema
let ObjectId = Schema.Types.ObjectId

const CategorySchema = new Schema({
  AdCarouselId: ObjectId,
  title: String,
  createAt: { type: Date, default: Date.now() }
})

mongoose.model('goodsCategory', CategorySchema)


// 在其他js文件中使用,比如前端发送请求过来后,我们在路由逻辑处理的时候需要对数据库进行增删改查
const router = require('koa-router')()
const mongoose = require('mongoose')

router.post('/addGoodsCateory', async (ctx, next) => {
  console.log(ctx.request.body)
  // 获取modle
  const goodsCategory = mongoose.model('goodsCategory')
  // 存操作需要创建实例,这个实例其实就是一行的所有信息,存入字段需要跟前端传入对应
  let newgoodsCategory = new goodsCategory(ctx.request.body)
  await newgoodsCategory.save().then((res) => {
    // 保存成功可以得到保存进数据库的那条数据,类似查询操作的结果
    ctx.body = {
      code: 1,
      data: res
    }
  }).catch((err) => {
    ctx.body = {
      code: 0,
      data: err
    }
  })
})

module.exports = router


// 读取操作不需要创建实例,直接使用model进行查询
const router = require('koa-router')()
const mongoose = require('mongoose')

router.get('/getGoodsCateory', async (ctx, next) => {
  // 获取modle
  const goodsCategory = mongoose.model('goodsCategory')

  await goodsCategory.find().then((res) => {
    ctx.body = {
      code: 1,
      data: res
    }
  }).catch((err) => {
    ctx.body = {
      code: 0,
      data: err
    }
  })
})

module.exports = router
  • mongoose的保存方法不止是save(),save方法需要创建保存实例,而且一次只能保存一条数据。mongoose还提供了不需要创建保存实例、能够保存多条数据的方法create方法,还有insertMany方法
const router = require('koa-router')()
const mongoose = require('mongoose')

router.post('/addGoodsCateory', async (ctx, next) => {
  console.log(ctx.request.body)
  // 获取modle
  const goodsCategory = mongoose.model('goodsCategory')
  // create不需要创建保存实例,直接在model上操作
  await goodsCategory.create(ctx.request.body).then(res => {
    // 保存成功可以得到保存进数据库的那条数据,类似查询操作的结果
    // create可以保存多条数据,使用逗号隔开,成功后返回值也是一个一个传回来
    // 还可以使用insertMany替代create,也是直接操作model,传入的一个数组,返回值也是一个数组
    ctx.body = {
      code: 1,
      data: res
    }
  }).catch(err => {
    ctx.body = {
      code: 0,
      data: err
    }
  })
})

module.exports = router
  • mongoose的查询操作
  • 提供了find()方法查询数据库所有数据,不加参数就是返回所有数据,配置参数就是按参数查询数据库,返回值是数组。find(conditions, [projection], [options]) 
  1. conditions 查询条件,类型是个对象,可以传入多个条件
  2. projection 控制返回字段,即留下数据库的某些字段,某些字段舍弃。可以传入字符串,传入字符串说明只要求返回该字段和_id字段,默认_id字段会传回来。如果需要控制多个字段,就传入对象类型,使用1和0来决定返回和舍弃
  3. options对查询结果进行简单处理配置,有{skip:2}跳过前2条,{limit:10}返回前10条,{sort: {age: 1}}根据指定字段排序,1升序,-1降序,count计数,distinct去重
// 查询读取操作不需要创建实例,直接使用model进行查询
const router = require('koa-router')()
const mongoose = require('mongoose')

router.get('/getGoodsCateory', async (ctx, next) => {
  // 获取modle
  const goodsCategory = mongoose.model('goodsCategory')

  await goodsCategory.find().then((res) => {
    ctx.body = {
      code: 1,
      data: res
    }
  }).catch((err) => {
    ctx.body = {
      code: 0,
      data: err
    }
  })
})

module.exports = router
  • 根据id查询findById(id, [projection])  projection 控制返回字段
  • findOne([conditions], [projection], [options]) 返回查询结果的第一个
  • 查询条件conditions参数字段详解

查询年龄大于18的find({age:{$gt:18}})

传入正则,查询name里面有tom的find({name:/tom/})

$where复杂查询,查询年龄等于班级的find({$where:'this.age===this.class'})

  • 更新操作update(conditions, doc, [options])
  1. conditions查询条件,要修改肯定是先查询出某个自己要修改的
  2. doc要修改的字段,类型是对象,可以修改多个
  3. option配置项

  • 更新操作updateMany(conditions, doc, [options])更新多个,其实就是update方法加上multi配置
  • 复杂更新操作可以使用find + save方法进行更新,
let test = mongoose.model('User')
test.find().then(res => {
  res.forEach(item => {
    item.age++
    item.save()
  })
})
  • updateOne(conditions, doc) 只更新找到的第一条数据
  • findOneAndUpdate(conditions, doc, [options]) 只更新找到的第一条数据
  • findByIdAndUpdate(conditions, doc, [options]) 根据id查询并更新找到的那条数据

删除操作

  • remove方法,model上可以直接使用,实例(行,也就是文档,一条完整数据)也可以使用remove方法,在model上使用remove方法时,传入第一个参数为查询条件,删除所有符合条件的结果。在某一实例(行,文档,一条完整数据)可以直接调用remove方法。需要注意的是,model上使用remove方法一定要执行回调,实例上使用可以不需要,这种回调可以直接使用exec()来代替
let test = mongoose.model('User')
test.remove({age:{$gt:10}}).then(res => {
  console.log(res)
})

test.find({age:{$gt:10}}).then(res => {
  res.forEach(item => {
    item.remove()
  })
})
  • findOneAndRemove(conditions) 删除符合结果的第一条数据,回调函数不能省略
  • findByIdAndRemove(conditions) 根据id查询然后删除

mongoose中的钩子函数,也就是回调函数,可以监控增删改查的执行,类似于守卫,又称为中间件

这种守卫有后置守卫和前置守卫只分,post和pre,其实都是增删改查前的回调,这些守卫挂载在Schema上

const mongoose = require('mongoose')
const Schema = mongoose.Schema
let ObjectId = Schema.Types.ObjectId

const CategorySchema = new Schema({
  AdCarouselId: ObjectId,
  title: String,
  createAt: { type: Date, default: Date.now() }
})

CategorySchema.pre('find', () => {
  console.log('heheheh')
})

mongoose.model('goodsCategory', CategorySchema)

mongoose查询后处理方法,类似于第三个配置参数。但是mongoose还非常人性的给我们提供了查询结果处理方法,这样方法在实际的业务开发中非常实用,比如分页的时候就可以使用limit+skip+count来实现

  1. sort方法排序find().sort('age')按age从小到大排序,如果是-age那就是从大到小,也可以多个参数,使用空格隔开
  2. skip方法,跳过个数,显示其他。skip(2)跳过前2个,显示其他的
  3. limit方法,按个数展示
  4. count计数
  5. distinct去重
const router = require('koa-router')()
const mongoose = require('mongoose')

router.get('/getGoodsCateory', async (ctx, next) => {
  // 获取modle
  const goodsCategory = mongoose.model('goodsCategory')
  await goodsCategory.find().skip(1).then((res) => {
    ctx.body = {
      code: 1,
      data: res
    }
  }).catch((err) => {
    ctx.body = {
      code: 0,
      data: err
    }
  })
})

module.exports = router

mongoose文档校验,保证Schema定义好数据的完整性

  • 为什么要文档校验
  1. 保存数据时,不按照schema定义的结构也可以保存成功,少字段多字段都可以,但是多余的字段不会被保存进去,保存的字段类型到了数据库可能发生了变化,比如本来是布尔值,到了数据却成了1
  2. 因此在保存数据的时候对必要字段进行校验是非常必要的,这部分工作在定义schema的时候就可以完成

required: 数据必须填写
default: 默认值
validate: 自定义匹配,可以是函数
min: 最小值(只适用于数字)
max: 最大值(只适用于数字)
match: 正则匹配(只适用于字符串)
enum:  枚举匹配(只适用于字符串)

数据表之间的联系,类似于关系型数据库的联表查询,本质上mongodb是NoSql数据库,不提供这种关联查询,但是mongoose提供了这种操作,其实也是查询的一种封装,只是按你的要求再去查了另一张表

要使用联表查询需要有关联的字段,而且关联的字段只能是每张表的_id字段,然后在Schema定义的时候,使用ref字段验证进行表关联起来,然后在表查询的时候使用populate("ID字段")就可以带回另一张表里面的数据,第二个参数控制返回字段

猜你喜欢

转载自blog.csdn.net/weixin_32682577/article/details/88087955