Node.js --- 详解MongoDB与Mongoose

1. MongoDB的下载与配置

1.1 下载

使用MongoDB需要下载其对应版本的服务端与客户端

服务端:Mongod

https://www.mongodb.com/try/download/community-kubernetes-operator

客户端:使用Mongosh

MongoDB Shell Download | MongoDB

1.2 配置MongoDB

MongoDB的数据会默认存储在'C:/data/db'中,所以用户需要在C盘中创建data文件夹,其中再创建db文件夹

在MongoDB中包含多个数据库,数据库包含多个集合collection,每一个集合包含了多个文档,在操作mongdb中需要进入到相应的数据库,然后针对于对于collection进行操作

使用MongoDB时,需要保证服务端和客户端同步打开,使用cmd命令行启动

2. 数据库命令

以下是在MongoDB客户端中常见的数据库命令操作:

2.1 数据库操作

显示所有数据库

bash 复制代码
show dbs

切换数据库

bash 复制代码
use myDatabase

如果数据库不存在,会在插入数据时自动创建。

bash 复制代码
db

显示当前所在数据库

bash 复制代码
use 库名
db.dropDatabase()

删除当前数据库

2.2 集合命令操作

创建集合

bash 复制代码
db.createCollection('集合名称')

显示当前数据库中所有的集合

bash 复制代码
show collections

删除集合

bash 复制代码
db.集合名.drop()

集合重命名

bash 复制代码
db.集合名.renameCollection('名称')

2.3 文档命令

以myCollection集合为例

插入数据

bash 复制代码
db.myCollection.insertOne({ name: "Alice", age: 25, city: "New York" })

插入多个文档

bash 复制代码
db.myCollection.insertMany([
  { name: "Bob", age: 30, city: "San Francisco" },
  { name: "Charlie", age: 35, city: "Los Angeles" }
])

2.4 查询数据

查询所有文档

bash 复制代码
db.myCollection.find()

按条件查询

bash 复制代码
db.myCollection.find({ age: { $gt: 25 } })  # 查询年龄大于25的文档

运算符

在mongodb中不能直接使用<,>,<=,>=等符号,需要使用替代符号

|-----|------|
| > | $gt |
| < | $lt |
| >= | $gte |
| <= | $lte |
| !== | $ne |

逻辑运算

逻辑或

bash 复制代码
db.collectionName.find({$or:[{age:{$lt:30}},{age:{$gt:40}}]})

逻辑与

bash 复制代码
db.collectionName.find({$and:[{age:{%lt:20}},{age:{$gt:15}}]})

更新数据

bash 复制代码
db.myCollection.updateOne({ name: "Alice" }, { $set: { age: 26 } })
bash 复制代码
db.myCollection.updateMany({ city: "New York" }, { $set: { city: "NYC" } })

删除数据

删除单个文档

bash 复制代码
db.myCollection.deleteOne({ name: "Bob" })

删除多个文档

bash 复制代码
db.myCollection.deleteMany({ age: { $lt: 30 } })  # 删除年龄小于30的文档

3. 基于Node.js的驱动 - Mongoose

Mongoose是构建在MongoDB驱动之上的更高级的抽象库

简化了复杂查询和数据操作,支持中间件,钩子等高级功能

3.1 连接MongoDB

在javascript中实现操作MongoDB数据库需要与之进行连接,默认端口为27017,下划线后声明操作的数据库名称

javascript 复制代码
mongoose.connect("mongodb://127.0.0.1:27017/test")

设置事件以及相应的回调函数

javascript 复制代码
mongoose.connection.on('open',()=>{console.log('连接成功')});
mongoose.connection.on('error',()=>{console.log('Error')})
mongoose.connection.on('close',()=>{console.log('链接关闭')})

分别为数据库连接事件,连接失败事件,数据库关闭事件,以及对应的回调函数

设置回调关键字也可以使用once,事件回调函数只执行一次

javascript 复制代码
mongoose.connection.once('open',async ()=>{})

async声明一个异步函数,最终会返回一个Promise对象,函数内部可以使用await来等待异步操作完成。

断开数据库连接,在正式运行时一般不使用该操作

javascript 复制代码
mongoose.disconnect()

