1. MongoDB 数据库的相关概念

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

| 术语 | 解释说明 | | —- | —- |

| database | 数据库,mongoDB数据库软件中可以建立多个数据库 |

| collection | 集合,一组数据的集合,可以理解为JavaScript中的数组 |

| document | 文档,一条具体的数据,可以理解为JavaScript中的对象 |

| field | 字段,文档中的属性名称,可以理解为JavaScript中的对象属性 |

1.1 MongoDB 第三方包 mongoose

使用Node.js操作MongoDB数据库需要依赖Node.js第三方包mongoose
使用npm install mongoose命令下载

1.2 MongoDB 的启动与关闭

  • 在命令行工具中运行net start mongoDB即可启动MongoDB,否则MongoDB将无法连接。
  • 在命令行工具中运行 net stop mongoDB 停止MongoDB

1.3 MongoDB 的链接

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

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

1.4 创建数据库

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

2.MongoDB 增删改查

2.1 创建集合

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

  1. // 设定集合规则
  2. const courseSchema = new mongoose.Schema({
  3. name: String,
  4. author: String,
  5. isPublished: Boolean
  6. });
  7. // 创建集合并应用规则(集合名称,应用的规则),返回的是构造函数
  8. const Course = mongoose.model('Course', courseSchema); // courses

2.2 创建文档

第一种方法

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

  • 1.创建集合实例。
  • 2调用实例对象下的save方法将数据保存到数据库中。 ``` // 创建集合的实例 const course = new Course({ name: ‘Node.js course’, author: ‘黑马讲师’, tags: [‘node’, ‘backend’], isPublished: true }); // 将数据保存到数据库中 course.save();
  1. ##### 第二种方法

//通过回调函数接收返回数据 Course.create({name: ‘JavaScript基础’, author: ‘黑马讲师’, isPublish: true}, (err, result) => { // 错误对象 console.log(err) // 当前插入的文档 console.log(result) });

  1. ```
  2. //通过 Promise 对象接收返回数据
  3. Course.create({name: 'JavaScript基础', author: '黑马讲师', isPublish: true})
  4. .then(result => console.log(result))
  5. .catch(err => console.log(err))

2.3 mongoDB数据库导入数据

  • mongoimport –d 数据库名称 –c 集合名称 —file 要导入的数据文件
  • 找到mongodb数据库的安装目录,将安装目录下的bin目录放置在环境变量中。

    2.4 查询文档

find

  1. // 根据条件查找文档(条件为空则查找所有文档)
  2. Course.find().then(result => console.log(result))
  1. // 返回文档集合(数组)
  2. [{
  3. _id: 5c0917ed37ec9b03c07cf95f,
  4. name: 'node.js基础',
  5. author: '黑马讲师‘
  6. },{
  7. _id: 5c09dea28acfb814980ff827,
  8. name: 'Javascript',
  9. author: '黑马讲师‘
  10. }]

findOne

  1. // 根据条件查找文档
  2. Course.findOne({name: 'node.js基础'}).then(result => console.log(result))
  1. // 返回文档(对象)
  2. {
  3. _id: 5c0917ed37ec9b03c07cf95f,
  4. name: 'node.js基础',
  5. author: '黑马讲师‘
  6. }

匹配查询

需要先补齐 集合规则 与 集合 才能进行查询

  1. const userSchema = new mongoose.Schema({
  2. name: String,
  3. age: String,
  4. email: String,
  5. password: String,
  6. hobbies: [String]
  7. })
  8. const User = mongoose.model('User', userSchema);
  1. // 匹配大于 小于
  2. //$gt : > , $lt : <
  3. User.find({age: {$gt: 20, $lt: 50}}).then(result => console.log(result))
  1. // 匹配包含 $in
  2. User.find({hobbies: {$in: ['敲代码']}}).then(result => console.log(result))
  1. // 选择要查询的字段
  2. User.find().select('name email').then(result => console.log(result))
  1. // 将数据按照年龄进行排序,(升序)
  2. User.find().sort('age').then(result => console.log(result))
  3. // 将数据按照年龄进行排序,(降序)
  4. User.find().sort('-age').then(result => console.log(result))
  1. // skip 跳过多少条数据 limit 限制查询数量
  2. User.find().skip(2).limit(2).then(result => console.log(result))

