【node.js】MongoDB

MongoDB

非关系型数据库,存储的数据都是 key-value 的形式

安装数据库
  • 官网下载,然后配置环境变量即可
  • 可在小黑屏输入 mongo 连接数据库,以查看是否安装成功
  • 我们可以自己手动开关数据库:
    window + r → 输入 services.msc 打开任务管理器 → 找到 MongoDB 数据库,右键 启动 / 停止
基本概念
  1. 数据库,可以理解为一个对象
  2. 表:也叫 “集合”,就是数据库对象的属性
  3. 文档:就是表中的数据,即数组的一项
常用命令
  1. show dbs:查询所有数据库
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
  1. db:查看当前数据库
> db
test

可以看见,当前使用的数据库为 test;但是,因为现在 test 数据库中没有数据,所以不会被 show dbs 查询出来

  1. use 数据库名:切换数据库;如果找不到该数据库,则会创建该数据库
  2. show collections:展示当前数据库中的表(集合)
  3. db.表名.find():查询该表中的数据(文档)
  4. db.dropDatabase():删除当前数据库

mongoose

node.js 的第三方模块 mongoose,用于操作 MongoDB 数据库

  1. 初始化项目:npm init -y
  2. 下载 mongoose 模块:npm i mongoose
  3. 引入 mongoose 模块:const mongoose = require('mongoose')
  4. 连接数据库:
// localhost - 数据库的 ip 地址
// 27017 - MongoDB 的端口号,默认为 27017
// node - 数据库名,表示使用该数据库;没有则自动创建
mongoose.connect('mongodb://localhost:27017/node').then(_ => {
    console.log("连接成功!");
}, reason => {
    console.log("连接失败!", reason);
});
  1. 断开数据库连接:mongoose.disconnect()
可通过监听 open & close 事件,来监听数据库的连接与断开
mongoose.connection.once('open', () => {
    console.log("数据库已连接");
});
mongoose.connection.once('close', () => {
    console.log("数据库已断开");
});

Schema 模式

Schema 是 mongoose 的一个方法,可以设计表结构,用于创建 model 模型

MongoDB 支持的数据类型:
  • String、Number、Boolean、Date、Array、ObjectID、Buffer、Map、Schema
  • 添加数据时,如果数据类型写错了,MongoDB 会进行隐式类型转换;如果转换不成功,则抛出错误

  • MongoDB 没有 Object 类型,如果强行设置为 Object 类型,会转为 Array 类型

// 创建 Schema 实例
const studentSchema = new mongoose.Schema({
    name: String,
    age: Number,
    course: Array,
});
// 创建 Student 集合
const StudentModel = mongoose.model('Student', studentSchema);
配置 Schema
  • timestamps:为 true 时,会添加 createdAt & updatedAt 两个字段,代表 [创建时间] & [更新时间]
  • versionKey:为 false 时,不会添加版本号 __v 字段
const studentSchema = new mongoose.Schema({
    name: String,
    age: Number,
    course: Array,
}, {
    timestamps: true,
    versionKey: false
});

注意:我们要在创建表时就确定表结构,确定后不要轻易更改!

表约束

通用约束

  1. require:Boolean;是否必需填写;如果设置为 true,没填写时会抛出错误
    可以写为 require: [true, "message"]:后面的 “message” 是报错信息
  2. default:默认值。可以是函数,如果是函数,则默认值为函数的返回值
  3. validate:自定义验证。有一个 validator 方法 & 一个 message 属性
    validator 的参数为当前字段;return true 表示通过验证,return false 则抛出错误,错误信息为 message
const studentSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true,
        validate: {
            validator(name) {
                if (name.length <= 5) return true;
                return false;
            },
            message: "name is too long"
        }
    },
    age: {
        type: Number,
        required: [true, "age is required"],
        default: 21
    },
    course: {
        type: Array,
        default: ["HTML", "CSS", "JS"]
    },
});

