系列文章
- node.js express框架开发入门教程
- express+mongoDB开发入门教程之mongoDB安装
- express+mongoDB开发入门教程之mongoose使用讲解
文章目录
- 系列文章
- 前言
- 一、Mongoose是什么?
- 二、Mongoose安装
- 三、Mongoose在express项目中使用
- 步骤一、连接mongoDB数据库
- 步骤二、建立模型
- 步骤三、增删改查操作
- 1、 新增数据
- 2、更新数据
- 3、删除数据
- 4、查找数据
- 筛选条件
- 1、运算符(><=!==)
- 2、枚举值 $in
- 3、逻辑运算$or/and
- 4、正则匹配
- 5、排序sort
- 6、限制个数limit
- 7、跳过个数skip
- 8、设置数据返回字段select
- 9、查询总数量count
- demo演示:
前言
本文将讲述Mongoose的入门开发教程,以及通过一些示例演示对mongoDB数据库的增删改查操作。
一、Mongoose是什么?
Mongoose是一个强大的Node.js库,它提供了对MongoDB数据库的操作,有了Mongoose实际开发中对MongoDB增删改查将变得很容易。
二、Mongoose安装
npm install mongoose -S
目前mongoose最新版本是v8.0,要求安装node v16以上版本,不同的mongoose版本一些api使用方式是有区别的,8.0操作上把旧版本的回调函数改成promise( aysnc/await)形式
三、Mongoose在express项目中使用
在项目根目录新建model文件夹放置Mongoose相关文件,model目录下新建db.js文件
步骤一、连接mongoDB数据库
假设连接到名为system数据库
db.js文件写入
const mongoose = require("mongoose");
//数据库连接地址+数据库名称(system)
const DBURL='mongodb://127.0.0.1:27017/system'
//连接数据库
mongoose.connect(DBURL).then(res=>{
console.log('连接成功')
}).catch(e=>{
console.log('连接失败')
})
module.exports = mongoose;
通过API mongoose.connect 连接数据库,其中system为想要连接的数据库名称,返回值Promise,通过then/catch或async/await监听连接失败或成功。当然也可以通过监听连接事件connected和error来判断,需要注意的是数据库名即使不存在也能连接成功,在mongoDB中不存在的库或者集合在进行数据添加操作的时候会自动生成,最后导出mongoose供后续创建模型使用。
连接成功与否也可以通过监听连接事件:
mongoose.connection.on('connected',()=>{
console.log('数据库连接成功')
})
mongoose.connection.on('error',err=>{
console.log('数据库连接失败')
})
其他连接事件还有:
- connecting:初始连接时发出
- open:在所有该连接的模型上执行 ‘connected’ 和 onOpen 后发出
- disconnecting:断开连接发出
- close:成功关闭连接
- reconnected:重新连接成功发出
步骤二、建立模型
模型可以理解成每个文档字段配置,建立模型就是对字段属性进行设置,生成一个模型对象,后续增删改查操作将基于该模型对象进行方法调用
假设现在有个管理后台要存储用户信息,信息包括姓名、年龄、性别3个字段,集合名称为user
在model目录下新建user.js文件
写入:
const db = require("./db");
//定义结构
let userSchema = db.Schema(
{
name: {
type:String,
required:true
},
age: Number,
sex: {
type:String,
default:'未知'
},
},
{ versionKey: false }//隐藏_v字段
);
//创建模型
let userModel = db.model("user", userSchema, "user");
module.exports = userModel;
先通过mongoose.Schema定义文档字段配置,再调用mongoose.model生成模型对象,最后导出模型对象供增删改查操作使用
mongoose.Schema 类似vue props可以设置字段类型、是否必填、默认值等
type:有效类型有如下:
- 字符串 String
- 数字 Number
- 日期 Date
- 缓冲 Buffer
- 布尔值 Boolean
- 混合 Schema.Types.Mixed
- ObjectId Schema.Types.ObjectId
- 数组 []
- 十进制 128 Schema.Types.Decimal128
- 映射 Map
- 结构 subSchema
- UUID Schema.Types.UUID
- BigInt BigInt
required:是否必填true/false
default:默认值
需要注意的是我们发现上面代码db.model(“user”, userSchema, “user”)生成模型时候传入3个参数其中第一个和第三个参数都是user。user为要操作的集合(表)名称,如果不设置第三参数类似db.model(“user”, userSchema)最终操作的是users集合,因为mongoose会默认在参数后面加s,实际变成users,好在提供第三个参数,我们可以通过第三个参数设置真实集合名称。
给集合添加一个新文档默认会生成_id和_v字段,想要隐藏_v字段可以设置 versionKey: false
除了以上几个属性还有更多配置属性可以查看官网SchemaType
步骤三、增删改查操作
增删改查操作都是基于步骤二导出的模型进行函数调用,常用的命令可以总结如下表所示
API | 说明 |
---|---|
Model.create() | 新增一条数据 |
Model.deleteOne() | 删除一条数据 |
Model.deleteMany() | 删除多条数据 |
Model.updateOne() | 修改一条数据 |
Model.updateMany() | 修改多条数据 |
Model.find() | 查找满足条件所有数据 |
Model.findOne() | 查找满足条件第一条数据 |
Model.findById() | 通过id查找一条数据 |
Model.findOneAndUpdate() | 查找一条数据修改并返回 |
下面所有示例将在routers/users.js文件内进行编写,该路由前缀为users,并在app.js注册,模型userModel由上面步骤二所创建
1、 新增数据
方法一:Model.create()
//导入模型
var userModel = require("../model/user");
//新增接口
router.post("/create", async (req, res) => {
try {
await userModel.create({ name:'李磊', age:20, sex:'男' });
res.send({
code: 200,
msg: "success",
});
} catch (e) {
res.send({
code: 500,
msg: e.message || "fail",
});
}
});
方法二:Document.save()
//导入模型
const userModel = require("../model/user");
//新增接口
router.post("/create", async (req, res) => {
try {
let doc=new userModel({ name:'李磊', age:20, sex:'男' })
await doc.save()
res.send({
code: 200,
msg: "success",
});
} catch (e) {
res.send({
code: 500,
msg: e.message || "fail",
});
}
});
请求接口:
数据库数据:
动态获取新增参数示例:
//导入模型
const userModel = require("../model/user");
//新增接口
router.post("/create", async (req, res) => {
//获取请求参数,请求数据为json格式
let { name, age, sex } = req.body;
try {
await userModel.create({ name, age, sex });
res.send({
code: 200,
msg: "success",
});
} catch (e) {
res.send({
code: 500,
msg: e.message || "fail",
});
}
});
执行新增请求:
数据库数据
ps:上面请求新增时候我们并没有传sex值,由于在创建模型时候sex设置了默认值为"未知"所以当请求没有传值默认保存为"未知",并且数据库默认会为每条数据创建一个_id字段作为唯一标识
2、更新数据
更新单条数据:
例如:修改李磊年龄为22岁
userModel.updateOne({ name: "李磊" }, { age: 22 });
更新多条数据
例如:修改所有年龄为22岁的用户为男性
userModel.updateMany({ age: 22 }, { sex:"男性" });
动态传参更新
通过传入id,更新其他字段值
//导入模型
const userModel = require("../model/user");
//更新
router.post("/update", async (req, res) => {
let {id,name,age,sex}=req.body
if(!id){
res.send({
code: 500,
msg: "id不能为空",
});
return;
}
try {
await userModel.updateOne({ _id: id }, {name,age,sex });
res.send({
code: 200,
msg: "success",
});
} catch (e) {
res.send({
code: 500,
msg: e.message || "fail",
});
}
});
接口请求测试
修改李磊年龄为100,性别为女
数据库数据:
ps:Model.updateOne/Many(condition,newVal),第一个参数为查询条件,为对象可以设置多个属性,第二个为更新的值也是个对象可以设置多个属性
3、删除数据
删除单条
例如:删除李磊用户
userModel.deleteOne({ name: "李磊" });
删除多条
例如:删除所有男性用户
userModel.deleteMany({ sex: "男性" });
4、查找数据
查找单条数据:
let data=await userModel.findOne({ sex: "男性" });//查找第一条性别为男的数据
let data2=await userModel.findById("6594c18f112053f96104b7a1");//通过id查找数据
查找所有满足条件数据
//查找年龄为10岁,性别为男性的所有数据
let data=await userModel.find({age:10, sex: "男性" });
筛选条件
增加筛选条件方法比较多,这里就挑一些比较常用讲解
1、运算符(><=!==)
//查找年龄大于10岁,小于30岁数据
let data=await userModel.find({age:{$gt:10,$lt:30}});
也可以用链接语法构建查询
let data=await userModel.find().where('age').gt(10).lt(33).exec();
上面两句是等价的。
所有运算符如下:
运算符 | 说明 |
---|---|
$gt | 大于> |
$gte | 大于等于>= |
$lt | 小于< |
$lt | 小于等于<= |
$ne | 不等于!= |
2、枚举值 $in
//查找性别为男性或男或未知的所有数据
let data=await userModel.find({ sex: { $in: ['男性','男','未知'] }});
链接语法构建查询
let data=await userModel.find().where('sex').in(['男性','男', '未知']);
3、逻辑运算$or/and
//查找年龄为10岁或者20岁数据
let data=await userModel.find({$or: [{age:10}, {age:20}]});
//查找年龄为10岁且为男性数据
let data=await userModel.find({ $and: [{age:10}, {sex:"男性"}]});
4、正则匹配
//查找名字带小的所有数据
let data=await userModel.find({name:/小/});
5、排序sort
//查找所有数据按年龄正序排序返回
let data=await userModel.find().sort({ age: 1 });
//查找所有数据按年龄倒序排序返回
let data=await userModel.find().sort({ age: -1 });
ps:1正序,-1倒序
6、限制个数limit
//返回10个数据
let data=await userModel.find().limit(10);
7、跳过个数skip
//跳过前100个返回第101-110位置数据
let data=await userModel.find().skip(100).limit(10);
8、设置数据返回字段select
//返回数据只有name、age字段
let data=await userModel.find().select('name age');
9、查询总数量count
//返回数据总个数
let data=await userModel.find().count();//2
demo演示:
下面结合开发实际场景写一个例子来演示下筛选条件使用
假设我们要实现上面一个列表页,其中姓名支持模糊搜索,带分页功能
var express = require("express");
var router = express.Router();
//导入模型
const userModel = require("../model/user");
//分页查询接口
router.post("/findByPage", async (req, res) => {
let { name, age, sex, page, size } = req.body;
//查询参数过滤掉值为undefined或null或空值属性
let params = Object.entries({ name, age, sex }).reduce((prev, cur) => {
let [key, value] = cur;
if (value!==undefined&&value!==null&&value!=='') {
if (key === "name") {
//名字模糊搜索正则处理
prev[key] = new RegExp(value);
} else {
prev[key] = value;
}
}
return prev;
}, {});
try {
let query = userModel.find(params);
if (page && size) {
//获取分页数据
query.skip((page - 1) * size).limit(size);
}
let data = await query.exec();//分页数据
let total= await userModel.find(params).count();//总条数
res.send({
code: 200,
data,
total,
msg: "success",
});
} catch (e) {
res.send({
code: 500,
msg: e.message || "fail",
});
}
});
现有数据库数据:
测试请求数据
1.分页
2.模糊搜索
3.其他
ps:查询条件某字段值如果是undefined或者空值''或者null条件参数JSON对象要过滤掉该属性否者查出来数据为空
至此最基本入门技能——增删改查操作已经介绍完,更多细节、技巧和api请查阅官方文档 https://mongoose.nodejs.cn/docs/queries.html