2.5 删除文档

  1. // 删除单个,返回删除的数据
  2. Course.findOneAndDelete({}).then(result => console.log(result))
  3. // 删除多个,{}不填默认删除所有
  4. User.deleteMany({}).then(result => console.log(result))

2.6 更新文档

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

2.6 mongoose 验证

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

  1. required: true 必传字段
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. // [是否必填,自定义错误信息]
  5. required: [true, '请传入文章标题']
  6. }
  7. })
  1. minlength:3 字符串最小长度
  2. maxlength: 20 字符串最大长度
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. required: [true, '请传入文章标题'],
  5. minlength: 2,
  6. //[长度,自定义错误信息]
  7. maxlength: [5,'文章长度不能超过5']
  8. }
  9. })
  1. min: 2 数值最小为2
  2. max: 100 数值最大为100
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. required: [true, '请传入文章标题'],
  5. minlength: 2,
  6. maxlength: 5,
  7. trim: true
  8. },
  9. age: {
  10. type: Number,
  11. //也可以自定义错误信息,同上
  12. min: 18,
  13. max: 100
  14. }
  15. })
  1. enum: [‘html’, ‘css’, ‘javascript’, ‘node.js’]
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. required: [true, '请传入文章标题'],
  5. minlength: 2,
  6. maxlength: 5,
  7. trim: true
  8. },
  9. age: {
  10. type: Number,
  11. min: 18,
  12. max: 100
  13. },
  14. category: {
  15. type: String,
  16. //传入值必须为enum中包含的值
  17. enum: ["Html", "JavaScript", "Node.js"]
  18. }
  19. })
  1. trim: true 去除字符串两边的空格
  2. validate: 自定义验证器
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. required: [true, '请传入文章标题'],
  5. minlength: 2,
  6. maxlength: 5,
  7. trim: true
  8. }
  9. author: {
  10. type: String,
  11. validate: {
  12. validator: (v) => {
  13. //v表示要验证的值, 返回布尔值,true 验证成功,false 验证失败,
  14. return v && v.legth > 4
  15. },
  16. //自定义错误信息
  17. message: '传入的值不符合验证规则'
  18. }
  19. }
  20. })
  1. default: 默认值
  1. const postSchema = new mongoose.Schema({
  2. title: {
  3. type: String,
  4. required: [true, '请传入文章标题'],
  5. minlength: 2,
  6. maxlength: 5,
  7. trim: true
  8. },
  9. age: {
  10. type: Number,
  11. min: 18,
  12. max: 100
  13. },
  14. publishDate: {
  15. //类型为日期类型
  16. type: Date,
  17. //默认值为当期日期
  18. default: Date.now
  19. }
  20. })

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

  1. Post.create({ title: 'bbb', age: 20, category: "html", author: 'bd' })
  2. .then(res => {
  3. console.log(res);
  4. })
  5. .catch(error => {
  6. //获取错误信息对象
  7. const err = error.errors;
  8. //循环错误信息对象
  9. for (var item in err) {
  10. //打印错误信息
  11. console.log(err[item].message);
  12. }
  13. })

2.7 集合关联

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

  • 使用id对集合进行关联
  • 使用populate方法进行关联集合查询 ``` // 用户集合 const User = mongoose.model(‘User’, new mongoose.Schema({ name: { type: String } })); // 文章集合 const Post = mongoose.model(‘Post’, new mongoose.Schema({ title: { type: String }, // 使用ID将文章集合和作者集合进行关联 author: { type: mongoose.Schema.Types.ObjectId, ref: ‘User’ } }));

//创建用户 // User.create({ name: ‘itheima’ }).then(res => { // console.log(res); // })

//创建文章 // Post.create({ title: ‘123’, author: ‘5e8d617a10b7d10cb4fac26f’ }).then(res => { // console.log(res); // })

//联合查询 Post.find() .populate(‘author’) .then((err, result) => console.log(result));

  1. ##### 一个增删改查的小案例:

const http = require(‘http’) const mongoose = require(“mongoose”) const url = require(“url”) const querystring = require(‘querystring’)

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

//创建用户集合规则 const UserSchema = new mongoose.Schema({ name: { type: String, required: true, maxlength: 20, minlength: 2 }, age: { type: Number, max: 80, min: 18 }, password: String, email: String, hobbies: [String] }) //创建集合 返回集合构造函数 const User = mongoose.model(“User”, UserSchema)

//创建服务器 const app = http.createServer()

//为服务器对象添加请求事件 app.on(“request”, async (req, res) => { //请求方式 const method = req.method; //请求地址 const { pathname, query } = url.parse(req.url, true);

  1. if (method == 'GET') {
  2. //呈现用户列表页面
  3. if (pathname == "/list") {
  4. //查询用户信息
  5. let users = await User.find();
  6. console.log(users);
  7. //html字符串
  8. let list = `
  9. <!DOCTYPE html>