String 约束

  1. lowercase:Boolean;小写
  2. uppercase:Boolean;大写
  3. trim:Boolean;去除空格
  4. minlength:Number;最小长度
  5. maxlength:Number;最大长度
  6. enum:Array;只能填写 Array 中的元素
  7. match:Regex;需匹配正则
const studentSchema = new Schema({
    name: {
        type: String,
        trim: true,
        enum: ["superman", "superwoman"]
    },
    age: Number,
    course: Array
});

Number 约束

  1. min:最小数值
  2. max:最大数值
const studentSchema = new Schema({
    name: String,
    age: {
        type: Number,
        min: 18
    },
    course: Array
});

Array [元素的] 约束

  1. type:设置数组元素的类型(可用类型参考 MongoDB 支持的数据类型);只设置 type 时可直接写属性值
const studentSchema = new Schema({
    name: String,
    age: Number,
    course: [String] // 会将元素隐式转换为 String 类型,转不了则报错
});
  1. minlength / maxlength:设置数组元素的长度
const studentSchema = new Schema({
    name: String,
    age: Number,
    course: [{
        type: String,
        minlength: 4 // 元素的最小长度为 4
    }]
});
  • 可以发现,其实就是将配置对象写到 [] 里面啦~

添加数据

save

  • 创建模型实例 StudentModel,调用 save 方法:
