Skip to content

Mongodb / Mongoose

介绍

KUN Visual Novel 后端整体是采用 koa + mongodb 技术栈的,mongodb 是一个 NoSQL 数据库,与传统的 MySQLPostgreSQL 可能有一些区别。

  • 由于我们是 Nodejs 的后端,所以我们采用 mongoose 来作为 mongodb 的驱动,或许您可能了解 ORMmongoose 就好比 Hibernate, MyBatis 之类的东东
  • mongodb 中,CollectionDocument 可以类比于关系型数据库中的 TableRow
  • 关于 Schema,在 mongodbSchema 是数据库中 Collection 的结构描述,在 mongoose 中,它是一层 ORMmongooseschema 不仅用于描述数据模型的结构,还可以定义数据模型的方法、虚拟字段等。
  • 关于 Model,在 mongodbModelCollection 的抽象,它允许应用程序执行数据库操作。由于 mongoose 的缘故,使用 mongoose.model 可以很方便的使用 Model 与数据库进行交互,它通过定义 Schema和使用模型的方式,将应用程序中的数据映射到数据库文档。
  • 关于数据库范式,由于这是 NoSQL所以关系范式没有啦。因此您可以看到我们的用户 Model 等完全不遵循关系数据库范式。

为什么要选 Mongodb

Mongodb 似乎非常适合论坛这种网站,只是听说

难道就我一个人觉得 Mongo 这个词很瑟琴吗

Mongodb

本项目中我们使用了 Mongodb 作为数据库,它是一个非关系型数据库

可以查看 Mongodb Documentation 来进行了解,这是良好的学习方式

Mongoose

本项目中我们使用了 Mongoose 来在 Nodejs 环境下操作 Mongodb,官网(v8.0.3)的描述是

elegant mongodb object modeling for node.js

可以查看 Mongoose Documentation 来进行了解

使用

本项目中在 src/db/connection.ts 定义了 Mongodb 的连接

typescript
import mongoose from 'mongoose'
import env from '@/config/config.dev'

const DB_URL = `mongodb://${env.MONGO_USERNAME}:${env.MONGO_PASSWORD}@${env.MONGO_HOSTNAME}:${env.MONGO_PORT}/${env.DB_NAME}`

mongoose.connect(DB_URL)

export default mongoose

我们已经在根目录的 .env 文件中定义了所需的环境变量,我们这里将其参数(用户名、密码、HOST、PORT、数据库名)拼合在一起形成了一个 Mongodb 的地址,然后使用 Mongoose 来连接 Mongodb

Models

需要给 Mongoose 定义一层 Model 才能通过 Mongoose 将集合映射到数据库

大白话就是定义一个这样的数据结构,Mongoose 才能把数据写到数据库里

可以查看本项目 src/models 文件夹下,里面存放了所有的 model

Transaction

Transaction 是一个很重要的特性,在 mongodb 中同样存在 TransactionKUN Visual Novel 采用 mongoose,会以如下方式使用 Transaction

typescript
  // Start Transaction
  const session = await mongoose.startSession()
  session.startTransaction()

  try {

   ...some operation

  } catch (error) {
   // If catch error, abort transaction
   await session.abortTransaction()
   session.endSession()
   throw error
  }

Trigger

mongodb 中,没有 trigger,但是 KUN Visual Novel 借助 mongoosepre-save 实现了类似的效果

typescript
// pre-save hook,increase nid before save document
NonMoeSchema.pre('save', increasingSequence('nid'))

Join

mongodb 中没有 join,但是我们可以用类似的方法实现,例如 Embedded, References, Aggregation,由于 KUN Visual Novel 未采用 _id 作为集合的唯一查询标识 (_id 是有的,只是没有用而已),因为我们觉得在浏览器地址栏输入 topics/1 或者 kungalgamer/1,就能进入用户或者话题的主页是一件非常酷的事情,所以我们将每一个 Schema 都添加了一个字段 XXXid,用来标识唯一性。因此我们无法使用 ref 来进行类似于 join 的操作,所以我们使用了 Virtual 来实现类似的操作。

typescript
// Create virtual 'users'

TopicSchema.virtual('user', {
 ref: 'user',
 localField: 'uid',
 foreignField: 'uid',
})

使用的时候只需要

typescript
  const topics = await TopicModel.find(query)
   .sort(sortOptions)
   .skip(skip)
   .limit(limit)
   .populate('user', 'uid avatar name')
   .lean()