环境搭建

  • 安装依赖
npm i egg-mongoose --save

配置

// config/plugin.js
exports.mongoose = {
  enable: true,
  package: 'egg-mongoose',
}
// config/config.default.js
exports.mongoose = {
  url: 'mongodb://127.0.0.1:27017/example',
  options: {},
}

使用

// app/model/user.js  user 对 post 一对多
module.exports = (app) => {
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const UserSchema = new Schema({
    name: { type: String },
    age: { type: Number, default: 0 },
    posts: [
      {
        type: Schema.Types.ObjectId,
        ref: 'Post',
      },
    ],
  })

  return mongoose.model('User', UserSchema)
}

// app/model/post.js
module.exports = (app) => {
  const mongoose = app.mongoose
  const Schema = mongoose.Schema

  const PostSchema = new Schema({
    title: { type: String },
    content: { type: String },
  })

  return mongoose.model('Post', PostSchema)
}

mongoose 预定义模式修饰符

  • 预定义模式修饰符: lowercase、uppercase、trim、match、enum、minlength、maxlength、min、max、default、select、validate、get、set, alias,required
const userSchema = mongoose.Schema({
  name: {
    type: String,
    lowercase: true, // 将字符串转换为小写
    trim: true, // 去除字符串两边的空格
  },
  gender: {
    type: String,
    enum: 1 || 2, // 枚举
    default: 1, // 默认值
  },
  status: {
    type: Number,
    min: 0, // 最小值
    max: 1, // 最大值
  },
  age: {
    type: Number,
    min: [18, '未成年'], // 最小值,错误提示
    max: [60, '已退休'], // 最大值,错误提示
  },
})

Getters 与 Setters 自定义修饰符

const userSchema = mongoose.Schema({
  name: {
    type: String,
    lowercase: true,
    trim: true,
    get: (val) => {
      return val + '123'
    },
    set: (val) => {
      return val + '456'
    },
  },
})

mongoose 索引

const DeviceSchema = new mongoose.Schema({
  sn: {
    type: Number,
    unique: true, // 唯一索引
  },
  name: {
    type: String,
    index: true, // 索引
  },
  age: {
    type: Number,
    index: true, // 索引
  },
})

mongoose 内置 curd

// Model.deleteMany(): 删除所有匹配的文档
db.collection.deleteMany({ age: { $gte: 22 } })

// Model.deleteOne(): 删除匹配的第一个文档
db.collection.deleteOne({ age: { $gte: 22 } })

// Model.find(): 查询所有匹配的文档
db.collection.find({ age: { $gte: 22 } })

// Model.findById(): 根据文档的 _id 属性查询文档
db.collection.findById('5f9b0b3b9b9b7c1b4c3b3b3b')

// Model.findByIdAndDelete(): 根据文档的 _id 属性查询文档并删除
db.collection.findByIdAndDelete('5f9b0b3b9b9b7c1b4c3b3b3b')

// Model.findByIdAndRemove(): 根据文档的 _id 属性查询文档并删除
db.collection.findByIdAndRemove('5f9b0b3b9b9b7c1b4c3b3b3b')

// Model.findByIdAndUpdate(): 根据文档的 _id 属性查询文档并更新
db.collection.findByIdAndUpdate('5f9b0b3b9b9b7c1b4c3b3b3b', { age: 23 })

// Model.findOne(): 查询匹配的第一个文档
db.collection.findOne({ age: { $gte: 22 } })

// Model.findOneAndDelete(): 查询匹配的第一个文档并删除
db.collection.findOneAndDelete({ age: { $gte: 22 } })

// Model.findOneAndRemove(): 查询匹配的第一个文档并删除
db.collection.findOneAndRemove({ age: { $gte: 22 } })

// Model.findOneAndReplace(): 查询匹配的第一个文档并替换
db.collection.findOneAndReplace({ age: { $gte: 22 } }, { age: 23 })

// Model.findOneAndUpdate(): 查询匹配的第一个文档并更新
db.collection.findOneAndUpdate({ age: { $gte: 22 } }, { age: 23 })

// Model.replaceOne(): 替换匹配的第一个文档
db.collection.replaceOne({ age: { $gte: 22 } }, { age: 23 })

// Model.updateMany(): 更新所有匹配的文档
db.collection.updateMany({ age: { $gte: 22 } }, { age: 23 })

// Model.updateOne(): 更新匹配的第一个文档
db.collection.updateOne({ age: { $gte: 22 } }, { age: 23 })

扩展 mongoose 静态方法

