意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

mongoose的CRUD

来源:恒创科技 编辑:恒创科技编辑部
2023-12-05 18:32:59

Mongoose 介绍
Mongoose 是 MongoDB 的一个抽象层,有点像是 Express 对 Node 的感觉。

官方文档的两个介绍大概说明了 Mongoose 是干嘛的:

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.

Let’s face it, writing MongoDB validation, casting and business logic boilerplate is a drag. That’s why we wrote Mongoose.


mongoose的CRUD

首先,Mongoose 是一个在异步环境下使用的 MongoDB 对象模型工具,其次,Mongoose 可以减少 MongoDB 验证、类型转换,以及业务逻辑样板的代码量。

关于 MongoDB 的部分,可以看这个——MongoDB 的安装和基础 CRUD,这里要用的也不会超过之前对 MongoDB 的了解了。

安装 Mongoose
Mongoose 在 npm 上有依赖,可以直接用 npm 安装:

npm i mongoose

等下载好了就可以直接在项目里面使用 Mongoose 了。

连接数据库
配置 config.env

config.env 中的配置如下:

# 远程的MongoDB路径,我用的是 Atlas 的免费版进行学习,这点在上面的 MongoDB 文章里提到,感兴趣的可以去看看
DATABASE=mongodb+srv://<USERNAME>:<PASSWORD>@<CLUSTER_ENV>/<DOCUMENTS>?retryWrites=true&w=majority
# 上面用 <> 包起来的变量都会被代替掉,不会放出来的
DB_USERNAME=some-random-username
DB_PASSWORD=some-random-password
CLUSTER_ENV=atlas-cluster-env
DOCUMENTS=atlas-documents

# 也可以使用本地的 Mongodb 进行学习
# 这里的 <DOCUMENTS> 用自己想用的就行了,如果没有的话,MongoDB 会自己创立的
DATABASE_LOCAL=mongodb://localhost:27017/<DOCUMENTS>

连接数据库

目前在 server.js 文件中进行连接

// 导入必要的包
const mongoose = require("mongoose");
const dotenv = require("dotenv");

// dotenv 必须先读取环境配置,等 app 声明调用了再找配置就来不及了
dotenv.config("./config.env");

// 必要的环境配置
const app = require("./app");
// replace 必要的 connection string
// 包括 用户名、密码、cluster、document,确保和云端信息一致
const DB = process.env.DATABASE.replace(
"<USERNAME>",
process.env.DB_USERNAME
);

// 连接 MongoDB
// 云端的连接方法
mongoose
.connect(DB, {
// 一些优化和减少 warning 的配置
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
})
.then(() => {
console.log("DB successfully connected");
});
// 本地的连接方法基本差不多,只是需要把 DB 换成 process.env.DATABASE_LOCAL 就好了
/*
mongoose
.connect(process.env.DATABASE_LOCAL, {
// 一些优化和减少 warning 的配置
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
})
.then(() => {
console.log("DB successfully connected");
});
*/

这个时候在本地运行的 express 服务器就可以连接上远程的数据库了。

创建 Model
这是 Model 层的业务,先创建一个 schema,然后再以 model 的形式导出,让 controllers 使用。

const mongoose = require("mongoose");

// 为了方便,暂时还不会自创 Schema,里面的 type 都是 JS 自己的
const productSchema = new mongoose.Schema({
product: {
type: String,
// 第二个参数会决定 报错信息
required: [true, "This will be a warning message"],
// 目前假设每个产品都有独一无二的名字
unique: true,
trim: true,
},
createdAt: {
// MongoDB 会依照 传过去的毫秒 自动创建当前时间
type: Date,
default: Date.now(),
},
price: {
type: Number,
required: [true, "A product must have price"],
},
});

const Product = mongoose.model("Product", productSchema);

module.exports = Product;

实现 Controller

即实际的业务逻辑,这里假设 routers 里面已经通过 request 调用了 controller 里面的函数。最外层的文件如下:

// 即上文 export 的 Product
const Product = require('../models/productModel');

// 等待实现的 CRUD
exports getAllProducts = async (req, res) => {}

exports getProduct = async (req, res) => {}

exports createProduct = async (req, res) => {}

exports deleteProduct = async (req, res) => {}

