天天看点

Koa入门(四)Koa 操作数据库

1 NoSql 简介

我们使用

koa

开发后台,最常用的数据库就是

mongodb

,这是

NoSql

数据库类型的一种,那什么是

NoSql

呢?首先需要说的是,

NoSql

并不表示

NO SQL

没有

SQL

的意思。实际上,它是

Not Only SQL

的缩写。它的意义是:适用关系型数据库的时候就使用关系型数据库,不适用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。

关系型数据库中的表都是存储一些结构化的数据,每条记录的字段的组成都一样,即使不是每条记录都需要所有的字段,但数据库会为每条数据分配所有的字段。而非关系型数据库以键值对

(key-value)

存储,它的结构不固定,每一条记录可以有不一样的键,每条记录可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销。

1.1 NoSql数据库优缺点

  • 在优势方面主要体现在下面几点:
    • 简单的扩展
    • 快速的读写
    • 低廉的成本
    • 灵活的数据模型
  • 在不足方面主要有下面几点:
    • 不提供对SQL的支持
    • 支持的特性不够丰富
    • 现有的产品不够成熟

2 Mongoodb

MongoDB

使用

C++

语言编写的非关系型数据库。特点是高性能、易部署、易使用,存储数据十分方便。

2.1 主要特性

  • 面向集合存储,易于存储对象类型的数据
  • 模式自由
  • 支持动态查询
  • 支持完全索引,包含内部对象
  • 支持复制和故障恢复
  • 使用高效的二进制数据存储,包括大型对象
  • 文件存储格式为

    BSON

    (一种

    JSON

    的扩展)

3 Mongodb 安装

有两种安装方式

3.1 Mongodb Atlas

一种是使用

mongodb atlas

,官网注册完,直接本地连接就可以了。缺点是需要文明上网,通常连上了也是很慢。注册地址,教程可以参考这里

Koa入门(四)Koa 操作数据库

另一种是安装到本地(我的是windows10,本地快啊),直接安装 msi,启动后直接连接,大家可以自行网上搜索安装。下载地址

Koa入门(四)Koa 操作数据库

4 Koa + Mongodb 操作

4.1 安装链接

  • npm i mongoose -S

    const mongoose = require('mongoose')

    // 默认 27017 端口

    mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }, () => console.log('数据库连接成功'))

    mongoose.connection.on('error', console.error)像链接地址、端口配置我们最好单独放在配置文件中,更好的维护// app/config.js

    module.exports = {

    connectionStr: 'mongodb://localhost:27017/test'

    }

  • app/index.js

    引入
  • 启动

    mongoodb

    服务
Koa入门(四)Koa 操作数据库
  • 启动

    Koa

    服务
Koa入门(四)Koa 操作数据库

可以看到

koa

链接数据库成功

4.2 创建

user modal

  • 新建

    app/models/user.js

    (使用复数),建立模型const mongoose = require('mongoose')

    const { Schema, model } = mongoose

    // 通过 schema 设计出表结构,简单结构复杂结构都可以,每一条自动创建 _id

    const userSchema = new Schema({

    name: {

    type: String,

    required: true // 必要

    },

    password: {

    type: String,

    required: true

    }

    })

    // User 表

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

  • 操作数据库实现增删改查功能

把我们创建的模型引入到上一节中创建的控制器中

const User = require('../models/user')

class UsersCtl {
  // 获取用户列表
  async find(ctx) {
    // 操作数据库一定要 await
    ctx.body = await User.find()
  }
  // 根据 id 查找某一个用户
  async findById(ctx) {
    ctx.body = await User.findById(ctx.params.id)
  }
  // 创建用户
  async create(ctx) {
    ctx.body = await new User(ctx.request.body).save()
  }
  // 更新用户信息
  async update(ctx) {
    const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body)
    ctx.body = user
  }
  // 删除用户
  async delete(ctx) {
    const user = await User.findByIdAndRemove(ctx.params.id)
    ctx.body = user
  }
}

module.exports = new UsersCtl()           

复制

我们先新增用户再获取和删除,方便测试,使用

postman

测试如下:

增加:

Koa入门(四)Koa 操作数据库

获取列表:

Koa入门(四)Koa 操作数据库

获取某人:

Koa入门(四)Koa 操作数据库

删除某人:

Koa入门(四)Koa 操作数据库
Koa入门(四)Koa 操作数据库

更新用户信息:

Koa入门(四)Koa 操作数据库

4.3 优化逻辑

