MongoDB基础知识
1. 简介
1、 NoSQL(Not Only SQL),不仅仅是SQL,主要是指非关系型数据库,是对不同与传统的关系型数据库的数据管理系统的统称
2、 NoSQL用于超大规模数据的存储,这些类型的数据存储吧需要固定的模式,无需多余的操作就可以横向扩展
1.2 NoSQL 和 RDBMS的区分
NoSQL | RDBMS |
---|---|
代表着不仅仅是SQL | 高度组织化结构化数据 |
没有声明性的查询语句 | 结构化查询语言(SQL) |
没有预定义的模式 | 数据和关系都存储在单独的表中 |
键值对存储、列存储、文档存储、图形数据库 | 数据操作语言、数据定义语言 |
最终一致性、而非ACID属性 | 严格的一致性 |
非结构化和不可预知的数据 | 基础事务 |
CAP定理 | |
高性能、高可用、可伸缩性 |
1.3 NoSQL 数据库四大家族
1.3.1 键值(Key-Value)存储
特点:键值数据库就像传统语言中使用的哈希表。通过Key添加、查询或者删除数据
优点:查询速度快
缺点:数据无结构化,通常只被当作字符串或者二进制数据存储
应用场景:内存缓存、用户信息比如会话、配置信息、购物车等,主要用于处理大量数据的高访问负载
NoSQL代表:Redis Memcached
1.3.2 文档(Document-Qriented) 存储
特点:文档数据库将数据以文档的形式存储,类似JSON,是一系列数据项的集合,每个数据项都有一个名称与对应的值,值既可以是简单的数据类型,如字符串、数字、日期等;也可以是复杂的类型,如有序列表和关联对象
优点:数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构
缺点:查询性能不高,缺乏统一的查询语句
应用场景:日志、Web应用等
NoSQL代表:MongoDB CouchDB
1.3.3 列(Wide Column Store / Column-Family)存储
特点:列存储数据库将数据存储在列族(Column Family)中,将多个列聚合成一个列族,键仍然存在,但是他们的特点是指向多个列。例如:我们有一个Person类,我们通常会一起查询他们的姓名和年龄,而不是薪资,这种情况下,姓名和年龄就会被放入一个列族中,而薪资则在另一个列族中
优点:列存储查找速度快,扩展性强,更容易进行分布式扩展,适用于分布式文件系统,应对分布式存储的海量数据
缺点:查询性能不高,缺乏统一的查询语句
应用场景:日志、分布式文件系统(对象存储)、推荐画像、时空数据、消息/订单等
NoSQL代表:Cassandra HBase
1.3.4 图形(Graph-Oriented)存储
特点:图形数据库允许我们将数据以图的方式存储
优点:图形相关算法、比如最短路径寻址、N度关系查找等
缺点:很多时候需要对整个图做计算才能得到需要的信息,分布式的集群方式不好做,处理超级节点乏力,没有分片存储机制,国内社区不活跃
应用场景:社交网络、推荐系统等,专注于构建关系图谱
NoSQL代表:Neo4j Infinite Graph
1.4 NoSQL的优缺点和适用情况
1.4.1 优点
- 高可扩展性
- 没有标准化
- 分布式计算
- 有限的查询功能
- 低成本
1.4.2 缺点
- 最终一致是不直观的程序
- 架构的灵活,半结构化数据
- 没有复杂的关系
1.4.3 适用情况
- 数据模型比较简单
- 需要灵活性更强的IT系统
- 对数据库性能要求较高
- 不需要高度的数据一致性
- 对于始定的Key,比较容易映射复杂值的环境
1.5 MongoDB 介绍
MongoDB是为快速开发互联网Web应用而设计的数据库系统
MongoDB的设计目的是极简、灵活、作为Web应用栈的一部分
MongoDB数据模型是面向文档的,所谓文档是一种类似于JSON的结构,简单理解MongoDB这个数据库中存的是各种各样的JSON(BSON)
1.5.1 相关概念
1、 数据库(database)
数据库是一个仓库,在仓库中可以存放文档
2、 集合(collection)=== 表
集合类似于数组,在集合中可以存放文档
3、 文档(document)=== 行
文档是数据库中的最小单位,我们存储和操作的内容都是文档
SQL术语概念 | MongoDB术语概念 |
---|---|
database(数据库) | database(数据库) |
table(表) | collection(集合) |
row(行) | document or BSON document(文档) |
column(列) | field(字段) |
index(索引) | index(索引) |
table joins(表连接) | embedded document and linking(嵌入的文档和链接) |
指定任意唯一的列或列组合作为主键 | 在MongoDB中,主键被自动设置为_id字段 |
aggregation(e.g.group by) | 聚合操作 |
1.5.2 支持的数据类型
2. MongoDB的启动
查看MongoDB是否安装成功
mongod --version
mongodb默认使用执行 mongod 命令所处的盘符根目录下的 /data/db 作为自己的数据存储目录
2.1 前台启动
MongoDB的默认启动方式是 前台启动
所谓的前台启动就是MongoDB启动进程后会占用当前的终端窗口,这时候你就需要重新开启一个终端窗口来写你的shell命令
# 切换到MongoDB安装bin目录
cd D:/software/MongoDB/bin
# 前台启动命令
mongod --dbpath D:/software/MongoDB/data/db/ --logpath D:/software/MongoDB/log/mongod.log --logappend --port 27017 --bind_ip 0.0.0.0
# 也可以简写为:
mongod --dbpath D:/software/MongoDB/data/db/ --logpath D:/software/MongoDB/log/mongod.log
--path:指定数据文件存放目录
--logpath:指定日志文件,注意是指定文件而不是目录
--logappend:使用追加的方式记录日志
--port:指定端口,默认是 27017
--bind_ip:绑定服务ip,若绑定127.0.0.1,则只能本机访问,默认是本机访问
# 前台结束服务
Ctrl+C
2.2 后台启动
所谓的后台启动就是以守卫进程的方式启动MongoDB,只需命令中添加 --fork
即可
后台启动不会占用当前的命令窗口
目前命令 命令--fork只适用于Linux/Unix
# 后台启动
mongod --dbpath D:/software/MongoDB/data/db/ --logpath D:/software/MongoDB/log/mongod.log --fork
# 结束服务 结尾加 --shutdown 即可
mongod --dbpath D:/software/MongoDB/data/db/ --logpath D:/software/MongoDB/log/mongod.log --fork --shutdown
2.3 配置文件启动
使用命令启动的方式并不适合管理,毕竟每次输入名称都需要考虑各个参数的配置,我们可以通过配置文件来配置启动参数,然后通过指定文件的方式来使用服务,这样再管理MongoDB就比较方便了
2.3.1 配置文件 mongodb.conf
在bin目录下新增一个mongodb.conf
配置文件
# 数据文件存放目录
dbpath=D:/software/MongoDB/data/db/
# 日志文件存放目录
logpath=D:/software/MongoDB/log/mongod.log
# 以追加的方式记录日志
logappend=true
# 端口默认为 27017
port=27017
# 对访问 IP 地址不做限制,默认为本机地址
bind_ip=127.0.0.1
# 以防护的方式启动,即在后台启动; 由于windows 不支持防护方式,所以一般都是采用前台启动的方式;
# 注意如果是前台启动,执行mongod -f mongodb.conf后,需要另外开启一个新的cmd窗口 开启mongo的连接
# fork = true
# 开启身份认证
auth=true
2.3.2 启动MongoDB
# 1. 启动MongoDB
mongod -f mongodb.conf
2.3.3 新开一个命令窗口,连接启动 mongo 数据库
# 1. 启动mongo
mongo
# 2. 查看数据库(注意:此时是查询不到数据库的,必须要 开启身份认证 才可以查到数据)
show dbs
# 3. 开启身份验证
use admin
db.auth("admin","admin")
# 4. 再次查看 数据库就有数据了
show dbs
2.3.4 关闭/结束
mongod -f mongodb.conf --shutdown
2.4 环境变量启动-推荐
2.4.1 配置全局环境变量
配置环境变量的方法如下:
1、 复制安装MongoDB里面的bin目录的路径 例如:D:\software\MongoDB\bin
2、 选择目录:计算进--属性--高级系统设置--环境变量,在系统变量的path里面,将上面的目录路径添加进去
3、 重启电脑
4、 开启服务
方式一:任务管理器--服务,开启MongoDB服务
方式二:进入MongoDB安装目录bin里面,执行命令 net start mongodb
5、 直接打开cmd,输入 mongo
命令,开启连接 mongo数据库
如果想要退出 连接:exit 或者 ctrl+c
2.4.2 开启身份验证
使用配置环境变量的方式默认是没有开启身份验证的,如果想要开启身份验证,可以执行如下操作
# 1. 添加设置 超级管理员
use admin
db.createUser({
"user" : "admin",
"pwd": "admin",
"roles" : [{
role: "root",
db: "admin"
}]
})
# 2. 退出卸载服务 (bin目录)
mongod --remove
# 3. 重新安装需要输入账号密码的服务(注在原安装命令基础上加上--auth即可)
mongod --install --dbpath D:\software\MongoDB\data\db --logpath D:\software\MongoDBlog\mongodb.log --auth
2.4.3 启动服务
# 启动服务
mongo
# 再次查询验证服务
show dbs
# 默认是看不到数据库的,需要验证 db.auth("admin","admin") 验证通过才可以;
# 或者直接通过命令:mongo 服务器IP地址:端口/数据库 -u 用户名 -p 密码
mongo 127.0.0.1:27017/admin -u admin -p admin
# 关闭服务
db.shutdownServer()
# 退出客户端
exit
3. 相关服务操作命令如下(bin目录下的操作)
# 1. 创建服务:
mongod --install --dbpath D:/software/MongoDB/data/db/ --logpath D:/software/MongoDB/log/mongod.log
# 2. 删除服务
mongod remove
# 3. 启动服务
net start mongodb
# 4. 关闭服务
net stop mongodb
4. 常用基本命令
# 1. 查看所有相关命令的使用
help
# 2. 退出当前
mongo shellexit
# 3. 查看所有的数据库,如果数据库里面没有数据,默认不显示
show dbs
# 4. 查看当前数据库所有的集合
show collections
# 5. 查看当前数据库的所有用户
show users
# 6. 查看当前所在的数据库
db
# 7. 跳转/切换 到指定数据库;如果没有指定的数据库,那么默认是新建数据库
use admin
# 8. mongo函数关闭服务器
user admin(必须切换到admin服务器)db.shutdownServer()
# 9. 查看admin数据库中所有用户信息
db.system.users.find()
# 10. 查看所有数据库相关方法
db.help()
# 11. 查看版本号
db.version()
# 12. mongo shell 清屏cls
5. MongoDB用户与权限管理
5.1 常用权限
权限 | 说明 |
---|---|
read | 允许用户读取指定数据库 |
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数 ,如索引创建、删除、查看统计或访问system.profile |
userAdmin | 允许用户向system.user集合写入,可以在指定数据库里创建、删除、管理用户 |
readAnyDatabase | 必须在admin数据库中定义,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 必须在admin数据库中定义,赋予用户所有数据库的读写权限 |
dbAdminAnyDatabase | 必须在admin数据库中定义,赋予用户所有数据库的adAdmin权限 |
userAdminAnyDatabase | 必须在admin数据库中定义,赋予用户所有数据库的userAdmin权限 |
root | 必须在admin数据库中定义,超级账号,超级权限 |
5.2 角色种类
超级用户角色:root
数据库用户角色:read、readWrite;
数据库管理角色:dbAdmin、userAdmin;
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;
备份恢复角色:backup、restore;
所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
5.3 创建管理用户-管理员
MongoDB有一个用户管理机制,简单描述为 用户管理组,这个组的用户专门为 管理普通用户 而设的,我们称之为 管理员
管理员通常是没有数据库的读写权限,只有操作用户的权限,所以我们只需赋予管理员userAdminAnyDatabase角色即可
管理员账户 必须在admin数据库下创建
用户被创建在哪个数据库下,就只能在哪个数据库登陆,所以我们把所有的用户都创建在amdin数据下,这样我们切换数据库的时候,就不需要频繁的进行登陆了
# 0. 查看所有数据库
show dbs
# 1. 切换到admin数据库
user admin
# 2. 查看当前数据库中所有的用户信息
db.system.users.find()
# 或者
show users
# 3. 创建用户
# 在MongoDB中,我们可以使用 db.creatUser({用户信息})函数创建用户
# 3.1 创建用户语法:
db.create({
user: "<name>", # 用户名
pwd: "<cleartext password>", # 密码
# 存放用户相关的自定义数据,可选属性
customData: {
<any information >
},
# 数组类型,配置用户的权限
roles: [{
role: "<role>",
db: "<database>"
}......]
})
db.createUser({
"user": "账号",
"pwd": "密码",
"roles": [{
role: "角色",
db: "所属数据库"
}]
})
# 3.2 创建一个用户
db.createUser({
user: "uaad",
pwd: "uaad",
roles: [
{
role: "userAdminAnyDatabase",
db: "admin"
}
]
})
# 4. 重启服务
# 管理员创建完账号以后,需要重启mongo,并开启身份验证功能
# 关闭服务
db.shutdowmServer()
5.4 创建普通用户
创建一个test数据库,然后为数据库添加一个 用户名和密码是user1的用户,并赋予该用户 对该数据库的读写操作权限
操作都在MongoDB安装bin目录下进行
5.4.1 MongoDB开启并连接服务
此处采用的是 配置文件的方式 开启的连接,有身份认证
1 启动MongoDB
# 1. 启动MongoDB
mongod -f mongodb.conf
2 新开一个命令窗口,连接启动 mongo 数据库
# 1. 启动mongo
mongo
# 2. 查看数据库(注意:此时是查询不到数据库的,必须要 开启身份认证 才可以查到数据)
show dbs
# 3. 开启身份验证
use admin
db.auth("admin","admin")
# 4. 再次查看 数据库就有数据了
show dbs
5.4.2 创建test数据库
# 如果有该数据库,就切换; 如果没有改数据库,就自动创建并切换到改数据库
use test
5.4.3 创建用户
db.createUser({
user: "user1",
pwd: "user1",
roles: [{
role: "readWrite",
db: "test"
}]
})
4.5.4 身份认证
打开一个新的客户端连接,并切换至test数据库,进行身份验证
# 1. 开启服务
mongo
# 2. 切换到 test 数据库
use test
# 3. 开启身份认证
db.auth("user1","user1")
4.5.5 向test数据库插入文档
db.user.insert(
{ "name": "zhangsan" }
)
4.5.5 查看user表中的用户
db.user.find()
5.5 更新用户
5.5.1 更新角色/权限
如果我们需要对已存在的用户角色进行修改,可以 使用 db.updateUser()
函数来更新用户角色
注意:执行该函数需要当前用户具有
userAdmin
或userAdminAnyDatabase
或root
权限
# 语法
db.updateUser("用户名",{"roles":[{"role":"角色名称","db":"数据库"},{"更新项2":"更新内容"}]})
# 例如给admin用户再添加 readWtiteAnyDatabase 和 dbAdminAnyDatabase 权限
db.updateUser("admin", {
"roles": [{
"role": "userAdminAnyDatabase",
"db": "admin"
}, {
"role": "readWriteAnyDatabase",
"db": "admin"
}, {
"role": "dbAdminAnyDatabase",
"db": "admin"
}]
})
再次查看 admin 用户的权限
use admin
show users
5.5.2 更改密码
更改密码有2种方式,更改密码时需要切换到该用户所在的数据库
需要具有
userAdmin
或userAdminAnyDatabase
或root
角色权限的用户执行
# 方法一:
db.updateUser("用户名", {
"pwd": "新密码"
})
# 方法二:
db.changeUserPassword("用户名", "新密码")
5.6 删除用户
使用 db.dropUser()
函数可以删除指定用户,删除成功后,返回true
db.dropUser("uaad")
删除用户时,需要切换到该用户所在的数据库
需要具有
userAdmin
或userAdminAnyDatabase
或root
角色权限的用户才可以删除其他用户
6. 使用MogoDB GUI 可视化管理
方式一:
任务管理器 -- 服务 的方式开启 MongoDb,然后打开GUI可视化管理工具,连接Mongo即可
方式二:
使用 配置文件 mongod -f mongodb.conf
的方式开启 MongoDb,然后打开GUI可视化管理工具,连接Mongo即可
7. MongoDB数据库操作
7.1. 创建数据库
use 命令表示切换到数据库,如果数据库不存在,会自动创建数据库
# 语法
use databasename
# 创建一个名称为 order 的数据库
use order
7.2. 查询数据库
show dbs
# 或者
show databases
不同角色看见的数据库是不一样的,管理员可以看见所有的数据库,普通用户只能看见自己权限内的数据库
新创建的数据库,默认里面是没有数据的,所以当使用 show dbs 或者 show databases
查看数据库的时候,是查询不到的;需要向此数据库新增一条数据才可以查到数据库
7.3. 删除数据库
db.DropDatabase()
删除数据库首先要切换到要删除的数据库中,使用 use database
切换到指定的数据库,而且登陆用户必须具有 dbAdminAnyDatabase
权限
8. MongoDB 集合(表)操作
8.1. 创建集合
# 创建一个名为 order 的集合
db.createCollection('order')
- name 集合的名称,optons 可选参数,指定有关内存、大小及索引的选项
- 固定集合:有固定大小的集合,当达到最大值时,它会覆盖最早的文档
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为true,则创建固定集合,而且当为true时,必须指定sise大小 |
size | 数值 | (可选)限定集合空间的大小,默认没有限制(以字节计) |
autoIndexId | 布尔 | (可选)如果为true,自动在_id字段创建索引。默认true |
max | 数值 | (可选)限制集合中,包含文档的最大数量。默认是没有限制 |
也可以通过直接插入数据,隐式创建集合 db.member.insert({"name":"zhangsan"})
,自动创建集合 member
同时将数据插入到 集合中
# 方式一:当第一个文档被插入时,集合会被自动创建并包含该文档
db.member.insert({"name":"zhangsan"})
# 方式二:创建一个空集合;如果里面没有数据,查看集合(show tables 或者 show collections)时,是无法查到数据的;只有向该集合中插入文档,才可以查询到集合db.member
8.2. 查询集合
show tables
# 或者
show collections
如果集合里面没有数据,是查询不到集合的
8.3. 删除集合
db.database_name.drop()
9. MongoDB 文档(行)操作
9.1 插入文档
9.1.1 单条插入
可以使用 insert/insertOne/save
插入单条文档
# 插入 name=zhangsan 的一条文档:::
db.member.insert({"name":"zhangsan"})
db.member.insertOne({"name":"zhangsan"})
db.member.save({"name":"zhangsan"})
# 通过定义一个变量的方式 插入数据是非常方便的(推荐)
user1 = {
"name": "zhangsan",
"age": "18",
"email": "375878@qq.com"
}
db.member.insert(user1)
save方法如果指定了
_id
字段就是更新文档,否则就是插入一条新的文档数据
9.1.1 批量插入
可以使用 insert/insertMany/save
插入多条文档,与单条插入的主要区别是:把单条插入时的函数参数的对象类型 {}
变成数组类型 [{},{},{}]
db.member.insert({
"name": "zhangsan"
}, {
"name": "lisi"
}, {
"name": "wangwu"
})
db.member.insertMany({
"name": "zhangsan"
}, {
"name": "lisi"
}, {
"name": "wangwu"
})
db.member.save({
"name": "zhangsan"
}, {
"name": "lisi"
}, {
"name": "wangwu"
})
# 通过定义一个变量的方式 插入数据是非常方便的( 推荐)
user1 = {
"name": "zhangsan"
}
user2 = {
"name": "lisi"
}
user3 = {
"name": "wangwu"
}
db.member.insert([user1, user2, user3])
save方法如果指定了
_id
字段就是更新文档,否则就是插入一条新的文档数据
9.2 更新文档
通过 update
系列函数或者save
函数可以更新集合中的文档
update() 函数用于更新已经存在的文档,语法格式: db.database_name.update(query,update,options)
- query:update的查询条件,类似于SQl 更新中的 where 条件
- update:update的对象和一些更新的操作符等,相当于SQL更新中的set 部分,
- options:upsert-可选,如果不存在update文档,是否插入该文档。true为插入,默认是false,不插入,
- options:multi-可选,是否批量更新。true表示按照查询条件查询出来的多条记录全部更新;false是只更新找到的第一条记录,默认是false
user = {
"name": "lichao",
"age": "20",
"hobbies": ["music", "runing", "read"],
"addr": {
"County": "China",
"City": "ShangHai"
}
}
# 修改单条
db.member.updateOne({"name":"张三"},user)
# 查找的数据如果是多条,只会修改第一条
db.menber.update({"name":"张三"},user) 等价于 修改单条数据
# 查找的数据如果是多条,修改所有匹配到的数据
db.menber.update({"name":"张三"},{"$set":user},false,true)
# 修改多条
db.menber.updateMany({"name":"张三"},{"$set":user})
9.3 查询文档
9.3.1 查询所有
# 1. 查询集合中的所有
db.database_name.find()
# 2. 查询集合中的所有(去重)
db.database_name.distinct("name")
# 3. 查询集合中的所有(以格式化的展示数据)
db.database_name.find().pretty()
# 4. 查询name=zhangsan的所有
db.database_name.find({name:'zhangsan'})
# 5. 只显示name字段 查询所有数据
db.database_name.find({},{name:1})
# 6. 除了name字段,其余的字段都显示, 查询所有数据
db.database_name.find({},{name:0})
9.3.2 比较运算符查询
SQL运算符 | MongoDB运算符 |
---|---|
= | = |
!= | "$ne" |
> | "$gt" |
< | "$lt" |
>= | "$gte" |
<= | "$lte" |
# select * from user where id=3
db.user.find({"_id":"3"})
# select * from user where id!=3
db.user.find({"_id":{"$ne":3}})
# select * from user where id>3
db.user.find({"_id":{"$gt":3}})
# select * from user where id<3
db.user.find({"_id":{"$lt":3}})
9.3.3 逻辑运算查询
MongoDB中用 ,
分割多个条件是and关系,或者直接用 $and $or $not (与或非)
# select * from user where id >= 3 and id <= 10
db.user.find({
"_id": {
"$gte": 3,
"$lte": 10
}
})
# select * from user where id >= 3 and id <= 10 and age >= 30
db.user.find({
"_id": {
"$gte": 3,
"$lte": 10
}
}, {
"age": {
"$gte": 30
}
})
# 或者
db.user.find({
"$and": [{
"_id": {
"$gte": 3,
"$lte": 10
}
}, {
"age": {
"$gte": 30
}
}]
})
# select * from user where id >= 3 and id <= 10 or id <= 20 or name = 'lisi'
db.user.find({
"$or": [{
"_id": {
"$gte": 3,
"$lte": 10
}
}, {
"_id": {
"$lte": 20
}
}, {
"name": "lisi"
}]
})
# select * from user where id % 2 == 1
db.user.find({
"_id": {
"$mod": [2, 1]
}
})
# select * from user where id % 2 != 1
db.user.find({
"_id": {
"$not": {
"$mod": [2, 1]
}
}
})
9.3.4 $type操作符查询
# 查询name是字符串类型的数据
db.user.find({"name":{"$type":2}})
9.3.5 正则查询
主要是定义在 / /
中
# 查询 name是 z开头 u或者n 结尾的名称
# select * from user where name regexp '^z.*?(u|n)$'
db.user.find({'name':/^z.*?(u|n)$/i})
9.3.6 投影查询
只选择必要的字段,而不是整个文档所有的字段,设置字段列表值为1或0 即可
1显示字段 0隐藏字段 _id默认显示出来
# select name,age from user where id=3
db.user.find({"_id":3},{"name":1,"age":1})
# 如果想要 _id也隐藏,设置 _id:
db.user.find({"_id":3},{"_id":0,"name":1,"age":1})
9.3.7 数组查询
# 1. 查询hobbies中有read的人
db.user.find({"hobbies":"read"})
# 2. 查询hobbies中既有read也有tea爱好的人
db.user.find({"hobbies":{"$all":["read","tea"]}})
# 3. 查询索引为第二个 爱好为tea的人 (索引从0开始)
db.user.find({"hobbies.2":"tea"})
# 4. 查看所有人的第一个到第三个爱好(左闭右开)
db.user.find({},
// 查询条件为所有
{
"_id": 0,
"name": 0,
"age": 0 "hobbies": {
"$slice": [0, 2]
}
})
# 5. 查看所有人的最后2个爱好
db.user.find({},
// 查询条件为所有
{
"_id": 0,
"name": 0,
"age": 0 "hobbies": {
"$slice": -2
}
})
# 6 查询子文档有 "County": "China"的人,
user = {
"name": "lichao",
"age": "20",
"hobbies": ["music", "runing", "read"],
"addr": {
"County": "China",
"City": "ShangHai"
}
}
db.user.find({
"addr.County": "China"
})
3.8 排序查询
1升序
-1降序
# 1 按照姓名正序排列
db.user.find().sort({name:1})
# 2 按照 年龄 倒序,id正序 排列
db.user.find().sort({age:-1,_id:1})
9.3.9 分页查询
limit表示取多少个文档:每页显示多少条数据
skip表示跳过几个文档
db.user.find().skip((pageNum-1)*pageSize).limit(pageSize)
# pageSize: 每页显示多少条数据 (此处是每页显示2条数据)
# pageNum: 第几页(当前是第几页)
# 1. 前两个(第一页)
db.user.find().limit(2).skip(0)
# 2. 第三个和第四个(第二页)
db.user.find().limit(2).skip(2)
# 3. 第五个和第六个(第三页)
db.user.find().limit(2).skip(4)
9.3.10 统计
# 方式一:查询id大于3的人数
db.user.count({"_id":{"$gt":3}})
# 方式一:查询id大于3的人数
db.user.find({"_id":{"$gt":3}}).count()
9.3.11 聚合查询
语法:
db.集合名称.aggregate([{管道:{表达式}} ....])
1 常用管道
$group:将集合中的文档分组,用于统计结果
$match:过滤数据,只要输出符合条件的文档
$sort :聚合数据进一步排序
$skip:跳过指定文档数
$limit:限制集合数据返回文档数
2 常用表达式
$sum:计算总和,$sum:1 同 count 统计
$avg:计算平均值
$min :获取集合中所有文档对应值的最小值
$max :获取集合中所有文档对应值的最大值
$push :在结果文档中插入值到一个数组中
$addToSet :在结果文档中插入值到一个数组中, 但不创建副本
$first :根据资源文档的排序获取第一个文档数据
$last :根据资源文档的排序获取最后一个文档数据
3 $match和$group
$group:将集合中的文档分组,用于统计结果,相当于 mysql中 的 group by
$match:过滤数据,只要输出符合条件的文档,,相当于 mysql中 的 where
可以使用任何查询操作符 $gt $lt $in 等
{"$match":{"字段":"条件"}}
{"$group":{"_id":分组字段,"新的字段名":聚合操作符}}
案例展示:
# 数据结构参考
emp1 = {
"_id": 1,
"name": "张三",
"sex": "male",
"age": "20",
"hire_date": "20151212", # 入职时间
"post": "公务员", # 职位
"salary": "7800.00", # 薪资
}
# 1. 查询职位为公务员的员工
# select * from db.emp where post="公务员"
db.emp.aggregate([{"$match"}:{"post":"公务员"}])
# 2. 以post分组,查询id大于3的所有员工的平均工资
# select * from db.emp where id>3 group by post
db.emp.aggregate([ "$match":{"_id":{"$gt":3}}, {"$group":{"_id":"$post","avg_salary":{"$avg":"$salary"}}}])
# 3. post分组,查询id大于3的所有员工的平均工资,平均工资大于10000
# select * from db.emp where id>3 group by post having avg(salary) > 10000
db.emp.aggregate([ "$match":{"_id":{"$gt":3}}, {"$group":{"_id":"$post","avg_salary":{"$avg":"$salary"}}}, "$match":{"avg_salary":{"$gt":10000 }}])
# 4. group分组:group函数的_id字段即可
# 按照职位分组
{"$group":{"_id":"$post"}}
# 按照性别分组
{"$group":{"_id":"$sex"}}
# 按照多个字段分组
{"$group":{"_id":{"state":"$state","sex":"$sex"}}}
# 5. 以post分组,获取所有员工中的最大薪资
# select post,max(salary) from db.emp group by post
db.emp.aggregate([ {"$group":{"_id":"$post","max_salary":{"$max":"$salary"}}},])
# 6. 获取每个部分的最大薪资和最小薪资
db.emp.aggregate([ {"$group":{"_id":"$post","max_salary":{"$max":"$salary","mix_salary":{"$mix":"$salary"}}},])
# 7. 获取每个部门的总工资,并倒序排序
db.emp.aggregate([ {"$group":{"_id":"$post","count":{"$sum":"$salary"},{"$sort":"count":1}}},])
# 8. 求每个部门的总人数
db.emp.aggregate([ {"$group":{"_id":"$post","count":{"$sum":1},{"$sort":"count":1}}},])
# 9. 查询岗位名以及各岗位内的员工姓名
select post,group_concat(name) from db.emp group by post
# 包含重复的数据
db.emp.aggregate([ {"$group":{"_id":"$post","names":{"$push":"$name"}}},])
# 去除重复的数据
db.emp.aggregate([ {"$group":{"_id":"$post","names":{"$addToSet":"$name"}}},])
4 $project
用于投射,即设定该键值对是否保留,1保留 0不保留
可对原有的键值对做操作后增加自定义表达式(查询哪里要显示的列)
{"$project":{"要保留的字段名":1,"要去掉的字段名":0,"新增的字段名":"表达式"}}
select name, post, (age + 1) as new_age from db1.emp
db.emp.aggregate([{
$project: {
"name": 1,
"post": 1,
"new_age": {
"$add": {
"$age": 1
}
}
}
}])
5 $sort $limit $skip
排序:{"$sort":{"字段名":1,"字段名":-1}}
限制:{"$limit":n} 限制多少个文档
跳过:{"$skip":n} 跳过多少个文档
# 1. 获取平均工资最高的前2个部门
db.emp.aggregate([{
"$group": {
"_id": "$post",
"平均工资": {
"$avg": "$salary"
}
}
}, {
"$sort": {
"平均工资": -1
}
}, {
"$limit": 2
}])
# 2. 获取平均工资最高的第2个部门
db.emp.aggregate([{
"$group": {
"_id": "$post",
"平均工资": {
"$avg": "$salary"
}
}
}, {
"$sort": {
"平均工资": -1
}
}, {
"$limit": 2
}, {
"$skip": 1
}])
6 $sample:随机选取n个
# 随机获取3个文档
db.emp.aggregate([{
"$sample": {
size: 3
}
}])
7 $concat $substr $toLower $toUpper
{"$substr":[$值为字符串的字段名,起始位置,截取几个字节]}
{"$toUpper":expr} # 转大写
{"$toLower":expr} # 转小写
{"$concat":[expr1,expr2,...,exprN]} # 指定的表达式或字符串连接在一起返回
1. 截取字符串db.emp.aggregate([{$project:{"_id":0,"str":{$substr:["$sex",0,2]}}}])
2. 拼接db.emp.aggregate([{$project:{"name":1, "post":1,"name_sex":{$concat:["$name","测试拼接数","$sex"]}}}])
3. 将性别的英文转为大写
db.emp.aggregate([{"$project":{"sex":{"$toUpper":"$sex"}}}])
8 索引---实现高效的数据查询
索引是一种排序好的便于快速查询的数据结构,可以帮助数据库高效的查询数据
索引的优缺点:
- 优点:提高数据查询的效率,降低数据库的IO成本;通过索引对数据进行排序,降低数据排序的成本,降低CPU的消耗
- 缺点:占用磁盘空间;大量索引影响SQL语句效率,因为每次插入和修改数据都需要更新索引
选择设置索引的规则:
-
- 为常做条件、排序、分组的字段建立索引
-
- 选择唯一性索引
-
- 选择较小的数据列,为较长的字符串使用前缀索引
创建索引
db.datatabase_name.ensureIndex({KEY:1})
KEY是要指定索引的字段名;
1是升序 -1是降序
1.1 按照降序将name设置为索引
例如: db.emp.ensureIndex({"name":-1})
1.2 也可以指定所建立的索引的名称(类似于索引的别名)
例如: db.emp.ensureIndex({"name":-1},{"name":"nameIndex"})
1.3 给name和age添加组合索引
例如: db.emp.ensureIndex({"name":-1,"age":1})
1.4 创建唯一索引
db.emp.createIndex({"name":-1,"age":1}) 与 1.3创建组合索引是是一样的
db.emp.createIndex({name:1}, {unique: "name"}) 当你设置字段为唯一索引时,那么此字段的值就是唯一的
查询索引
db.emp.getIndexes()
删除索引
db.database_name.dropIndex(indexname)
例如: db.emp.dropIndex("nameIndex")db.database_name.dropIndexes();
# 删除全部索引,但是系统默认的索引还是存在的
分析索引
db.database_name.find().explain('executionStats')
扫描方式: > COLLSCAN 全表扫描 > IXSCAN 索引扫描 > FETCH 根据索引去检索指定document
分析索引:
9.4 删除文档:remove()
remove()函数用来移除集合中的文档,语法如下:
db.database_name.remove(<query>,{justOne:<boolean>})
参数:
query:删除文档的条件(可选)
justOne:true,只删除一个文档;false删除所有匹配的文档;(可选)
删除所有
db.database_name.remove({})
# 或者
db.database_name.deleteMany({})
只删除符合条件的第一个
db.database_name.deleteOne(<query>)
10. MongoDB备份和还原
10.1 备份数据库
# 备份导出数据语法:
mongodump -h -port -u -p -d -o-h
host 服务器IP地址(一般不写 默认本机)
-port 端口(一般不写 默认27017)
-u user 账号
-p pwd 密码-d
database 数据库(数据库不写则导出全局)
-o open
# 备份到指定目录下备份所有数据库
mongodump -u admin -p admin888 -o D:\mongodb\bak,
# 备份指定数据库 shop
mongodump -u admin -p admin888 -d shop -o D:\mongodb\bak2
10.2 还原数据库
# 还原数据裤语法:
mongorestore -h -port -u -p -d --drop
# 备份数据目录-h -port -u -p-d
# 不写则还原全部数据库--drop
# 先删除数据库再导入还原所有数据
mongorestore -u admin -p admin888 --drop D:\mongodb\bak,
# 还原指定数据
mongorestore -u shop2 -p admin888 -d shop --drop D:\mongodb\bak2\shop
11. nodejs操作mongoose
11.1 简介
mongoose 是node中提供操作MongoDB的模块,它是基于 MongoDB 官方的 mongodb 包进一步做了封装,可以提高开发效率,让你操作 MongoDB 数据库更方便
能够通过node语法实现MongoDB数据库增删改查,从而实现用node写程序来管理MongoDB数据库
schema: 约束字段/列数据
主要作用:用来约束MongoDB文档数据(哪些字段必须,哪些字段可选的)
model:模型
一个模型 对应 一个集合,主要用它来实现数据的增删改查
通过模型来管理集合中的数据
11.2 mongoose使用
1 项目创建和下载安装依赖
# 1. 创建项目目录
mkdir mongodb-demo
# 2. 进入项目目录
cd mongodb-demo
# 3. 初始化项目
npm init -y
# 4. 下载安装mongoose第三方模块
# --save 可以省略,npm 5.0版本以后
npm install mongoose --save # 或者 npm i mongoose
2 mongoose 构建
// 0. 引入第三方模块 mongoose
const mongoose = require('mongoose');
// 1. 连接本机数据库 school ,如果指定连接的数据库不存在,当你插入第一条数据之后就会自动被创建出来
mongoose.connect('mongodb://localhost/school')
// 2. 设置集合(表)的数据架构 Schema
var Schema = mongoose.Schema({
name: {
type: String,
required: true, // 必填
},
age: Number, // 年龄
// 性别
gender: {
type: Number,
enum: [0, 1], // 枚举数据
default: 0, // 默认值
},
job: String, // 职位
});
/*
3. 将文档结构发布 一个 名为 student 的模型
mongoose.model 方法就是用来将一个架构发布为 model
第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称,mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称:例如这里的 Student 最终会变为 students 集合名称
第二个参数:架构 Schema
返回值:模型构造函数
*/
var model_student = mongoose.model('Student', Schema);
3 增删改查
3.1 保存/新增 数据 save
// 创建实例对象
var stuObj = new model_student({
name: '李玉霞',
age: 23,
gender: 0,
job: '医生'
});
// 将数据对象 保存 到集合
stuObj.save(function(err, result) {
if (err) return console.error(err);
console.log('保存成功');
});
3.2 查询数据
// 1. 查询 所有 数据
model_student.find(function(err, result) {
if (err) return console.error(err);
console.log(result);
})
// 2. 查询 name = 'zhangsan' 的所有数据
model_student.find({
name: 'zhangsan'
}, function(err, result) {
if (err) return console.error(err);
console.log(result);
})
// 3. 查询 name = 'zhangsan' 一条的数据
model_student.findOne({
name: 'zhangsan'
}, function(err, result) {
if (err) return console.error(err);
console.log(result);
})
3.3 删除数据
// 1. 删除 name = 'zhangsan' 的所有数据
model_student.remove({
name: 'zhangsan'
}, function(err, result) {
if (err) return console.error(err);
console.log('删除成功');
})
// 根据 id 删除一个
model_student.findByIdAndRemove(id,[options],callback)
// 根据 条件 删除一个
model_student.findOneAndRemove(id,[options],callback)
3.4 更新数据
// 根据id 更新数据
model_student.findByIdAndUpdate('6073c730a1cf34434845a151', {
name: 'lisi'
}, function(err, result) {
if (err) return console.error(err);
console.log('更新成功');
})
// 根据条件更新所有
model_student.update(conditions,doc,[,options],callback)
// 根据指定的条件更新一个
model_student.findOneAndUpdate(conditions,doc,[,options],callback)
// 根据id更新一个
model_student.findByIdAndUpdate(id,doc,[,options],callback)
3.5 分页查询,每页显示2条数据
model_student.find({}).skip(1).limit(2).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})