exports updateProduct = async (req, res) => {}

Create

即 createProduct 方法。

exports.createProduct = async (req, res) => {
// 从 req.body 里面已经穿来了一个 JSON对象
// 现在暂时是在 postman 里面写死的,之后会写 View 或者用 React 再做一个 VM
try {
const product = await Product.create(req.body);

res.status(201).json({
status: "Success",
data: {
product,
},
});
} catch (err) {
res.status(400).json({
status: "fail",
message: err,
});
}
};

Read

即两个方法:find() 和 findById()。

exports.getAllProducts = async (req, res) => {
try {
const products = await Product.find();
res.status(200).json({
status: "Success",
results: products.length,
data: {
products,
},
});
} catch (err) {
res.status(404).json({
status: "fail",
message: err,
});
}
};

exports.getProduct = async (req, res) => {
try {
const product = await Product.findById(req.params.id);
res.status(200).json({
status: "Success",
data: {
product,
},
});
} catch (err) {
res.status(404).json({
status: "fail",
message: err,
});
}
};

Update

还没有实现 updateMany(),先实现 findByIdAndUpdate(),这个部分的代码是被 patch() 所调用的。

exports.updateProduct = async (req, res) => {
try {
const product = await Product.findByIdAndUpdate(req.params.id, req.body, {
// new 是为了返回更新后的 数据,默认返回是未被更新的数据
new: true,
runValidators: true,
});
res.status(200).json({
status: "Success",
data: {
product,
},
});
} catch (err) {
res.status(404).json({
status: "fail",
message: err,
});
}
};

Delete

即 findByIdAndDelete() 函数。

exports.deleteProduct = async (req, res) => {
try {
await findByIdAndDelete(req.params.id);
} catch (err) {
res.status(204).json({
status: "Success",
});
res.status(404).json({
status: "fail",
message: err,
});
}
};

到这里基础的 CRUD 就全都完成了。

试了之后才发现,真的非常简单。

下一步做的优化就是 findById() 里面传 query param,一些过滤的优化,然后再考虑把 CRUD 的 try{} catch (err) {} 封装一下。

目前的 try/catch 其实也非常简陋,很多判断都没有做。

学习项目的配置

简单地说一下吧,之前好像在那个学习笔记里面有放过……不过现在结构这么简单,有时间找都重新写完了。

之后还是应该抽空把 node 和 express 部分的笔记单独抽离出来。

结构

一个典型的 MVC 结构,如下:

|- src
| |- controllers
| | |- productController.js
| |- models
| | |- productModel.js
| |- routes
| |- app.js
| |- config.env
| |- package.json
| |- server.js

目前 View 还没有开始折腾,现在折腾 API 部分,这也就是为什么开始学 MongoDB 和用 Mongoose 了。

mondels
里面放的就是 MVC 里的 M,即 Model。这里面主要是一些 Schema Definition,定义数据类型、是否是必须的,等一些细节方面的东西。在 Schema 里面定义好了之后,如果在进行 CRUD 的操作时,数据不对的话,就会报错。

所以说在 Mongoose 的介绍里面也说了,写 MongoDB 的验证很烦,主创开发 Mongoose 的原因之一就是为了操作 MongoDB 的时候没这么麻烦。

controllers

处理的是业务逻辑,即传统的 CRUD 操作。

routes

处理的是路由,在 RESTful API 里面,资源的路径名字始终保持一致,根据传来的 HTTP request 去进行 CRUD 的操作。这时候就需要路由去判断传来的 HTTP request,随机调用对应的 controller 里对应的方法。

具体可以查看 IBM 的文档,在 参考 里。

app.js

app 相关的配置,目前负责调用配置一些简单的中间件,以及对 routers 的管理。

config.env

一些配置,包括 MongoDB 网址、用户名、密码之类的会放在这里方便管理。

package.json

server.js

服务器相关的,目前连接 MongoDB 的操作会放在这里。

package.json
在这篇教学里面,除了 Mongoose 之外,还会用到 dotenv 去进行环境配置。

"dependencies": {
"dotenv": "^8.2.0",
"mongoose": "^5.12.3"
}




上一篇: mysql直接拷贝data目录下数据库源文件还原数据库方法 下一篇: JavaScript单线程和任务队列是什么