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');
});
相关推荐
大鸡腿同学几秒前
【成长类】《只有偏执狂才能生存》读书笔记:程序员的偏执型成长地图
后端
0xDevNull10 分钟前
MySQL数据冷热分离详解
后端·mysql
研究点啥好呢16 分钟前
Github热门项目推荐 | 创建你的像素风格!
c++·python·node.js·github·开源软件
AI袋鼠帝18 分钟前
OpenClaw(龙虾)最强开源对手!Github 40K Star了,又一个爆火的Agent..
后端
科技小花26 分钟前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸28 分钟前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain30 分钟前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希1 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神1 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员1 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全