3.2 创建文档的结构对象

3.2.1 定义构造器

mongoose.Schema是Mongoose提供的一个构造器,用于定义MongoDB集合中文档的结构,以及字段的验证规则

javascript 复制代码
const BookSchema = new mongoose.Schema({
    name: { type: String, required: true },   // 书名,类型是字符串,必填
    author: { type: String, required: true }, // 作者,类型是字符串,必填
    price: { type: Number, required: true },  // 价格,类型是数字,必填
    pub_time: { type: Date, required: true }, // 出版时间,类型是日期,必填
});
字段类型

|------------|-------------------------------------------|
| 类型 | 描述 |
| String | 字符串 |
| Number | 数字 |
| Boolean | 布尔值 |
| Array | 数组 |
| Date | 日期 |
| Buffer | Buffer对象 |
| Mixed | 任意类型,mongoose.Schema.Types.Mixed指定 |
| ObjectId | 对象ID,mongoose.Schema.Types.ObjectId指定 |
| Decimal128 | 高精度数字, mongoose.Schema.Types.Decimal128指定 |

字段值验证

必填项:

javascript 复制代码
name:{type:String,required:true}

默认值:

javascript 复制代码
name:{type:String, default:'default'}

枚举值:

设置的值必须是数组当中的

javascript 复制代码
name:{type:String,enum:['male','female']}

唯一值:

表示这个值是唯一的,不能产生重复的

javascript 复制代码
name:{type:String, unique:true}

注意:永远不要相信用户的输入!!!

3.2.2 模型对象

模型对象是对MongoDB集合的封装,提供了用于操作集合的方法,增删改查。

javascript 复制代码
const BookModel = mongoose.model("books", BookSchema);

·第一个参数:表示数据库中集合的名称,mongoose会使用集合名称的复数创建集合

·第二个参数:之前所有定义的Schema,用于描述集合中文档的结构

3.3 模型方法

方法中的语句与MongoDB操作语句别无二致,但需要使用到模型对象进行对应操作

插入文档

  • 创建文档实例并保存

只会创建一个文档实例,并不会自动将其保存到数据库

javascript 复制代码
const book = new BookModel({数据})

显式调用 save() 方法才能将该实例保存到数据库中

javascript 复制代码
await book.save()
  • 快速创建插入
javascript 复制代码
        const data = await BookModel.create({
            name: "西游记",
            author: "吴承恩",
            price: 19.9,
            pub_time:new Date()
        });

删除文档

javascript 复制代码
await BookModel.deleteOne({ name: "红楼梦" }); // 删除匹配的单个文档
await BookModel.deleteMany({name:'西游记'})//删除多个文档

更新文档

javascript 复制代码
await BookModel.updateOne({ name: "西游记" }, { price: 22.0 }); // 更新价格
await BookModel.updateMany({ name: "西游记" }, { price: 22.0 }); // 更新价格

读取文档

javascript 复制代码
const book = await BookModel.findOne({ price: { $gte: 20 } }); // 查找单个价格大于等于20的书
const books = await BookModel.find({price:{$gte:20}}) //批量查找
const books = await BookModel.find();

个性化读取

字段筛选
javascript 复制代码
const results = await BookModel.find({}, "name price"); // 只返回 name 和 price

使用字符串定义需要返回的字段属性

数据排序
javascript 复制代码
Model.find(filter).sort({ field: 1 or -1 })

1表示升序;-1表示降序

数据截取

limit():限制查询结果的数量

javascript 复制代码
const results = await BookModel.find().limit(5); // 获取前 5 条文档
console.log(results);

skip():跳过指定数量的文档,常用于分页查询

javascript 复制代码
const page = 2; // 当前页数
const limit = 3; // 每页显示的文档数量

const results = await BookModel.find()
    .skip((page - 1) * limit)
    .limit(limit);

console.log(results);

4. 示例:模拟注册登陆系统

4.1 前端页面

login.html:

html 复制代码
<!DOCTYPE html>>
<head>
    <title>登陆页面</title>