我们在操作数据库前一定要判断数据的正确性,例如增加要判断是否已有重名,修改要判断是否有这个用户,一些操作还需要权鉴等等。

  • 使用

    koa-parameter

    进行参数校验

    npm i koa-parameter -S

    app/index.js

    ...

    const parameter = require('koa-parameter')

    ...

    // 校验请求体的,放在后面

    app.use(parameter(app)) // 上下文 ctx 增加个方法,全局校验

  • 增删改查增加判断

    大家可自行使用

    postman

    测试以下代码const User = require('../models/user')

    class UsersCtl {

    // 获取用户列表

    async find(ctx) {

    // 操作数据库一定要 await

    ctx.body = await User.find()

    }

    // 根据 id 查找某一个用户

    async findById(ctx) {

    const user = await User.findById(ctx.params.id)

    if (!user) {

    ctx.throw(404, '用户不存在')

    } else {

    ctx.body = user

    }

    }

    // 创建用户

    async create(ctx) {

    // 中间库全局方法校验参数

    ctx.verifyParams({

    name: { type: 'string', required: true },

    password: { type: 'string', required: true }

    })

    // 判断库中是否已存在用户名

    const { name } = ctx.request.body

    const user = await User.findOne({ name })

    if (user) {

    ctx.throw(409, '用户名已存在')

    }

    ctx.body = await new User(ctx.request.body).save()

    }

    // 更新用户信息

    async update(ctx) {

    ctx.verifyParams({

    name: { type: 'string', required: true },

    password: { type: 'string', required: true }

    })

    const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body)

    if (!user) {

    ctx.throw(404, '用户不存在')

    }

    ctx.body = user

    }

    // 删除用户

    async delete(ctx) {

    const user = await User.findByIdAndRemove(ctx.params.id)

    // 删除后,会先把原来的返回下

    if (!user) {

    ctx.throw(404, '用户不存在')

    }

    ctx.body = user

    }

    }

    module.exports = new UsersCtl()

5 mongoodb 其他操作

  • 我们看到列表中有返回密码字段,这是不合适的,容易泄露,所以需要在模型中隐藏。如果需要的话可以在查表时使用

    select

    关键字password: {

    type: String,

    required: true,

    select: false // 不会返回

    }

    await User.find().select(‘+password’)通常使用

    Koa

    会在前端使用

    fields

    字段,后台通过该字段相应返回隐藏字段前端传入格式:password;name;age, ? 拼接 url 后面

    // 获取用户列表

    async find(ctx) {

    const { fields = '' } = ctx.query

    const selectFields = fields.split(';').filter(f => f).map(f => ' +' + f).join('')

    // 操作数据库一定要 await

    ctx.body = await User.find().select(selectFields)

    }async find(ctx) {

    let { fields = '', page = 1, limit = 10 } = ctx.query

    const selectFields = fields.split(';').filter(f => f).map(f => ' +' + f).join('')

    // page 和 limit 不能小于等于 0

    page = Math.max(+page, 1) - 1 // page 前端从 1 开始,后台从 0 开始

    limit = Math.max(+limit, 10)

    // 操作数据库一定要 await

    ctx.body = await User.find().limit(limit).skip(page * limit).select(selectFields)

    }

  • 如果列表过多,我们需要使用分页查找

    mongoodb

    提供了

    limit skip

    字段
  • 列表使用模糊搜索,一个正则搞定cosnt { q = '' } = ctx.query

    await User.find({

    name: new RegExp(q) // 模糊搜索

    })如果多个字段搜索呢?await User.find({ $or: [{title: q}, {name: q}] })

  • 使用引用关联表

    做个关注用户功能,用户模块添加字段// 获取列表会自动返回following字段

    following: {

    type: [

    {

    type: Schema.Types.ObjectId, // 使用 _id 关联

    ref: 'User' // 引用到 User 表

    }

    ]

    }关注控制器// 关注

    async follow(ctx) {

    // 获取自己(正常需要登录的哈, 从 ctx.state.user._id 获取)

    const ownUser = await User.findById(ctx.params.my_id)

    // mongoose 自带的数据类型, 使用toString()方法

    if (!ownUser.following.map(id => id.toString()).includes(ctx.params.id)) {

    ownUser.following.push(ctx.params.id)

    ownUser.save()

    }

    ctx.status = 204

    }使用put方法注册路由router.put('/following/:my_id/:id', follow)使用

    postman

    请求后拉取列表
Koa入门(四)Koa 操作数据库

如果想获取关注者的详细信息使用

populate

关键字:

ctx.body = await User.find({
  name: new RegExp(q) // 模糊搜索
}).limit(limit).skip(page * limit).select(selectFields).populate('following')           

复制

Koa入门(四)Koa 操作数据库

之后想写下实战小例子,小程序或者

pc(vue3)

的配合

Koa

,但是没想好做什么,感兴趣的朋友可以公众号后台留言呦。

如果文章对你有帮助,欢迎分享到朋友圈!谢谢阅读!