1. 简介

MongoDB是一个基于分布式文件存储的数据库,操作语法与JavaScript类似,容易上手。它与mysql不一样,mysql操作的是表格,即结构化数据,而mongodb操作的是json非结构化数据。下载地址https://www.mongodb.com/try/download/community

数据库是按照数据结构来组织、存储和管理数据的应用程序,主要作用就是管理数据,对数据进行增删改查。

Mongodb数据库在使用前,需要在终端执行mongod来打开数据库服务端,在终端执行mongo打开客户端,通过相关命令来完成数据库的增删改查。当然也可以下载Mongoose包来通过代码完成对Mongodb数据库的增删改查。

相比纯文件管理数据,数据库管理数据有如下特点:

  • 速度更快
  • 扩展性更高
  • 安全性更高

Mongodb中有三个重要概念:

  • 数据库(database):是一个数据仓库,数据库服务下可以创建很多数据库,数据库中可以存放很多集合

  • 集合(collection):集合类似于 JS 中的数组,在集合中可以存放很多文档

  • 文档(document):档是数据库中的最小单位,类似于 JS 中的对象

理解:

  • 一个JSON文件好比是一个数据库,一个Mongodb服务下可以有N个数据库
  • JSON文件中的一级属性的数组值好比是集合
  • 数组中的对象好比是文档
  • 对象中的属性也可以称为字段

一般情况下:

  • 一个项目使用一个数据库

  • 一个集合会存储同一种类型的数据

2. 操作命令

1.显示所有的数据块:show dbs

2.切换到指定的数据库,如果数据库不存在会自动创建数据库:use 数据库名

3.显示当前数据库:db

4.删除当前数据库:先use 库名,再db.dropDatabase()

5.创建集合:db.createCollection(‘集合名称’)

6.显示当前数据库中的所有集合:show collections

7.删除某个集合:db.集合名.drop()

8.重命名集合:db.集合名.renameCollection(‘newName’)

9.插入文档:db.集合名.insert(文档对象)

10.查询文档:db.集合名.find(查询条件)

  • _id是mongodb自动生成的唯一编号,用来唯一标识文档

11.更新文档:

  • db.集合名.update(查询条件,新的文档)
  • db.集合名.update({name:’张三’},{$set:{age:19}})

12.删除文档:db.集合名.remove(查询条件)

3. Mongoose

Mongoose是一个对象文档模型库,主要作用就是方便使用代码操作mongodb数据库。

因为它有事一个工具包,所以在通过npm init创建一个包后,就可以在该文件夹下使用npm i mongoose来下载该包。

3.1 通过mongoose连接上Mongodb数据库

在下载Mongoose工具包后,在该文件夹下创建一个js文件,添加以下程序即可完成连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//导入mongoose
const mongoose = require('mongoose');

//连接mongodb服务
mongoose.connect('mongodb://127.0.0.1:27017/bilibili'); //协议-ip-端口-路径(要操作数据库的名称,不存在会自动创建)

//设置回调
mongoose.connection.once('open', ()=>{ //设置连接成功的回调,这里on和once都可以,区别在于once只会连接1次(当服务端关了又开了后,once不会去连接了)
console.log('连接成功');
});

mongoose.connection.on('error', ()=>{ //设置连接错误的回调
console.log('连接失败');
});

mongoose.connection.on('close', ()=>{ //设置连接关闭的回调
console.log('连接关闭');
});

//关闭mongodb的连接
setTimeout( () => {
mongoose.disconnect();
}, 2000); //2s钟后关闭

3.2 Mongoose插入文档