</head>
<body>
    <form method="post" action="/dashboard">
            <!-- 姓名 -->
            <label for="name">姓名</label>
            <input type="text" id="name" name="name" placeholder="请输入你的姓名" required><br><br>
                
            <!-- 年龄 -->
            <label for="age">年龄</label>
            <input type="number" id="age" name="age" placeholder="请输入你的年龄" min="0" required><br><br>
                
            <!-- 提交按钮 -->
            <button type="submit">提交</button>
    </form>
</body>

register.html:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Train Page</title>
</head>
<body>
    <h1>用户信息注册</h1>
    <form method="post" action="/save">
        <!-- 姓名 -->
        <label for="name">姓名</label>
        <input type="text" id="name" name="name" placeholder="请输入你的姓名" required><br><br>
        
        <!-- 年龄 -->
        <label for="age">年龄</label>
        <input type="number" id="age" name="age" placeholder="请输入你的年龄" min="0" required><br><br>
        
        <!-- 提交按钮 -->
        <button type="submit">提交</button>
    </form>
</body>
</html>

page.html:

html 复制代码
<!DOCTYPE html>
<head>
    <title>主页面</title>
</head>
<body>
    <h1>欢迎访问主页</h1>
    <a href="/login">登录</a>
    <a href="/register">注册</a>
</body>

4.2 后端数据存储与验证

javascript 复制代码
const bodyParser = require('body-parser');
const express = require('express');
const mongoose = require('mongoose');
const path = require('path');

const app = express();

// 配置 body-parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 连接 MongoDB
mongoose.connect('mongodb://127.0.0.1:27017/test', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

mongoose.connection.once('open', () => {
    console.log('数据库连接成功');
});

// 定义用户 Schema 和模型
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, min: 0, required: true },
});

const User = mongoose.model('User', userSchema);

// 主页面
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname,"page.html"));
});

// 注册页面
app.get('/register', (req, res) => {
    res.sendFile(path.join(__dirname, 'register.html'));
});

// 登录页面
app.get('/login', (req, res) => {
    res.sendFile(path.join(__dirname, 'login.html'));
});

// 保存用户信息
app.post('/save', async (req, res) => {
    try {
        const { name, age } = req.body;
        const newUser = new User({ name, age });
        console.log('保存用户:', name, age);
        await newUser.save();
        res.send('<h1>保存用户成功</h1><a href="/">返回主页</a>');
    } catch (err) {
        console.error('保存用户失败:', err);
        res.status(500).send('<h1>保存失败</h1><a href="/">返回主页</a>');
    }
});

// 登录验证
app.post('/dashboard', async (req, res) => {
    try {
        const { name, age } = req.body;

        // 查找用户
        const user = await User.findOne({ name });
        if (!user) {
            return res.status(400).send('<h1>用户不存在</h1><a href="/login">返回登录</a>');
        }

        // 验证年龄
        if (parseInt(age) === user.age) {
            res.send('<h1>登录成功</h1><a href="/">返回主页</a>');
        } else {
            res.status(400).send('<h1>年龄错误,登录失败</h1><a href="/login">返回登录</a>');
        }
    } catch (err) {
        console.error('登录失败:', err);
        res.status(500).send('<h1>登录失败</h1><a href="/login">返回登录</a>');
    }
});

// 启动服务
app.listen(3000, () => {
    console.log('服务启动,监听端口 3000');
});
相关推荐
拾忆,想起42 分钟前
深入浅出负载均衡:理解其原理并选择最适合你的实现方式
分布式·后端·微服务·负载均衡
燕双嘤1 小时前
Require:利用MySQL binlog实现闪回操作
数据库·mysql
小扬的马甲1 小时前
postgresql分区表相关问题处理
数据库·postgresql
uzong2 小时前
大模型给我的开发提效入门篇
人工智能·后端
uzong2 小时前
在mac上搭建一个安卓开发环境
后端
uzong2 小时前
新公司在使用的 Hibernate Validator 框架
java·后端
这猪好帅2 小时前
【Redis】初识Redis
数据库·redis·缓存
网络安全-老纪2 小时前
网络安全的几种攻击方法
网络·数据库·web安全
dgiij2 小时前
node.js的进程保活
后端·node.js·bash
蒜蓉大猩猩3 小时前
Node.js - Express框架
后端·架构·node.js·express