# MongoDB

# 1. 数据库概述及环境搭建

# 1.1 为什么要使用数据库

  • 动态网站中的数据都是存储在数据库中的
  • 数据库可以用来持久存储客户端通过表单收集的用户信息
  • 数据库软件本身可以对数据进行高效的管理

# 1.2 什么是数据库

  • 数据库即存储数据的仓库,可以将数据进行有序的分门别类的存储。它是独立于语言之外的软件,可以通过数据库软件提供的 API 去操作数据。
  • 常见的数据库软件有:mysql、mongoDB、oracle。

# 1.3 MongoDB 数据库下载安装

下载地址:https://www.mongodb.com/download-center/community (opens new window)

# 1.4 MongoDB 可视化软件

MongoDB Compass 可视化操作软件,是使用图形界面操作数据库的一种方式。

# 1.5 数据库相关概念

在一个数据库软件中可以包含多个数据仓库,在每个数据仓库中可以包含多个数据集合,每个数据集合中可以包含多条文档(具体的数据)。

术语 解释说明
database 数据库,mongoDB 数据库软件中可以建立多个数据库
collection 集合,一组数据的集合,可以理解为 JavaScript 中的数组
document 文档,一条具体的数据,可以理解为 JavaScript 中的对象
field 字段,文档中的属性名称,可以理解为 JavaScript 中的对象属性

# 1.6 Mongoose 第三方包

使用 Node.js 操作 MongoDB 数据库需要依赖 Node.js 第三方包 mongoose

npm install mongoose -g

# 1.7 启动 MongoDB

在命令行工具中运行下面的命令即可启动 MongoDB,否则 MongoDB 将无法连接。

net start mongodb

# 1.8 数据库连接

使用 mongoose 提供的 connect 方法即可连接数据库。

mongoose
  .connect("mongodb://localhost/playground", {
    useNewUrlParser: true,
    useUnifiedTopology: true
  })
  .then(() => console.log("数据库连接成功"))
  .catch((err) => console.log("数据库连接失败", err));

# 1.9 创建数据库

在 MongoDB 中不需要显式创建数据库,如果正在使用的数据库不存在,MongoDB 会自动创建。

# 2. MongoDB 增删改查操作

# 2.1 创建集合

创建集合分为两步,一是对对集合设定规则,二是创建集合,创建 mongoose. Schema 构造函数的实例即可创建集合。

// 1.创建集合规则
const courseSchema = new mongoose.Schema({
  name: String,
  author: String,
  isPublished: Boolean
});
// 2.创建符合集合规则的构造函数
const Course = mongoose.model("Course", courseSchema); //courses

# 2.2 创建文档(增)

创建文档实际上就是向集合中插入数据。分为两步:

  1. 应用集合规则创建集合实例。
  2. 调用实例对象下的 save()方法将数据保存到数据库中。

// 创建集合实例
const course = new Course({
  name: "Node.js course",
  author: "itheima",
  tags: ["node", "backend"],
  isPublished: true
});
// 将数据保存到数据库中
course.save();

也可使用集合构造函数本身的 create 方法创建文档

  • create(object, callback) 回调函数形式

Course.create(
  {
    name: "javascript基础",
    author: "pink",
    isPublished: false
  },
  (err, result) => {
    console.log(err);
    console.log(result);
  }
);
  • create(object).then().catch() promise 对象形式

Course.create({
  name: "javascript应用",
  author: "pink",
  isPublished: true
})
  .then((doc) => console.log(doc))
  .catch((err) => console.log(err));

# 2.3 mongoDB 数据库导入数据(增)

找到 mongodb 数据库的安装目录,将安装目录下的 bin 目录放置在环境变量中。

$mongoimport –d 数据库名称 –c 集合名称 –file 要导入的数据文件

# 2.4 查询文档(查)

//  根据条件查找文档(条件为空则查找所有文档)
Course.find().then((result) => console.log(result));
//  通过字段查找
Course.findOne({
  name: "node.js基础"
}).then((result) => console.log(result));
//  匹配大于 小于
User.find({
  age: {
    $gt: 20,
    $lt: 50
  }
}).then((result) => console.log(result));
//  匹配包含
User.find({
  hobbies: {
    $in: ["敲代码"]
  }
}).then((result) => console.log(result));
//  选择要查询的字段
User.find()
  .select("name email")
  .then((result) => console.log(result));
// 将数据按照年龄进行排序
User.find()
  .sort("age")
  .then((result) => console.log(result));