添加用户
; //对数据进行循环 users.forEach(item => { list +=}) list +=
用户名 年龄 爱好 邮箱 操作
${item.name} ${item.age}
; item.hobbies.forEach(i => { list += ${i} }) list +=
${item.email} 删除 修改
res.end(list) } else if (pathname == '/add') { let add = <!DOCTYPE html>

添加用户

; res.end(add); } else if (pathname == '/modify') { let user = await User.findOne({ _id: query.id }); let hobbies = ['足球', '篮球', '橄榄球', '敲代码', '抽烟', '喝酒', '烫头']; console.log(user); let modify = <!DOCTYPE html>

修改用户

; hobbies.forEach(item => { let isHobby = user.hobbies.includes(item); if (isHobby) { modify += } else { modify += } }) modify +=
`; res.end(modify) } else if (pathname == “/remove”) { // res.end(query.id) await User.findOneAndDelete({ _id: query.id }); res.writeHead(301, { Location: “/list” }) res.end() } } else if (method == “POST”) { //用户添加功能 if (pathname == “/add”) { //接收用户提交的信息 let formData = ‘’; //接收post参数 req.on(“data”, param => { formData += param; }) //post参数接收完毕 req.on(“end”, async () => { // console.log(querystring.parse(formData)); let users = querystring.parse(formData); //将用户提交的数据添加到数据库中 await User.create(users); //重定向 res.writeHead(301, { Location: “/list” }) res.end(); }) } else if (pathname == “/modify”) { //接收用户提交的信息 let formData = ‘’; //接收post参数 req.on(“data”, param => { formData += param; }) //post参数接收完毕 req.on(“end”, async () => { // console.log(querystring.parse(formData)); let users = querystring.parse(formData); //将用户提交的数据添加到数据库中 await User.updateOne({ _id: query.id }, users); //重定向 res.writeHead(301, { Location: “/list” }) res.end(); }) } }}) //监听端口 app.listen(3000) ### 3. 模板引擎 #### 3.1 模板引擎 模板引擎是第三方模块。<br /> 让开发者以更加友好的方式拼接字符串,使项目代码更加清晰、更加易于维护。 #### 3.2 art-template 模板引擎 - 1. 在命令行工具中使用 npm install art-template 命令进行下载 - 2. 使用const template = require('art-template')引入模板引擎 - 3. 告诉模板引擎要拼接的数据和模板在哪 const html = template(‘模板路径’, 数据); - 4. 使用模板语法告诉模板引擎,模板与数据应该如何进行拼接 #### 3.3 art-template代码示例 // 导入模板引擎模块 const template = require(‘art-template’); // 将特定模板与特定数据进行拼接 const html = template(‘./views/index.art’,{ data: { name: ‘张三’, age: 20 } }); ``` // views/index.art 文件 <div> <span>{{data.name}}</span> <span>{{data.age}}</span> </div> ### 4. 模板引擎语法 #### 4.1 模板语法 1. art-template同时支持两种模板语法:标准语法和原始语法。 1. 标准语法可以让模板更容易读写,原始语法具有强大的逻辑处理能力。 > 标准语法: {{ 数据 }}
原始语法:<%=数据 %> #### 4.2 输出 将某项数据输出在模板中,标准语法和原始语法如下: > 标准语法:{{ 数据 }}
原始语法:<%=数据 %> <!-- 标准语法 --> <h2>{{value}}</h2> <h2>{{a ? b : c}}</h2> <h2>{{a + b}}</h2> <!-- 原始语法 --> <h2><%= value %></h2> <h2><%= a ? b : c %></h2> <h2><%= a + b %></h2> #### 4.3 原文输出 如果数据中携带HTML标签,默认模板引擎不会解析标签,会将其转义后输出。 - 标准语法:{{@ 数据 }} - 原始语法:<%-数据 %> <!-- 标准语法 --> <h2>{{@ value }}</h2> <!-- 原始语法 --> <h2><%- value %></h2> #### 4.4 条件判断 <!-- 标准语法 --> {{if 条件}} ... {{/if}} {{if v1}} ... {{else if v2}} ... {{/if}} <!-- 原始语法 --> <% if (value) { %> ... <% } %> <% if (v1) { %> ... <% } else if (v2) { %> ... <% } %> #### 4.5 循环语法 - 标准语法:{{each 数据}} {{/each}} - 原始语法:<% for() { %> <% } %> <!-- 标准语法 --> {{each target}} {{$index}} {{$value}} {{/each}} <!-- 原始语法 --> <% for(var i = 0; i < target.length; i++){ %> <%= i %> <%= target[i] %> <% } %> #### 4.6 子模板 使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。 - 标准语法:{{include ‘模板’}} - 原始语法:<%include(‘模板’) %> <!-- 标准语法 --> {{include './header.art'}} <!-- 原始语法 --> <% include('./header.art') %> #### 4.7 模板继承 使用模板继承可以将网站HTML骨架抽离到单独的文件中,其他页面模板可以继承骨架文件。 ``` //Html骨架模板 layout.art <!doctype html> {{block ‘head’}}{{/block}} {{block ‘content’}}{{/block}}
  1. ```
  2. <!--index.art 首页模板-->
  3. <!-- 继承骨架模板 -->
  4. {{extend './layout.art'}}
  5. {{block 'head'}} <link rel="stylesheet" href="custom.css"> {{/block}}
  6. {{block 'content'}} <p>This is just an awesome page.</p> {{/block}}

5 . 案例

5.1 案例介绍 – 学生档案管理

目标:模板引擎应用,强化node.js项目制作流程。
知识点:http请求响应、数据库、模板引擎、静态资源访问。
image-20200410163741420image-20200410163752537

5.2 制作流程

  1. 建立项目文件夹并生成项目描述文件
  2. 创建网站服务器实现客户端和服务器端通信
  3. 连接数据库并根据需求设计学员信息表
  4. 创建路由并实现页面模板呈递
  5. 实现静态资源访问
  6. 实现学生信息添加功能
  7. 实现学生信息展示功能

    5.3 第三方模块 router

    功能:实现路由
    使用步骤:
  • 获取路由对象
  • 调用路由对象提供的方法创建路由
  • 启用路由,使路由生效
  1. const getRouter = require('router')
  2. const router = getRouter();
  3. router.get('/add', (req, res) => {
  4. res.end('Hello World!')
  5. })
  6. server.on('request', (req, res) => {
  7. router(req, res)
  8. })

5.4 第三方模块 serve-static

功能:实现静态资源访问服务
步骤:

  • 引入serve-static模块获取创建静态资源服务功能的方法
  • 调用方法创建静态资源服务并指定静态资源服务目录
  • 启用静态资源服务功能 ``` const serveStatic = require(‘serve-static’) const serve = serveStatic(‘public’) server.on(‘request’, () => { serve(req, res) }) server.listen(3000)

```

5.5 添加学生信息功能步骤分析

  1. 在模板的表单中指定请求地址与请求方式
  2. 为每一个表单项添加name属性
  3. 添加实现学生信息功能路由
  4. 接收客户端传递过来的学生信息
  5. 将学生信息添加到数据库中
  6. 将页面重定向到学生信息列表页面

    5.6 学生信息列表页面分析

  7. 从数据库中将所有的学生信息查询出来

  8. 通过模板引擎将学生信息和HTML模板进行拼接
  9. 将拼接好的HTML模板响应给客户端