new StudentModel({
    name: "小陈",
    age: 20,
    course: ["C++", "Java"]
}).save((err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

打印的数据:

result {
  name: '小陈',
  age: 20,
  course: [ 'C++', 'Java' ],
  _id: new ObjectId("622b317092def5225f02c6b7"),
  __v: 0
}

可以看见,MongoDB 自动给我们添加了 _id 属性,这是数据的唯一标识

create

model.create(dataObj, (err, result) => {})
StudentModel.create({
    name: "小黄",
    age: 21,
    course: ["HTML", "CSS", "JS"]
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
添加多个数据:model.create(dataObj1, dataObj2, (err, result1, result2) => {})
StudentModel.create({ // 传入多个对象
    name: "小胡",
    age: 20,
    course: ["MongoDB"]
}, {
    name: "小傅",
    age: 20,
    course: ["Go"]
}, (err, result1, result2) => { // 接收会多个对象参数
    if (err) console.log("err", err);
    else {
        console.log("result1", result1);
        console.log("result2", result2);
    }
});
添加多个数据:model.create([dataObj1, dataObj2], (err, resultArr) => {})
StudentModel.create([{ // 传入一个数组
    name: "小郑",
    age: 20,
    course: ["MySQL"]
}, {
    name: "小仲",
    age: 22,
    course: []
}], (err, result) => { // 接收回一个数组参数
    if (err) console.log("err", err);
    else console.log("result", result);
});

查询数据

find

查询所有数据:model.find((err, result) => {}) 以数组的形式返回表中的数据;无数据则返回空数组
StudentModel.find((err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
根据条件查询:模型.find(conditions, (err, result) => {})
  • 模型.find({key: value}, (err, result) => {})
StudentModel.find({
    age: 21
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • 使用正则匹配
StudentModel.find({
    name: /小傅/
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • 使用 $gt大于、$gte大于等于、$lt小于、$lte小于等于、$ne不等于
StudentModel.find({
    age: { $gte: 21 } // 年龄 >= 21 的
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $exists:存在某属性
StudentModel.find({
    name: { $exists: false } // 没有设置 name 属性的
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

注意:如果是一个不存在的字段,比如 gender,不论 $exists 为何值,都会返回所有的数据

StudentModel.find({
    gender: { $exists: true }
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $size数组长度匹配
StudentModel.find({
    course: { $size: 2 }
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $in$nin:在、不在指定的多个字段之内
StudentModel.find({
    name: { $in: ["小谢", "小胡"] } // 名字在 ["小谢", "小胡"] 里面的
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $all数组中是否存在指定的所有项
StudentModel.find({
    course: { $all: ["C++", "Java"] }
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $or$nor:或者、或者取反
StudentModel.find({
    $or: [{ name: "小陈" }, { age: 22 }] // 名字为 [小陈] 或者 age 为 [22] 的
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $where:可以使用 JS 代码 / 函数
StudentModel.find({
    $where: "this.age === 20"
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
返回指定字段:model.find(conditions, projection, (err, result) => {})
  • { name: 1 }:只显示 name 属性 & _id 属性
    { age: 1, _id: 0 }:只显示 name 属性
StudentModel.find(null, { name: 1, _id: 0 }, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
设置返回的数据格式:模型.find(conditions, projection, options, (err, result) => {})
  • { sort: { age: 1 } }:按照 age 项,升序排列;-1 则降序排序
  • { skip: 2 }:略过前 2 条数据
  • { limit: 5 }:最多返回 5 条数据
StudentModel.find(null, null, { sort: { age: 1 }, limit: 3 }, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

findOne

返回第一个查找到的数据,无数据则返回 null

StudentModel.findOne((err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

findbyId

根据 id 查询:model.findById(id, (err, result) => {}) 返回指定数据,不存在该数据则返回 null

StudentModel.findById("6229fd57809fbf2dc34f83dd", (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

更改数据

updateOne

更改一个数据:model.updateOne(condition, newData, callback)

StudentModel.updateOne({ name: "小仲" }, {
    course: ["摸鱼"]
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

updateMany

更改多个数据:模型.updateMany(condition, newData, callback)

StudentModel.updateMany({ name: "小黄" }, {
    $inc: { age: 1 } // age + 1
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

findByIdAndUpdate

根据 id 更改数据:模型.findByIdAndUpdate(id, newData, (err, oldData)=>{})

StudentModel.updateOne("622aea67f487ff334ba925d4", {
    // 删除 age 字段,属性值随便写,反正都被删除了
    $unset: { age: 0 }
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
更新数组元素
  • $push: { arr: XXX }:为数组添加元素 XXX
StudentModel.updateMany({ name: "小黄" }, {
    $push: { course: "Vue" } // 为数组添加元素
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $addToSet: { arr: XXX }:为数组添加元素 XXX,如果 XXX 已存在则不添加
StudentModel.updateOne({ name: "小黄" }, {
    $addToSet: { course: "React" } // 为数组添加元素,如果已存在则不添加
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $pop: { arr: 1 }:删除数组的最后一项;如果设置为 -1 则删除第一项
StudentModel.updateOne({ name: "小黄" }, {
    $pop: { course: 1 } // 删除数组的最后一项;值为 -1 则删除第一项
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
  • $pull: { arr: XXX }:删除数组所有值为 XXX 的元素
StudentModel.updateOne({ name: "小黄" }, {
    $pull: { course: "class" } // 删除数组的所有 class 值
}, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

删除数据

remove

删除所有数据:model.remove((err, result) => {})
StudentModel.remove((err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});
根据条件删除:model.remove({key, value}, (err, result) => {})
StudentModel.remove({ name: "小胡" }, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

findByIdAndRemove

根据 id 删除:model.findByIdAndRemove(id, (err, result) => {})

StudentModel.findByIdAndRemove("6229febe332cf70aa225c6b4", (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

deleteOne

删除一个数据:model.deleteOne(condition, (err, result) => {})

Student.deleteOne({ name: "小傅" }, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

deleteMany

删除多个数据:model.deleteMany(condition, (err, result) => {})

Student.deleteMany({ name: "小郑" }, (err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result);
});

中间件

  • 前后钩子即 pre()post() 方法
  • 可以在执行以下操作时设置前后钩子:initvalidatesaveremovecount
    findfindOnefindOneAndRemovefindOneAndUpdateinsertManyupdate
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/demo').then(_ => {
    console.log("连接成功!"); // 2
}, reason => {
    console.log("连接失败!", reason);
});

// 创建 Schema 模式实例
const studentSchema = new mongoose.Schema({
    name: String,
    age: Number,
}, { versionKey: false });

// 设置钩子函数
studentSchema.pre('findOne', next => {
    console.log("pre"); // 1
    next();
});
studentSchema.post('findOne', res => {
    console.log("post", "res", res); // 3
});

// 创建表模型
const StudentModel = mongoose.model('Student', studentSchema);

// 调用 findOne 方法
StudentModel.findOne((err, result) => {
    if (err) console.log("err", err);
    else console.log("result", result); // 4
});

多连接

  • mongoose.createConnection() 返回链接对象
const mongoose = require('mongoose');

// 创建多连接
const con1 = mongoose.createConnection('mongodb://localhost:27017/demo');
const con2 = mongoose.createConnection('mongodb://localhost:27017/demo');

无论是使用 mongoose.connect / mongoose.createConnection 创建的连接
其连接池默认只能连接 5 个,可通过 poolSize 设置

mongoose.createConnection(url, {
    poolSize: 4 // 设置连接池的连接数为 4
});

表关联

const mongoose = require("mongoose");

mongoose.connect('mongodb://localhost:27017/node').then(_ => {
    console.log("连接成功!");
}, reason => {
    console.log("连接失败!", reason);
});

const Schema = mongoose.Schema;

const studentSchema = new Schema({
    name: String,
    age: Number,
    course: Array
});
const StudentModel = mongoose.model("Student", studentSchema);

const scoreSchema = new Schema({
    pass: Boolean,
    author: [{ // 关联 StudentModel
        type: Schema.Types.ObjectId,
        ref: StudentModel,
    }]
})
const ScoreModel = mongoose.model("Score", scoreSchema);

// ScoreModel.create({
//     pass: true,
//     author: '622b321bbeaecc99728c70ee' // 这里填入某条记录的 _id
// }, (err, result) => {
//     if (err) console.log("err", err);
//     else console.log("result", result);
// })

ScoreModel.find().populate("author").exec((err, result) => {
    if (err) console.log("err", err);
    else {
        console.log("result", result[0]);
        console.log("author", result[0].author[0]);
    }
});

session & MongoDB

<body>
    <h1 id="bb">名字h1>
    <button id="box">点击button>
body>

<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
    box.onclick = () => {
        axios({
            method: "post",
            url: "/register",
            data: { name: 'superman' }
        }).then(res => console.log("data", res.data));
    };

    // 免登录
    (() => {
        axios({ url: "/getSession" }).then(res => {
            console.log("data", res.data);
            bb.innerText = res.data.value
        })
    })();
script>
const express = require("express");
const app = express();
app.listen(8080, () => console.log("http://127.0.0.1:8080"));

app.use(express.static("./public"));

// 解析 post 的数据
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 使用 mongoose
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/node")
    .then(() => console.log("连接成功!"))
    .catch(() => console.log("连接失败!"));
let userSchame = new mongoose.Schema({
    name: String,
    pass: String,
});
let UserModel = mongoose.model("User", userSchame);

// 将 session 放入数据库
const session = require("express-session"); // 用于操作 session
const MongoStore = require("connect-mongo"); // 用于存储 session 到 MongoDB
app.use(session({
    secret: 'keyboard cat', // 加密的字符串,里面内容可以随便写
    resave: false, // 强制保存 session,即使它没变化
    saveUninitialized: true, // 强制将未初始化的 session 存储,默认为 true
    cookie: { // 配置 cookie
        maxAge: 1000 * 60 * 60 * 24, // 有效期为 1 天
        secure: false, // 是否仅 https 可用
    },
    store: MongoStore.create({ mongoUrl: 'mongodb://localhost:27017/node' }) // 存入 MongoDB
}));

// 配置 session
app.post("/register", (req, res) => {
    req.session.user = req.body; // 添加 session 属性
    console.log("session", req.session); // 获取 session
    res.send("设置 session")
})

// 免登录
app.get("/getSession", async (req, res) => {
    console.log("name", req.session.name);
    if (req.session.user) {
        // 在 MongoDB 中查询、获取用户数据
        let result = await UserModel.findOne({ name: "superman" });
        res.send({
            code: 1,
            value: req.session.user.name, // 获取 session 属性
            result
        });
    } else {
        res.send({ code: 0, value: "过期了" });
    }
});

你可能感兴趣的:(NodeJS,笔记,mongodb,node.js,数据库)