const userSchema = mongoose.Schema({
  name: {
    type: String,
    lowercase: true,
    trim: true,
    get: (val) => {
      return val + '123'
    },
    set: (val) => {
      return val + '456'
    },
  },
})

// 静态方法
userSchema.statics.findByName = function (name) {
  return this.find({ name: new RegExp(name, 'i') })
}

// 实例方法
userSchema.methods.findByName = function (name) {
  return this.model('User').find({ name: new RegExp(name, 'i') })
}

module.exports = mongoose.model('User', userSchema)

// 使用 静态方法
const User = require('./model/user')
User.findByName('张三').then((res) => {
  console.log(res)
})

// 使用 实例方法
const User = require('./model/user')
const user = new User()
user.findByName('张三').then((res) => {
  console.log(res)
})

mongoose 验证器

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, '用户名不能为空'],
  },
  age: {
    type: Number,
    max: 120,
    min: 0,
  },
  status: {
    type: String,
    enum: ['success', 'error'],
  },
  phone: {
    type: Number,
    match: /^1[3456789]\d{9}$/,
  },
  desc: {
    type: String,
    validate: {
      validator: function (v) {
        return v.length > 10
      },
      message: '描述信息不能少于10个字符',
    },
  },
})

mongoose 简单 curd

// app/controller/user.js
module.exports = (app) => {
  class UserController extends app.Controller {
    // 获取用户列表
    async index() {
      const { ctx } = this
      const users = await ctx.model.User.find({})
      ctx.body = users
    }

    // 创建用户
    async create() {
      const { ctx } = this
      const user = await ctx.model.User.create(ctx.request.body)
      ctx.body = user
    }

    // 更新用户
    async updateUser() {
      const { ctx } = this
      const user = await ctx.model.User.updateOne(
        { _id: ctx.params.id },
        ctx.request.body
      )
      ctx.body = user
    }

    // 删除用户
    async deleteUser() {
      const { ctx } = this
      const user = await ctx.model.User.deleteOne({ _id: ctx.params.id })
      ctx.body = user
    }
  }
  return UserController
}
// app/router.js
module.exports = (app) => {
  app.get('/', 'home.index')
  app.get('/user', 'user.index')
  app.post('/user', 'user.create')
  app.put('/user/:id', 'user.updateUser')
  app.delete('/user/:id', 'user.deleteUser')
}

关联查询

// app/controller/user.js
//  populate: 关联查询
//  ref 关联的模型
// 获取用户列表,关联查询(文章列表)
// 路由:/user -> controller.user.index
async index() {
  const { ctx } = this
  const users = await ctx.model.User.find({}).populate('posts')
  ctx.body = users
}

// 获取用户详情,关联查询(文章详情)
// 路由:/user/:id -> controller.user.show
async show() {
  const { ctx } = this
  const user = await ctx.model.User.findById(ctx.params.id).populate('posts')
  ctx.body = user
}

// 获取用户列表,关联查询 (文章列表,分页) 改进
// 路由:/user -> controller.user.index
async index() {
  const { ctx } = this

  const users = await ctx.model.User.find({}).populate('posts').select('title content')

  // 分页
  const page = Number(ctx.query.page) || 1
  const limit = Number(ctx.query.limit) || 10

  // 关联查询
  const posts = await ctx.model.Post.find({})
    .skip((page - 1) * limit)
    .limit(limit)
    .sort({ _id: -1 }) // 倒序

  // 文章总数
  const total = await ctx.model.Post.countDocuments({})

  ctx.body = {
    users,
    posts,
    postsCount: total
  }
}

聚合查询

// app/controller/user.js

// 获取用户列表,聚合查询(文章列表,分页)
// 路由:/user -> controller.user.index 改进

async index() {
  const { ctx } = this

  // 分页
  const page = Number(ctx.query.page) || 1
  const limit = Number(ctx.query.limit) || 10

  // 聚合查询
  const users = await ctx.model.User.aggregate([
    {
      $lookup: {
        from: 'posts',
        localField: 'posts',
        foreignField: '_id',
        as: 'postsList'
      }
    },
    {
      $project: {
        name: 1,
        age: 1,
        postsList: {
          title: 1,
          content: 1
        }
      }
    },
    {
      $skip: (page - 1) * limit
    },
    {
      $limit: limit
    },
    {
      $sort: {
        _id: -1
      }
    }
  ])

  // 文章总数
  const total = await ctx.model.User.countDocuments({})

  ctx.body = {
    users,
    postsCount: total
  }
}

参考