对于文档的插入操作,是在连接成功的回调函数中执行。在下面程序中,需要注意的是:在旧版本中 Model.create()第二个参数是一个回调函数用来捕获成功或异常。但是最新版本中,Model.create()就没有第二个参数了,而是返回了一个 Promise 对象,我们将采用 then 方法 和 catch 方法来捕获状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
mongoose.connection.once('open', ()=>{ //设置连接成功的回调,这里on和once都可以,区别在于once只会连接1次(当服务端关了又开了后,once不会去连接了)
//创建文档的结构对象
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('books', BookSchema); //参1:集合名称;参2:结构对象
//新增(下面这种方式在高版本是不支持的,会报错)
// BookModel.create({
// name: '西游记',
// author: '吴承恩',
// price: 19.9
// }, (err, data) => {
// //如果出现错误
// if(err){
// console.log(err);
// return;
// }
// //如果没有出错,则输出插入后的文档对象
// console.log(data);
// });

//较高的版本使用这种方式插入
BookModel.create({
name: '西游记',
author: '吴承恩',
price: 19.9
}).then((data) => {
console.log(data); //如果插入成功,则输出插入后的文档对象
}).catch((res) => {
console.log(res); //插入失败
});
//关闭数据块连接
mongoose.disconnect();
});

3.3 字段类型

文档结构可选的常用字段类型列表:

类型 描述
String 字符串
Number 数字
Boolean 布尔值
Array 数组,也可以使用[]来标识
Date 日期
Buffer Buffer对象
Mixed 任意类型,需要使用mongoose.Schema.Types.Mixed指定
Objectld 对象ID,需要使用mongoose.Schema.Types.ObjectId指定
Decimal128 高精度数字,需要使用mongoose.Schema.Types.Decimal128指定

测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mongoose.connection.once('open', ()=>{
//创建文档的结构对象
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number,
is_hot: Boolean,
tags: Array,
pub_time: Date
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('books', BookSchema); //参1:集合名称;参2:结构对象

BookModel.create({
name: '西游记',
author: '吴承恩',
price: 19.9,
is_hot: true,
tags: ['鬼怪', '励志', '社会'],
pub_time: new Date() //当前时间为发布时间
}).then((data) => {
console.log(data); //如果插入成功,则输出插入后的文档对象
mongoose.disconnect(); //关闭数据库
}).catch((res) => {
console.log(res); //插入失败
mongoose.disconnect(); //关闭数据库
});
});

3.4 字段值验证

Mongoose有一些内建验证器,可以对字段值进行验证

1.必填项

1
2
3
4
title: {
type: String,
required: true //表明该属性必须不为空
},

2.默认值

1
2
3
4
author: {
type: String,
default: '匿名' //设置默认值,如果没有选择这一属性,会默认为`匿名`
},

3.枚举值

1
2
3
4
gender: {
type: String,
enum: ['男','女'] //设置的值必须是数组中的
},

4.唯一值

1
2
3
4
username: {
type: String,
unique: true //设置这个属性为唯一性,不能有重复的该字段名
},

注意:

  • unique 需要 重建集合 才能有效果

  • 永远不要相信用户的输入,需要检查后再插入数据库

测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mongoose.connection.once('open', ()=>{
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: {
type: String,
required: true, //表明该属性必须不为空
unique: true //设置为独一无二的,不能重复的属性名
},
author: {
type: String,
default: '匿名'
},
style: {
type: String,
enum: ['言情', '城市', '志怪', '恐怖']
}
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('books', BookSchema); //参1:集合名称;参2:结构对象

BookModel.create({
name: '西游记', //为空了会报错
author: '吴承恩', //没有这一项,会默认作者是'匿名'
style: '言情' //类型只能选择上面定义好的枚举类里面的内容
}).then((data) => {
console.log(data); //如果插入成功,则输出插入后的文档对象
mongoose.disconnect();
}).catch((res) => {
console.log(res); //插入失败
mongoose.disconnect();
});
});

3.5 Mongoose删除和更新文档

下面程序需要注意的是,由于版本的问题,现在高版本已经不支持BookModel.deleteMany({}, (err, data) => {});这种回调的写法了。