//  skip 跳过多少条数据  limit 限制查询数量
User.find()
  .skip(2)
  .limit(2)
  .then((result) => console.log(result));

# 2.5 删除文档(删)

// 删除单个
Course.findOneAndDelete({
  _id: "5c09f267aeb04b22f8460968"
}).then((result) => console.log(result));
// 删除多个
User.deleteMany({}).then((result) => console.log(result));

# 2.6 更新文档(改)

// 更新单个
User.updateOne(
  {
    查询条件
  },
  {
    要修改的值
  }
).then((result) => console.log(result));
// 更新多个
User.updateMany(
  {
    查询条件
  },
  {
    要更改的值
  }
).then((result) => console.log(result));

# 2.7 规则验证

在创建集合规则时,可以设置当前字段的验证规则,验证失败就则输入插入失败。

required: true 必传字段
minlength: 3 字符串最小长度
maxlength: 20 字符串最大长度
min: 2 数值最小为2
max: 100 数值最大为100
enum: ['html', 'css', 'javascript', 'node.js']
trim: true 去除字符串两边的空格
validate: 自定义验证器
default: 默认值
//定义集合规则
const postSchema = new mongoose.Schema({
  // 定义验证规则
  title: {
    type: String,
    // 此字段是否必须,必须
    required: [true, "\n请传入文章标题"],
    minlength: [2, "\n文章长度不能小于2"],
    maxlength: [5, "\n文章长度不能超过5"],
    // 去除字符串两边的空格
    trim: true
  },
  age: {
    type: Number,
    min: 18,
    max: 100
  },
  publishData: {
    type: Date,
    // 默认值
    default: Date.now
  },
  categories: -{
    type: String,
    // 枚举,列出当前字段可以拥有的值
    enum: {
      values: ["html", "css", "javascript", "node.js"],
      message: "\n传入的分类名称在列表中不存在"
    }
  },
  author: {
    type: String,
    // 自定义规则
    validate: {
      validator: (v) => {
        // 返回布尔值
        return v && v.length > 4;
      },
      // 自定义错误信息
      message: "\n传入的值不符合验证规则"
    }
  }
});
// 创建集合
const Post = mongoose.model("Post", postSchema); //Posts

获取错误信息:error.errors['字段名称'].message

Post.create({
  title: "aa",
  age: 60,
  categories: -"htm",
  author: "pik"
})
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    // 获取错误信息对象
    const erro = error.errors;
    // 循环错误信息对象
    for (attr in erro) {
      console.log(erro[attr]["message"]);
    }
  });

# 2.7 集合关联

通常不同集合的数据之间是有关系的,例如文章信息和用户信息存储在不同集合中,但文章是某个用户发表的,要查询文章的所有信息包括发表用户,就需要用到集合关联。

使用 id 对集合进行关联 使用 populate 方法进行关联集合查询

// 定义用户集合规则
const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  }
});
// 定义文章集合规则
const postSchema = new mongoose.Schema({
  title: {
    type: String
  },
  author: {
    // 使用ID将文章集合和作者集合进行关联
    type: mongoose.Schema.Types.ObjectId,
    ref: "User"
  }
});
// 创建用户集合
const User = mongoose.model("User", userSchema);
// 创建文章集合
const Post = mongoose.model("Post", postSchema);
//联合查询
Post.find()
  .populate("author")
  .then((err, result) => console.log(result));

# 3. 案例 – 学生档案管理

# 3.1 案例介绍

目标:模板引擎应用,强化 node.js 项目制作流程。

知识点:http 请求响应、数据库、模板引擎、静态资源访问。

# 3.2 制作流程

建立项目文件夹并生成项目描述文件

创建网站服务器实现客户端和服务器端通信

连接数据库并根据需求设计学员信息表

创建路由并实现页面模板呈递

实现静态资源访问

实现学生信息添加功能

实现学生信息展示功能

npm ls -g --depth=0     #列出全局安装的第三方模块
npm install -g treer     #安装treer第三方模块
treer -i "node_modules"   #排除node_modules文件夹,生成目录
├─app.js 主服务
├─package-lock.json 依赖锁定文件
├─package.json 依赖描述文件
├─x.txt 项目描述文件
├─views 视图模板
|   ├─index.art
|   └-list.art
├─route 路由
|   └index.js
├─public 静态资源
|   ├─css
|   |  ├─list.css
|   |  └-main.css
├─model 模块
|   ├─connect.js
|   └-student.js
//connect.js
const mongoose = require("mongoose");
// 连接数据库
mongoose
  .connect("mongodb://localhost/playground", {
    useNewUrlParser: true
  })
  .then(() => console.log("数据库连接成功"))
  .catch(() => console.log("数据库连接失败"));
