MongoDB介绍
MongoDB是一个基于分布式文件存储的数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据库存储解决方案。
特点:高性能,易部署,易使用,存储数据方便。
MongoDB是一个介于关系数据库和非关系数据库之间的产品。是非关系数据库中功能最丰富最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。。MongoDB最大的特点就是它支持的查询语言非常强大,类似于面向对象的查询语言,几乎可以实现类似关系数据库的单表查询的绝大部分功能。
关系型数据库和非关系型数据库是两种不同的数据存储方式。关系型数据库通过二维表维持数据关系,需要预先定义数据结构,支持事务处理,安全性高,但查询速度慢,只能存储基础类型。非关系型数据库通过键值对维持数据关系,数据结构灵活,查询速度快,可以存储多种格式的数据,但不支持事务处理,安全性低。
MongoDB中的基础概念
MongoDB中的基础概念:文档,集合,数据库
下图是mysql和MongoDB的区别
一个MongoDB可以创建多个数据库,默认数据库为db,该数据库存储在data目录中。单个实例可以容纳多个独立的数据库,每个数据库都有自己的集合和权限,不同的数据库防在不同的文件中。数据库命名采用标识符,一般是utf-8字符串,不能为空,不能用local/admin/config这三个
文档
文档是一个键值对,MongoDB的文档不需要设置相同的字段,相同的字段不需要相同的数据类型,这与关系型数据库有很大区别,也是MongoDB非常突出的特点。
一个示例
{"title":"我是标题","content":"没有内容"}
集合
集合就是MongoDB文档组,类似于RDBMS,关系数据库管理系统中的表格
集合存在于数据库中,集合没有固定的结构,这意味着你在集合中可以插入不同格式和类型的数据,但是通常情况下我们插入集合的数据都会有一定的关联性。集合的命名不能使空字符串。也不能出现-,0等,不能以system,$开头
当第一个文档插入时,集合就会被创建。
数据库
MongoDB中常用的几种数据类型
- String − 这是最常用的用于存储数据的数据类型。在MongoDB中,字符串必须是UTF-8格式的。
- Integer − 这种类型用于存储数字值。整数可以是32位或64位,取决于服务器。
- Boolean − 这种类型用于存储布尔值(true/false)。
- Double − 这种类型用于存储浮点数值。
- Min/ Max键 − 这种类型用于将值与最低和最高的BSON元素进行比较。
- Arrays − 这种类型用于将数组、列表或多个值存储到一个键中。
- Timestamp − 时间戳。这对于记录文档的修改或添加时间非常有用。
- Object − 这种数据类型用于嵌入式文档。
- Null − 这种类型用于存储空值。
- Symbol − 这种数据类型与字符串的使用方式相同;然而,它通常用于使用特定符号类型的语言。12.
- Date − 这种数据类型用于以UNIX时间格式存储当前日期或时间。您可以通过创建Date对象并将日期、月份、年份传递给它来指定自己的日期时间。
- Object ID − 这种数据类型用于存储文档的ID。
- 二进制数据 − 这种数据类型用于存储二进制数据。
- Code − 这种数据类型用于将JavaScript代码存储到文档中。
- 正则表达式 − 这种数据类型用于存储正则表达式。
ObjectId
ObjectId类似唯一主键,可以很快的生成排序。
MongoDB中存储的文件必须有一个_id键,这个键的值可以是任何类型的,默认是个ObjectId对象。
字符串
BSON字符串都是UTF-8编码
MongoDB安装
可视化工具Studio 3T
查看相关文档安装MongoDB,使用可视化工具Studio 3T操作数据库。
都可以到官网下载相关安装文件,安装完之后使用Studio 3T。
利用可视化界面来操作数据库的增删改查,安装的目的是为了我们使用代码操作的时候,更方便的查看操作的结果。使用可视化工具要保证数据库是开启状态。
之后save即可
左侧点击
选择shell,在命令行输入命令。
MongoDB命令
创建数据库,不存在就创建,存在就切换到指定数据库。(blog为数据库名称)
use blog
查看所有数据库
show dbs
在数据库里创建数据表users
db.createCollection(users)
左侧如果没有显示,刷新所有即可。
document 文档操作
db.集合名.方法名
可以在集合中插入文档信息
db.users.insert({"username":"zhang","password":"123456"})
db.users.insertOne({"username":"zhangsan","password":"123456"})
db.users.insertMany([{"username":"zhangsan","password":"123456"}])
插入操作如果执行多次,就会插入多个文档
查询文档
db.users.find({"username":"zhangsan"})
db.users.findOne({"username":"zhangsan"})
更新文档
db.users.update({username:'zhangsan'},{$set:{username:'lisa'}})
多次执行可以将多个相同的文档数据逐个更新
db.users.update({username:'lisa'},{$inc:{age:18}})
上面的命令可以为lisa添加age属性
删除文档
删除年龄是18的
db.users.deleteOne({age:18});
删除user中所有数据
db.user.deleteMany({})
条件查询
范围查询
查询age=18的记录
db.users.find({"age":18})
查询age>18的记录
db.users.find({"age":{$gt:18}})
查询age<18的记录
db.users.find({"age":{$lt:18}})
查询13<=age<=100的记录
db.users.find({age:{$gte:13,$lte:100}});
筛选数据
查询指定列name,age数据
db.users.find({},{username:1,age:0})//1为显示,0为不显示。
如果只查看前5条数据。
db.users.find({},{username:1},{age:0}).limit(5)
查看10条以后的数据。
db.users.find({},{username:1},{age:0}).skip(1)
限制数据量
db.users.find({"username":"zhan"},{age:0}).limit(1).skip(1)
查询指定列age数据,age>15
db.users.find({age:{$gt:15}},{username:0},{age:1})
排序
按照年龄排序
升序
db.users.find().sort({age:1})
降序
db.users.find().sort({age:-1})
属性值为空的升序在最上面,降序在最下面。
Mongoose
一个第三方库,在express中利用mongoose库操作MongoDB比较方便。
mongoose为模型提供一种直接的基于scheme结构去定义数据结构。它内置数据验证,查询构建,业务逻辑钩子等,开箱即用。
简单来说,可以使用mongoose定义数据的表结构,表结构在mongoose中叫scheme,然后直接使用表结构创建一个数据模型。我们使用数据模型就可以操作数据库中的对应的表。这里的表不需要自己创建,直接使用数据模块,他会自动创建表。
mongoose的使用方式
安装mongoose
npm 初始化项目
安装 mongoose
npm i mongoose
引入模块,连接数据库
创建models文件夹,存放数据库的模型相关操作,新建文件index.js
//引入模块
let mongoose = require('mongoose');
//连接数据库
mongoose.connect('mongodb://127.0.0.1/blog').then(res => {
console.log('success');
}).catch(err)=> {
console.log("fail");
};
如果使用localhost失败可以使用127.0.0.1
mongoose中的对象
schema模式对象(用于约束文档的结构-类似表头)
model模型对象(MongoDB中的集合-表)
document文档对象(即MongoDB中的文档-表中一行数据)
Schema
mongoose中的一切起始于Schema。每个Schema都会映射到一个MongoDB 集合上,并定义这个集合里的文档(一行数据)的构成。
//获取创建集合的模块
let Schema = mongoose.Schema;
//新建文章表结构
let ArticleSchema = new Schema(
{
title: String,//文章标题
content: String,//文章内容
tag: String,//文章分类
author: String,//文章作者
views: {
type: Number,
default: 0,
},//文章的浏览量
},
{
timestamps: true,//启用时间戳时,mongoose会将createAt和updatedAt属性添加到模型中,表中添加一行数据的时候会产生时间戳,记录,数据的创建时间和修改时间
}
);
Model
Models是从Schema编译来的构造函数,它们的实例就代表可以从数据库保存和读取的documents。从数据库创建和读取document的所有操作都是通过model进行的。
通过它可以对表的内容进行增删改查
// 根据表结构创建数据模型,并把表结构映射数据中一个表Articles
// article通过这个对象我们就可以对数据库中的数据进行增删改查
let Article = mongoose.model('Article', ArticleSchema);
第一个参数是跟model对应的集合名字,将表结构ArticleSchema绑定到当前数据库上,并且得到操作表中内容的构造函数。用这些构造函数可以创建表中的数据,进行CRUD增删改查。
注意,当向表中插入数据的时候表才会生成,表的名字自动使用复数的形式,比如刚刚构造函数Article,在使用的时候会自动在数据库中创建一个Articles表。
创建文档数据,插入表中
// 创建数据,保存到数据库
Article.create({
title: "给学妹的信",
content: "每逢佳节倍思亲",
author: "zhagsan",
tag: "信件",
}).then((res) => {
console.log(res);
console.log("爆粗成功")
});
下面也可以
let atr=new Article({
title: "给x的信",
content: "每逢佳节倍思亲",
author: "zhagsan",
tag: "信件",
});
//保存到数据库
atr.save().then((res)=>{
console.log(res);
console.log("保存成功");
});
终端进入models安装mongoose执行nodemon index.js
也可以在创建数据的时候在对象里面写一个数组包含着多个对象。
在blog数据库中可以看到articles集合及其数据。
删除数据
第一个参数是一个对象,对象内是条件
- 删除指定id的文章
Article.deleteOne({ _id: '66a05e2bb682caaad9a91f6b' }).then(res => {
console.log('删除成功');
console.log(res);
}).catch(err => {
console.log('删除失败');
})
- 删除包含指定内容的数据
Article.deleteMany({ content:/每逢佳节/ }).then(res => {
console.log('删除成功');
console.log(res);
})
修改数据
第一个参数对象是一个条件
第二个参数对象是一个要修改的内容
//指定id文章修改内容
Article.updateOne({ _id: '66a05df8a4bbca4482cdd133' }, { title: 'aaa' }).then(res => {
console.log('修改成功');
})
上述代码实现修改文章标题为aaa
根据id修改views字段,自加1,并且修改的时候不易绕过updateAt更新
Article.updateOne(
{
_id: '66a077c6e960d145a3912b2c'
},
{
$inc: { views: 1 }//views字段自加1
},
{
timestamps: false//修改不让updateAt自增
}
).then((res) => {
console.log("修改成功");
console.log(res);
})
.catch((err) => {
console.log("修改失败");
});
查询数据
查询多条数据
能查询多条,也可以传入条件Article.find({ views: { $gte: 0, $lte: 1000 }, title: /x/ })//搜索包含x的数据,使用正则匹配 .sort({ views: -1 })//根据views排序 1升序 -1降序 .skip(1)//跳过前0条 .limit(10)//获取10条事件 .select({author:0})//1 查询结果中出现author字段,0是不出现这个字段 .exec()//执行查询 .then((r) => { console.log(r) });
- 根据id查询一条数据
Article.findOne({ _id: "66a077c6e960d145a3912b2c" }).then((err) => {
console.log(err)
})
Article.findById("66a077c6e960d145a3912b2c").then((err) => {
console.log(err);
});
Article.findByIdAndUpdate('66a077c6e960d145a3912b2c', { $inc: { views: 1 } }, { timestamps: false }).then(r => {
console.log(r);
});
图为最后一个代码运行结果,代码第一次执行是原来的views,再次执行,views加1
- 查询浏览量在1-1000的数据,标题包含x的数据
// 浏览量1-1000,标题包含x的数据
Article.find({ views: { $gte: 0, $lte: 1000 }, title: /x/ })
.exec()
.then((r) => {
console.log(r)
});
排序
根据文章views进行降序排序
Article.find({ views: { $gte: 0, $lte: 1000 }, title: /x/ })
.sort({views: -1 })
.exec()
.then((r) => {
console.log(r)
});
局部截图
聚合函数
聚合函数是数据库操作中不可或缺的一部分。
下图是一些常用的聚合函数表达式
参与示例的集合数据如下
MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。
有点类似 SQL 语句中的 count(*)
语法:>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
计算每个学生的学科总分数,使用aggregate()计算
db.scores.aggregate([
{ $group: {
_id: "$studentName", // 根据学生姓名分组
totalScore: { $sum: "$score" } // 计算学科总分数
}}
])
查询学生的个人信息以及班级信息
需要多表联查,使用$lookup联合student集合以及class集合
//管道查询,联合查询
db.getCollection("student").aggregate([{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
}])
查询小九的学生信息
//管道查询,联合查询,$match过滤条件查询
db.getCollection("student").aggregate([{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
}, {
$match: {
name: "小九"
}
}])
//两表关联,每个表都有条件
db.getCollection('student').aggregate([
{
$match: {
name: "小明"
}
},
{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
},
{
$match: {
"student_class.master": "老李"
}
}
])
查看学生课程信息
//遍历数组数据
db.getCollection('student').aggregate([
{
$unwind: "$course"
}
])
//管道查询,联合查询,$unwind数组便利,$match过滤条件查询
db.getCollection('student').aggregate([
{
$unwind: "$course"
},
{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
},
{
$match: {
name: "小明"
}
}
])
显示指定同学的姓名和年龄
$project可以用来提取想要的字段,1表示要该字段,0表示不要该字段,也可以对返回的字段进行重命名:
//管道查询,联合查询,$unwind数组便利,$match过滤条件查询,$project设置需要返回的字段属性
db.getCollection('student').aggregate([
{
$unwind: "$course"
},
{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
},
{
$match: {
name: "小明"
}
},
{
$project: {
name: 1,
age: 1
}
}
])
返回数据库的student集合中的前两条数据
//返回前两条数据
db.getCollection("student").aggregate([{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
}, {
$limit: 2
}])
跳过student的第一条数据并返回两个文档
//跳过第一个文档并返回两个文档
db.getCollection("student").aggregate([{
$lookup: {
from: "class",
localField: "id",
foreignField: "stuid",
as: "student_class"
}
}, {
$skip: 1
}, {
$limit: 2
}])
注意: s k i p 语句应该在 skip语句应该在 skip语句应该在limit之前。
按照班级分组查询
//分组查询
db.getCollection("class").aggregate([{
$group: {
_id: "$master",
count: {
$sum: 1
}
}
}])
将学生集合按照id号降序排序
将查询结果按照某个字段进行排序,相当于mysql的order by语句,1表示升序,-1表示降序,默认为升序显示。
//排序
db.getCollection("student").aggregate([{
$sort: {
id: -1
}
}])
管道的概念
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
这里我们介绍一下聚合框架中常用的几个操作:
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
m
a
t
c
h
:用于过滤数据,只输出符合条件的文档。
match:用于过滤数据,只输出符合条件的文档。
match:用于过滤数据,只输出符合条件的文档。match使用MongoDB的标准查询操作。
$limit:用来限制MongoDB聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。