1.通过以下方式来完成删除操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mongoose.connection.once('open', ()=>{
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number,
is_hot: Boolean
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('novels', BookSchema); //参1:集合名称;参2:结构对象

//删除1条,因为版本问题,不同通过回调函数的形式来写了
BookModel.deleteOne({_id: '66e65604f92fdec9fa0a7f53'}).then((data) => {console.log(data);}).catch((res)=>{console.log('删除失败~~~');return;});
//批量删除,删除is_hot为false的文档
BookModel.deleteMany({is_hot: false}).then((data) => {console.log(data);}).catch((res)=>{console.log('删除失败~~~');return;});
});

2.通过以下方式来完成更新操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mongoose.connection.once('open', ()=>{
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number,
is_hot: Boolean
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('novels', BookSchema); //参1:集合名称;参2:结构对象

//更新数据1条(参数:条件;更新的情况)
BookModel.updateOne({name: '名侦探柯南'}, {price: 13.9}).then((data) => {console.log(data);}).catch((res)=>{console.log('更新失败~~~');return;});
//批量更新
BookModel.updateMany({is_hot: true}, {price: 19.9}).then((data) => {console.log(data);}).catch((res)=>{console.log('更新失败~~~');return;});
});

3.6 读取文档

1.普通读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mongoose.connection.once('open', ()=>{
//设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number,
is_hot: Boolean
});
//创建模型对象, 对文档操作的封装对象
let BookModel = mongoose.model('novels', BookSchema); //参1:集合名称;参2:结构对象
//1.读取单条
BookModel.findOne({name: '名侦探柯南'}).then((data) => {console.log(data);}).catch((res)=>{console.log('读取失败~~~');return;});
//2.通过id获取
BookModel.findById('66e65688565eb1640b878f88').then((data) => {console.log(data);}).catch((res)=>{console.log('读取失败~~~');return;});
//3.批量获取(如果find里面没有写任何内容,就是获取全部)
BookModel.find({is_hot: true}).then((data) => {console.log(data);}).catch((res)=>{console.log('读取失败~~~');return;});
});

2.运算符

在mongodb不能直接使用>、<、>=、<=、!==等运算符,需要使用替代符号:

  • >使用$gt
  • <使用$lt
  • >=使用$gte
  • <=使用$lte
  • !==使用$ne
1
2
3
4
5
6
7
8
9
//查找价格小于30的书籍
BookModel.find({
price: {$lt: 30}
}).then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});

3.逻辑运算

  • $or表示逻辑或
  • $and表示逻辑与
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//查找名字为名侦探柯南或海贼王的书籍
BookModel.find({
$or: [{name: '名侦探柯南'},{name: '海贼王'}]
}).then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});

//价格大于15,下于30的书籍
BookModel.find({
$and: [{price: {$gt: 15}}, {price: {$lt:30}}]
}).then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});

4.正则匹配

条件中可以直接使用JS的正则语法,通过正则可以进行模糊查询

1
2
3
4
5
6
7
8
9
//查找名字带柯的书籍
BookModel.find({
name: /柯/
}).then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});

5.个性化读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//读取全部数据,但每条数据只显示name和author
BookModel.find().select({
name: 1, author: 1, _id: 0 //如果不添加_id: 0,默认会有id的
}).then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});
//对数据的价格进行升序排序
BookModel.find().select({
name: 1, author: 1, _id: 0})
.sort({price: 1}) //1是升序;-1是倒序
.then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});
//按照价格降序排序,又跳过了3个,所以选择查看第4和第5贵的书籍信息
BookModel.find().select({
name: 1, author: 1, _id: 0})
.sort({price: -1}) //降序
.skip(3) //跳过前面3个
.limit(2) //限定,选择前2个
.then((data) => {
console.log(data);
}).catch((res)=>{
console.log('读取失败~~~');
return;
});

最后如果想要操作更加的简单,可以使用Mongodb的图形化工具,比如说Navicat等。