//students.js
const mongoose = require("mongoose");
// 创建学生集合规则
const studentsSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
    minlength: 2,
    maxlength: 10
  },
  age: {
    type: Number,
    min: 10,
    max: 50
  },
  sex: {
    type: String
  },
  email: String,
  hobbies: [String],
  collage: String,
  enterDate: {
    type: Date,
    default: Date.now
  }
});
// 创建学生信息集合
const Student = mongoose.model("Student", studentsSchema);
// 将学生信息集合进行导出
module.exports.Student = Student;
// app.js
// 引入http模块
const http = require("http");
// 引入模板引擎;
const template = require("art-template");
// 引入path模块;
const path = require("path");
// 引入静态资源访问模块
const serveStatic = require("serve-static");
// 引入处理日期的第三方模块
const dateformat = require("dateformat");
// 配置template的根目录
template.defaults.root = path.join(__dirname, "views");
// 给模板引擎导入日期格式模块
template.defaults.imports.dateformat = dateformat;
// 创建网站服务器
const app = http.createServer();
// 引入路由功能模块
const router = require("./route/index.js");
// 引入数据库连接模块;
require("./model/connect.js");
// 实现静态资源访问服务
const serve = serveStatic(path.join(__dirname, "public"));
// 当客户端访问服务器端的时候
app.on("request", (req, res) => {
  // 启用路由功能
  router(req, res, () => {});
  // 启用静态资源访问服务功能
  serve(req, res, () => {});
});
// 监听80端口
app.listen(80);
console.log("服务器启动成功");

# 3.3 第三方模块 router

功能:实现路由 使用步骤: 获取路由对象 调用路由对象提供的方法创建路由 启用路由,使路由生效

// route.js
// 引入router模块
const getRouter = require("router");
// 获取路由对象
const router = getRouter();
// 引入模板引擎
const template = require("art-template");
// 引入querystring模块
const querystring = require("querystring");
// 引入用户集合文件
const Student_model = require("../model/student");
const Student = Student_model.Student;
// 引入url模块
const url = require("url");
// 呈现默认页提示信息
router.get("/", (req, res) => {
  let html = `
  <p>您来到了互联网的荒原:</p>
  <p>/add   学生档案信息添加页面</p>
  <p>/list   学生档案信息列表页面</p>
  `;
  res.writeHead(200, {
    "content-type": "text/html;charset=utf-8"
  });
  res.end(html);
});
// 呈递学生档案信息添加页面
router.get("/add", (req, res) => {
  let html = template("index.art", {});
  res.end(html);
});

// 呈递学生档案信息列表页面
router.get("/list", async (req, res) => {
  // 查询学生信息
  let students = await Student.find();
  let html = template("list.art", {
    students: students
  });
  res.end(html);
});
// 实现学生信息删除功能路由
router.get("/remove", async (req, res) => {
  let { query } = url.parse(req.url, true);
  let temp = query.id.replace(/"|'/g, "");
  await Student.findOneAndDelete({
    _id: temp
  });
  res.writeHead(301, {
    Location: "/list"
  });
  res.end();
});
// 实现学生信息添加功能路由
router.post("/add", (req, res) => {
  // 接收post请求参数
  let formData = "";
  req.on("data", (param) => {
    formData += param;
  });
  req.on("end", async () => {
    // 将用户的提交信息解析为对象结构
    let student = querystring.parse(formData);
    // 如果未设置用户名则不添加数据
    if (student.name == "") {
      res.writeHead(400, {
        "content-type": "text/html;charset=utf-8"
      });
      res.end("请填写用户名");
    }
    if (student.name) {
      await Student.create(student);
      res.writeHead(301, {
        Location: "/list"
      });
      res.end();
    }
  });
});
module.exports = router;

# 3.4 第三方模块 serve-static

功能:实现静态资源访问服务 步骤: 引入 serve-static 模块获取创建静态资源服务功能的方法 调用方法创建静态资源服务并指定静态资源服务目录 启用静态资源服务功能

const serveStatic = require("serve-static");
const serve = serveStatic("public");
server.on("request", () => {
  serve(req, res);
});
server.listen(3000);