简单介绍
下载地址:https://www.mongodb.com/download-center/community
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。
MongoDB与关系型数据库术语对比———————MongoDB不支持连接查询
MongoDB与关系型数据库存储数据对比
MongoDB的数据类型
安装
在Linux平台的MongoDB为解压版。我们只要解压tgz文件就可以使用。
安装依赖
MongoDB4.2.1需要依赖openssl10相关资源,必须先在Linux中安装openssl10。
yum install -y make
rpm -i compat-openssl10-1.0.2o-3.el8.x86_64.rpm
上传解压并移动
tar -zxf mongodb-linux-x86_64-rhel70-4.2.1.tgz
mv mongodb-linux-x86_64-rhel70-4.2.1 /opt/mongodb
创建数据、日志存储目录——————必须手动创建
mkdir -p /opt/mongodb/data/db/
mkdir -p /opt/mongodb/logs/
touch /opt/mongodb/logs/mongodb.log
至此MongoDB安装完毕
启动、停止和客户端连接
启动MongoDB
MongoDB的启动方式分为两种—————-无论哪种启动方式都需要执行bin目录中的mongod命令
1)前置启动
2)后置启动
前置启动
MongoDB的默认启动方式为前置启动。所谓前置启动就是MongoDB启动进程后会占用当前终端窗口。
/opt/mongodb/bin/mongod --dbpath /opt/mongodb/data/db/
后置启动
所谓后置启动就是以守护进程的方式启动MongoDB。命令中的fork就是开启子进程。
命令为:
/opt/mongodb/bin/mongod --dbpath /opt/mongodb/data/db/ --logpath /opt/mongodb/logs/mongodb.log --fork
配置启动(使用配置文件来启动)
命令启动并不适合管理,毕竟每次输入命令都需要考虑各参数的配置。可以通过配置文件来配置启动参数,这样在管理MongoDB上比较方便。
mkdir /opt/mongodb/etc/
vi /opt/mongodb/etc/mongodb.conf
dbpath=/opt/mongodb/data/db/
logpath=/opt/mongodb/logs/mongodb.log
bind_ip_all=true
port=27017
fork=true
port是端口,27017即默认的MongoDB端口号。
bind_ip_all是绑定所有IP,即所有客户端都可以访问。
/opt/mongodb/bin/mongod --config /opt/mongodb/etc/mongodb.conf
客户端访问
通过bin目录中的mongo来访问MongoDB服务器。
/opt/mongodb/bin/mongo --host 连接的主机地址(默认127.0.0.1) --port 端口(默认27017)
/opt/mongodb/bin/mongo --host --port
停止MongoDB
前置启动关闭———————-使用 ctrl + c即可关闭
使用kill命令关闭—————-kill -9 进程号
mongod命令关闭
mongod --shutdown --dbpath <path>
案例:/opt/mongodb/bin/mongod --shutdown --dbpath /opt/mongodb/data/db/
MongoDB函数关闭————-连接到MongoDB服务后,切换到admin库,并使用命令关闭服务。
use admin
db.shutdownServer()
db.runCommand(“shutdown”)
用户管理操作
Mongodb作为时下最为热门的数据库,那么其安全验证也是必不可少的,否则一个没有验证的数据库暴露出去,任何人可随意操作,这将是非常危险的。
MongoDB中的常用权限
read | 允许用户读取指定数据库 |
---|---|
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile |
userAdmin | 允许用户向system.users集合写入,可以在指定数据库里创建、删除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
dbAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
创建用户
MongoDB默认不做任何的用户权限认证,如果需要进行用户权限认证,则需要在启动的时候做认证参数开启,配置如下:
dbpath=/opt/mongodb/data/db/
logpath=/opt/mongodb/logs/mongodb.log
port=27017
fork=true
auth=true
第一步:切换到admin库————use admin
第二步:创建用户的函数格式如下
db.createUser({
user: "<name>",
pwd: "<cleartext password>",
customData: { <any information> },
roles: [
{ role: "<role>", db: "<database>" } | "<role>",
...
]
});
1)user:新建用户名。
2)pwd:新建用户密码。
3)customData:存放一些用户相关的自定义数据,该属性也可忽略。
4)roles:数组类型,配置用户的权限。
db.createUser({user:'bjsxt',pwd:'bjsxt',roles:[{role:'root',db:'admin'}])
登录认证——————-登录的用户必须进入到admin库才能进行登录认证。
- 进入admin库—————-use admin
- 认证————————————db.auth(‘用户名’, ‘密码’);
查看用户————————-拥有用户管理权限的用户才能查看用户信息。
- 进入admin库—————-use admin
- 查看——两者都是查看
show user db.system.users.find()
更新用户——语法是————————-db.updateUser(‘要更新的用户名’, {新的用户数据对象});
更新用户必须在admin库中执行。
use admin
db.updateUser('bjsxt', {'pwd':'123', 'roles':[{'role':'root', 'db':'admin'}]});
修改用户密码函数
虽然更新用户函数也可以修改用户密码,MongoDB也提供了独立修改密码的函数,函数语法如下:
db.changeUserPassword("用户名","新密码")
- 进入admin库——-use admin
- 修改密码
db.changeUserPassword('bjsxt', '111');
删除用户
函数语法————-db.dropUser(‘用户名’)
- 进入admin库——————use admin
- 删除用户
db.dropUser('bjsxt');
Database操作
创建数据库:
在MongoDB中创建数据库的命令使用的是use命令。该命令有两层含义:
1)切换到指定数据库。
2)如果切换的数据库不存在,则创建该数据库。
我们使用use命令创建一个名为sxttest的数据库————————use sxttest
查看数据库
我们可以通过show dbs | show databases 命令查看当前MongoDB中的所有数据库。
删除数据库——————-先要切换到该数据库中
在MongoDB中使用db.dropDatabase()函数来删除数据库。在删除数据库之前,需要使用具备dbAdminAnyDatabase角色的管理员用户登录,然后切换到需要删除的数据库,执行db.dropDatabase()函数即可。删除成功后会返回一个{ “ok” : 1 }的JSON字符串。
Collection操作
MongoDB中的集合是一组文档的集,相当于关系型数据库中的表。
创建集合
MongoDB使用db.createCollection()函数来创建集合。
语法格式:db.createCollection(name, options)。—————-默认创建的集合是没有大小的
- name: 要创建的集合名称。
- options: 可选参数, 指定有关内存大小及索引的选项。
options 可以是如下参数。
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。 |
autoindexid | 布尔 | (可选)如为 true,自动在 _id 字段创建索引。默认为 true。 |
size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。如果 capped 为 true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。
默认创建集合
在MongoDB中,我们也可以不用创建集合,当我们插入一些数据时,会自动创建集合,并且会使用文档管理命令中的集合名称作为集合的名称。文档管理命令后续课程详解。
db.collname.insert({'name':'bjsxt'});
show dbs;
函数创建集合
在数据库中创建一个名为test的集合,该集合创建时不指定任何参数。
db.createCollection('test')
在数据库中创建一个名为test2的固定集合,整个集合空间大小为2000000kb,文档最大个数为1000。
db.createCollection('test2', {'capped':true, 'size':2000000, 'max':1000});
查看集合
show collections
show tables
查看集合详情
db.dev2.stats();
删除集合
如果我们要删除集合,需要先切换到需要删除集合所在的数据库,使用drop()函数删除集合即可。
删除集合的语法格式为:db.集合名称.drop()。
db.test2.drop();
Document 操作
在MongoDB中文档是指多个键及其关联的值有序地放置在一起就是文档,其实指的就是数据,也是我们平时操作最多的部分。
MongoDB中的文档的数据结构和 JSON 基本一样。所有存储在集合中的数据都是 BSON 格式。
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
新增文档
新增单个文档
insert函数
db.test.insert({title:'北京尚学堂',
description:'程序员的摇篮',
url:'www.bjsxt.com',
tags:['java','大数据','python'],
'time':new ISODate('2020-01-01T10:10:10.000Z')})
save函数———————向test集合中插入单个文档。
db.test.save({title:'百战程序员',
description:'身经百战,高薪相伴',
url:'www.itbaizhan.cn',
tags:['javaWeb实战','数据库实战','微服务实战']})
insertOne函数———————————在MongoDB3.2以后的版本中,提供了insertOne()函数用于插入文档。
db.test.insertOne({title:'尚学堂大数据',
description:'培养大数据人才的摇篮',
url:'www.bjsxt.com',
tags:['hadoop','spark','Hbase']})
批量新增文档
insert函数
db.test.insert([
{title:'java',tags:['JavaSE','JavaEE','JavaME']},
{title:'ORM',tags:['Mybatis','Hibernate']},
{title:'Spring',tags:['SpringMVC','SpringBoot','SpringCloud']}
])
save函数
db.test.save([
{title:'java',tags:['JavaSE','JavaEE','JavaME']},
{title:'ORM',tags:['Mybatis','Hibernate']},
{title:'Spring',tags:['SpringMVC','SpringBoot','SpringCloud']}
])
insertMany函数—————在MongoDB3.2以后的版本中,提供了insertMany函数用于插入文档。
db.test.insertMany([
{title:'java',tags:['JavaSE','JavaEE','JavaME']},
{title:'ORM',tags:['Mybatis','Hibernate']},
{title:'Spring',tags:['SpringMVC','SpringBoot','SpringCloud']}
])
通过变量新增文档
Mongo Shell工具允许我们定义变量。所有的变量类型为var类型。也可忽略变量类型。变量中赋值符号后侧需要使用小括号来表示变量中的值。我们可以将变量作为任意插入文档的函数的参数。
语法格式:变量名=(<变量值>)
变量新增单一文档
document=({title:'SpringCloud',
tags:['Spring Cloud Netflix',
'Spring Cloud Security',
'Spring Cloud Consul']
})
变量批量新增文档
document=([
{title:'SpringCloud',tags:['Spring Cloud Netflix',
'Spring Cloud Security',
'Spring Cloud Consul']},
{title:'SpringBoot',tags:['Spring Boot']}
])
查询文档
MongoDB是通过findOne()和find()函数来实现文档查询的。
findOne函数———————————-findOne函数用于查询集合中的一个文档
db.集合名称.findOne({<query>},{<projection>});
参数解释:
query:可选,代表查询条件。
projection:可选,代表查询结果的投影字段名。即查询结果需要返回哪些字段或不需要返回哪些字段。
注意:在projection中不能使用{‘name’:0, ‘age’:1}这种语法格式,这是错误的语法。projection只能定义要返回的字段或不返回的字段。_id字段是MongoDB维护的字段,是唯一可以在projection中独立使用的。如:{_id:0, ‘name’:1, ‘age’:1}
find函数——————————-find函数用于查询集合中的若干文档
db.stu.find({<query>},{<projection>});
参数解释:
query:可选,代表查询条件。
projection:可选,代表查询结果的投影字段名。即查询结果需要返回哪些字段或不需要返回哪些字段。
投影约束
在MongoDB中,_id字段是默认返显示的投影字段。
查询stu集合中所有文档,且只显示name字段
db.stu.find({},{'name':1});
查询stu集合中所有文档,且显示除name字段以外的其他字段
db.stu.find({},{'name':0});
非_id字段,在投影约束中不能互斥,否则抛出异常。如:{“name”:1, “age”:0}抛出异常。
包含id字段,在投影约束中可以和其他字段互斥约束,但是,id字段必须为非投影显示约束(0)
包含id字段,在投影约束中,如果和其他字段投影约束互斥,且id字段投影约束为显示(1),会抛出异常,
pretty函数
pretty函数用于格式化find函数查询结果。让查询结果更易查看。findOne函数自动附带格式化查询结果的能力。
db.stu.find().pretty();
单条件逻辑运算符
如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | {:}或{:{$eq:}} | db.col.find({“by”:”尚学堂”}).pretty() db.col.find({“by”:{$eq:”尚学堂”}}) |
where by = ‘尚学堂’ |
小于 | {:{$lt:}} | db.col.find({“likes”:{$lt:50}}).pretty() | where likes < 50 |
小于等于 | {:{$lte:}} | db.col.find({“likes”:{$lte:50}}).pretty() | where likes <= 50 |
大于 | {:{$gt:}} | db.col.find({“likes”:{$gt:50}}).pretty() | where likes > 50 |
大于等于 | {:{$gte:}} | db.col.find({“likes”:{$gte:50}}).pretty() | where likes >= 50 |
不等于 | {:{$ne:}} | db.col.find({“likes”:{$ne:50}}).pretty() | where likes <> 50 |
在MongoDB中字符串也可以比较大小。按照Unicode编码顺序比较大小
日期可以比较大小: 过往 < 现在 < 未来
多条件逻辑运算符
And条件
MongoDB 的 find() 和 findOne() 函数可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。
查询stu集合中name字段为lisi,且age字段大于20的文档
db.stu.find({'name':'lisi', 'age':{'$gt':20}});
Or条件
MongoDB OR 条件语句使用了关键字 $or
db.集合名称.find({$or: [{key1: value1}, {key2:value2}]})
查询集合stu中name字段为zhangsan,或name字段为lisi且age字段为22的文档。
db.stu.find({
$or:[
{'name': 'zhangsan'},
{'name':'lisi', 'age':22}
]
});
$type查询
在MongoDB中根据字段的数量类型来查询数据使用$type操作符来实现
db.集合名.find({属性名:{$type:类型值}}) //这里的类型值能使用Number也能使用alias
$type的有效值如下:
Type | Number | Alias | Notes |
---|---|---|---|
Double | 1 | “double” | |
String | 2 | “string” | |
Object | 3 | “object” | |
Array | 4 | “array” | |
Binary data | 5 | “binData” | |
Undefined | 6 | “undefined” | Deprecated. |
ObjectId | 7 | “objectId” | |
Boolean | 8 | “bool” | |
Date | 9 | “date” | |
Null | 10 | “null” | |
Regular Expression | 11 | “regex” | |
DBPointer | 12 | “dbPointer” | Deprecated. |
JavaScript | 13 | “javascript” | |
Symbol | 14 | “symbol” | Deprecated. |
JavaScript (with scope) | 15 | “javascriptWithScope” | |
32-bit integer | 16 | “int” | |
Timestamp | 17 | “timestamp” | |
64-bit integer | 18 | “long” | |
Decimal128 | 19 | “decimal” | New in version 3.4. |
Min key | -1 | “minKey” | |
Max key | 127 | “maxKey” |
查询stu集合中name字段类型为字符串的文档:
db.stu.find({'name':{$type:2}});
db.stu.find({'name':{$type:'string'}});
正则查询
MongoDB中查询条件也可以使用正则表达式作为匹配约束
db.集合名称.find({字段名:正则表达式});
db.集合名称.find({字段名:{$regex:正则表达式[, $options:正则选项]}});
正则表达式格式:
正则选项:i,m,x,s可以组合使用
i - 不区分大小写以匹配大小写的情况。
m - 对于包含锚点的模式(即^对于开始, $对于结尾),在每行的开头或结尾处匹配具有多行值的字符串(可解析单字段中的换行符\n)。如果没有此选项,这些锚点将在字符串的开头或结尾处匹配。
x - 设置x选项后,正则表达式中的非转义的空白字符将被忽略。需要options语法
s - 允许点字符(即.)匹配包括换行符在内的所有字符。需要options语法
查询stu集合中name字段以’z’开头的数据
db.stu.find({'name':/^z/});
db.stu.find({'name':{$regex:/^z/}});
分页查询
在MongoDB中,使用函数limit()和skip()来实现分页数据查询。
limit函数——不使用limit函数,默认查询集合中全部文档。如果limit函数不传递参数,也是查询集合中的全部文档
db.集合名称.find().limit(查询数量);
db.stu.find().limit(10);
skip函数———在MongoDB中使用skip()方法来跳过指定数量的文档,skip方法同样接受一个数字参数作为跳过的文档条数
db.集合名称.find().[limit(查询数量).]skip(跳过数量);
排序
在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
db.集合名称.find().sort({key1:sortType1, key2:sortType2});
db.stu.find().sort({'age':1});
db.stu.find().sort({'age':1, 'name':-1});
更新文档
MongoDB通过update函数与save函数来更新集合中的文档
save更新文档
save()函数的作用是保存文档,如果文档存在则覆盖,如果文档不存在则新增。save()函数对文档是否存在的唯一判断标准是”id”系统唯一字段是否匹配。所以使用save()函数实现更新操作,则必须提供”_id”字段数据。
db.集合名称.save(
<document>
);
使用save()函数实现更新操作:
db.test.save({
"_id" : ObjectId("5d0207e460ad10791be757d2"),
"title" : "MongoDB 教程",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "北京尚学堂",
"tags" : ["mongodb","NoSQL"],
"likes" : 100
})
update更新文档———————————update() 函数用于更新已存在的文档。
db.集合名称.update(
<query>,
<update>,
< upsert:boolean>,
< multi:boolean>
)
参数说明:
query : update的查询条件
update : update的对象和一些更新的操作符等
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入这个document,true为插入,默认是 false,不插入。只有在表达式更新语法中才可使用。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。只有在表达式更新语法中才可使用。
在MongoDB中的update是有两种更新方式,一种是覆盖更新,一种是表达式更新。
覆盖更新
通过update方法来更新一个完整的文档:
db.col.update(
{"title" : "MongoDB 教程"},
{
"title" : "MongoDB 教程",
"description" : "MongoDB 是一个 Nosql 数据库",
"by" : "北京尚学堂",
"likes" : 100
}
)
表达式更新
db.集合名称.update(
<query>,
<doc_projection>,
<upsert>,
<multi>
);
{
$表达式:{具体更新规则}
}
$inc
用法:{$inc:{field:value}}
作用:对一个数字字段的某个field增加value
示例:将name为zhangsan的学生的age增加5db.stu.update({name:"zhangsan"},{$inc:{age:5}})
$set
用法:{$set:{field:value}}
作用:把文档中某个字段field的值设为value,如果field不存在,则增加新的字段并赋值为value。
示例:把zhangsan的年龄设为23岁db.stu.update({name:"zhangsan"},{$set:{'age':23}})
$unset
用法:{$unset:{field:1}}
作用:删除某个字段field
示例:将zhangsan的年龄字段删除db.stu.update({name:"zhangsan"},{$unset:{age:1}})
$push
用法:{$push:{field:value}}
作用:把value追加到field里。注:field只能是数组类型,如果field不存在,会自动插入一个数组类型
示例:给zhangsan添加别名”xiaozhang”db.stu.update({name:"zhangsan"},{$push:{"alias":"xiaozhang"}})
$addToSet
用法:{$addToSet:{field:value}}
作用:加一个值到数组内,而且只有当这个值在数组中不存在时才增加。
示例:往zhangsan的别名字段里添加两个别名A1、A2db.stu.update({name:"zhangsan"},{$addToSet:{"alias":["A1","A2"]}})
注意:此处加入的数据是一个数据为A1和A2的数组对象。并不是将两个数据依次加入alias数组中。
$pop
用法:删除数组内第一个值:{pop:{field:1}}
作用:用于删除数组内的一个值
示例:删除zhangsan记录中alias字段中最后一个别名db.stu.update({name:"zhangsan"},{$pop:{"alias":1}})
$pull
用法:{$pull:{field:_value}}
作用:从数组field内删除所有等于_value的值
示例:删除zhangsan记录中的别名xiaozhangdb.stu.update({name:"zhangsan"},{$pull:{"alias":"xiaozhang"}})
$pullAll
用法:{$pullAll:value_array}
作用:用法同$pull一样,可以一次性删除数组内的多个值。
示例:删除zhangsan记录内的A1和A2别名db.stu.update({name:"zhangsan"},{$pullAll:{"alias":["A1","A2"]}})
$rename
用法:{$rename:{old_field_name:new_field_name}}
作用:对字段进行重命名。底层实现是先删除old_field字段,再创建new_field字段。
示例:把zhangsan记录的name字段重命名为sname
db.stu.update({name:"zhangsan"},{$rename:{"name":"sname"}})
删除文档
MongoDB是通过remove()函数、deleteOne()函数、deleteMany()函数来删除集合中的文档。
remove函数
db.集合名称.remove(
<query>,
<justOne:boolean>
);
参数说明:
query:要删除的文档条件
justOne:可选参数,布尔类型,代表是否只删除第一个匹配条件满足的文档。默认值为false,代表删除全部满足匹配条件的文档。
注意:此方法已经过时,官方推荐使用deleteOne()和deleteMany()函数来实现删除操作。且在4.0-版本中,remove()函数并不会真正的释放存储空间,需要使用db.repairDatabase()函数来释放存储空间。在4.2.1版本中,删除函数repairDatabase()。
删除全部
db.stus.remove({});
条件删除
db.stus.remove({'age':10});
deleteOne函数
db.集合名称.deleteOne({<query>});
db.stus.deleteOne({'name':'zhangsan'});
deleteMany函数
db.集合名称.deleteMany({<query>});
db.stus.deleteMany({'age':{'$gt':10}});
内置函数
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。聚合框架是MongoDB的高级查询语言,允许我们通过转化合并由多个文档的数据来生成新的在单个文档里不存在的文档信息。通俗一点来说,可以把MongoDB的聚合查询等价于SQL的GROUP BY语句。
db.coll.insertMany([
{"name":"zhangsan", "age":20, "gender":"male", "birth":new ISODate('2000-01-01')},
{"name":"lisi", "age":21, "gender":"male", "birth":new ISODate('1999-02-01')},
{"name":"wangwu", "age":20, "gender":"female", "birth":new ISODate('2000-01-01')},
{"name":"zhaoliu", "age":22, "gender":"female", "birth":new ISODate('1998-01-01')}
]);
aggregate函数———————MongoDB中聚合的方法使用aggregate()。
db.集合名称.aggregate(<agg_options>)
参数解释:
agg_options:数组类型参数,传入具体的聚合表达式要求,来计算聚合结果。此参数代表聚合规则,如计算总和、平均值、最大最小值等。在MongoDB的聚合查询中,操作复杂度都在这里。
求和 $sum
db.coll.aggregate([{"$group":{"_id":null, "sum_age":{"$sum":"$age"}}}])
语法解释:
- $group:分组,代表聚合的分组条件
- id:分组的字段。相当于SQL分组语法group by column_name中的column_name部分。如果根据某字段的值分组,则定义为id:’$字段名’。
- sum_age:返回结果字段名。可以自定义,类似SQL中的字段别名。
- $sum:求和表达式。相当于SQL中的sum()。
- 在MongoDB中,字符串是不会进行数学相关运算的。得到的求和结果一定是0。
统计文档数量:
db.coll.aggregate([{"$group":{"_id":"$age", "count":{"$sum":1}}}])
语法解释:
- _id:’$age’ ——————-根据age字段的值进行分组。
- doc_ num : {$sum : 1} ————求和计算。每个符合条件的文档计数为1,进行累加求和。返回的结果作为字段doc_num的值。
条件筛选
db.coll.aggregate([
{
"$match":{"age":{"$lt":22}}
},
{
"$group":{"_id":null, "count":{"$sum":1}}
}
])
语法解释:
- $match:匹配条件
最大值 $max
db.coll.aggregate([{"$group":{"_id":null, "max_age":{"$max":"$age"}}}])
最小值 $min
db.coll.aggregate([{"$group":{"_id":null, "min_age":{"$min":"$age"}}}])
平均值 $avg
db.coll.aggregate([{"$group":{"_id":null, "avg_age":{"$avg":"$age"}}}])
字符串拼接
db.coll.aggregate([{"$project":{"all_info":{"$concat":["$name"," - ","$gender"]}}}])
$project - 管道,进行字符串拼接处理,日期处理等操作的函数
{"all_info":{"$concat":["$name"," - ","$gender"]}} - all_info定义别名, 处理后的结果的别名
"$concat":["$name"," - ","$gender"] - $concat - 字符串拼接表达式(函数),
对应的参数值是一个数组,数组中的每个元素就是要拼接的字符串,"字面值"或"$字段名"。
字符串转大写
db.coll.aggregate([{"$project":{"upperName":{"$toUpper":"$name"}}}])
字符串转小写
db.coll.aggregate([{"$project":{"lowerGender":{"$toLower":"$gender"}}}])
db.coll.aggregate([
{"$project":{
"lowerGender":{"$toLower":"$gender"},
"upperName":{"$toUpper":"$name"}
}
}
])
截取字符串
db.coll.aggregate([
{"$project":
{"subName":{"$substr":["$name", 0, 3]}}
}
])
日期格式化
db.agg.aggregate([
{'$project':
{'str':{'$dateToString':{'format':'%Y年%m月%d日 %H:%M:%S', 'date':'$birth'}}}
}
]);
使用MongoDB中的日期占位表达式格式化birth字段的数据。%Y年、%m月、%d日、%H 24小时制、%M分钟、%S秒
运算符
在MongoDB中,数学类型(int/long/double)和日期类型(date)可以做数学运行。日期只能做加减。日期加减单位是毫秒。因为MongoDB底层记录日期的方式是:1970-01-01 00:00:00.000到日期所在时间经历的毫秒数。
加法
db.agg.aggregate([{$project:{name:1, new_age:{$add:['$age', 1]}}}]);
减法
db.agg.aggregate([{$project:{name:1, new_age:{$subtract:['$age', 1]}}}]);
乘法
db.agg.aggregate([{$project:{name:1, new_age:{$multiply:['$age', 2]}}}]);
除法
db.agg.aggregate([{$project:{name:1, new_age:{$divide:['$age', 2]}}}]);
取模
db.agg.aggregate([{$project:{name:1, new_age:{$mod:['$age', 2]}}}]);
索引
简介:
建立索引后,MongoDB会额外存储一份按字段升序/降序排序的索引数据,索引结构通常采用类似btree的结构持久化存储,以保证从索引里快速(O(logN)的时间复杂度)找出某个值对应的位置信息,然后根据位置信息就能读取出对应的文档。简单的说,索引就是将文档按照某个(或某些)字段顺序组织起来,以便能根据该字段高效的查询。
在MongoDB3版本后,创建集合时默认为系统主键字段_id创建索引。且在关闭_id索引创建时会有警告提示。因为_id字段不创建索引,会导致Secondary在同步数据时负载变高。
索引管理
创建索引
MongoDB中使用createIndex或ensureIndex函数来创建索引。ensureIndex函数是1.8版本后增加的创建索引函数,是官方推荐使用的函数。createIndex函数在部分高版本MongoDB中已被移除,不推荐使用。
db.集合名称.createIndex(<keys>, <options>);
db.集合名称.ensureIndex(<keys>, <options>);
参数解释:
keys:用于创建索引的列及索引数据的排序规则。
db.stu.ensureIndex({'name':1});
db.stu.ensureIndex({'age':-1});
db.stu.ensureIndex({'name':1, 'age':-1})
options:创建索引时可定义的索引参数。可选参数如下:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。当集合数据量大时,创建索引会占用大量时间,推荐使用此参数,避免影响MongoDB的正常使用。 |
unique | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为false. |
name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。 |
dropDups | Boolean | 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为false. |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
查看索引
查看集合的索引信息。
在MongoDB中使用getIndexes()函数查看集合的索引信息。
db.集合名称.getIndexes()
db.stu.getIndexes();
{
"v" : 2, 版本, 根据MongoDB版本决定。
"key" : { 在什么字段上的索引。
"name" : 1 字段名称:排序方式 1:升序; -1:降序
},
"name" : "name_1", 索引名称
"ns" : "test.agg", 索引所在的集合(namespace)
"background" : true 创建索引时的参数。
}
查看索引键
在MongoDB中使用getIndexKeys()函数查看集合的索引键。
db.集合名称.getIndexKeys();
查看索引详情
在MongoDB中使用getIndexSpecs()函数查看索引详情。
db.集合名称.getIndexSpecs();
查看索引占用空间
在MongoDB中使用totalIndexSize()函数查看集合中索引的大小。
db.集合名称.totalIndexSize([is_detail]);
参数解释:
- is_detail:可选参数,传入除0或false外的任意数据,都会显示该集合中每个索引的大小及总大小。如果传入0或false则只显示该集合中所有索引的总大小。默认值为false。
删除指定索引
在MongoDB中使用dropIndex()函数删除指定的索引。
db.集合名称.dropIndex('索引名');
删除集合的索引自建索引
在MongoDB中使用dropIndexes()函数删除集合中的所有自建索引。此函数只删除自建索引,不会删除MongoDB创建的_id索引。谨慎使用。破坏性太强。
db.集合名称.dropIndexes();
重建索引
在MongoDB中使用reIndex()函数重建索引。重建索引可以减少索引存储空间,减少索引碎片,优化索引查询效率。一般在数据大量变化后,会使用重建索引来提升索引性能。重建索引是删除原索引重新创建的过程,不建议反复使用。
db.集合名称.reIndex()
索引类型
MongoDB支持多种类型的索引,包括单字段索引、复合索引、多key索引、文本索引等,每种类型的索引有不同的使用场合。
单字段索引(Single Field Index)
db.stu.ensureIndex({‘age’:1});
上述语句针对age创建了单字段索引,其能加速对age字段的各种查询请求,是最常见的索引形式,MongoDB默认创建的id索引也是这种类型。
{age: 1} 代表升序索引,也可以通过{age: -1}来指定降序索引,对于单字段索引,升序/降序效果是一样的。
在查询数据的时候,find()—- 条件是索引所在列的时候使用。
交叉索引
为一个集合的多个字段分别建立索引,在查询的时候通过多个字段作为查询条件,这种情况称为交叉索引。交叉索引的查询效率较低,在使用时,当查询使用到多个字段的时候,尽量使用复合索引,而不是交叉索引。
如—————stu集合有name字段和age字段等。
db.stu.createIndex({'name':1});
db.stu.createIndex({'age':-1});
db.stu.find({'name':'xxx', 'age': xxx});
复合|组合|聚合索引(Compound Index)
复合索引是Single Field Index的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推,如下针对age, name这2个字段创建一个复合索引。
如:db.stu.ensureIndex({‘age’:1, ‘name’:1});
多key索引Multikey Index)
当索引的字段为数组时,创建出的索引称为多key索引,多key索引会为数组的每个元素建立一条索引,比如stu集合加入一个habbies字段(数组)用于描述兴趣爱好,需要查询有相同兴趣爱好的人就可以利用habbit字段的多key索引。
db.stu.insert({“name” : “jack”, “age” : 19, habbies: [“football, runnning”]});
db.stu.ensureIndex( {habbies: 1} ) // 创建多key索引
db.stu.find( {habbies: “football”} )
索引特性
MongoDB除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。
唯一索引
保证索引对应的字段不会出现相同的值,比如_id索引就是唯一索引
如:db.stu.ensureIndex({‘name’:1},{‘unique’:true});
如果唯一索引所在字段有重复数据写入时,抛出异常。
部分索引(partial index)
只针对符合某个特定条件的文档建立索引,3.2版本才支持该特性。
MongoDB部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。部分索引通过指定过滤条件来创建,可以为MongoDB支持的所有索引类型使用部分索引。
简单点说:部分索引就是带有过滤条件的索引,即索引只存在与某些文档之上
db.stu.createIndex({'name':1},{'unique':true, 'partialFilterExpression':{'age': {$gt:25}}})
注意:部分索引只为集合中那些满足指定的筛选条件的文档创建索引。如果你指定的partialFilterExpression和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入。
索引覆盖查询特性
官方的MongoDB的文档中说明,覆盖查询是以下的查询:
- 所有的查询字段是索引的一部分
- 所有的查询返回字段都在索引中
由于所有出现在查询中的字段是索引的一部分,MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。
因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多
如有如下索引
db.stu.ensureIndex({gender:1,user_name:1})
那么执行如下查询时,该索引会覆盖查询
db.stu.find({gender:"M"},{user_name:1,_id:0})
也就是说,对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。
由于我们的索引中不包括 id 字段,id在查询中会默认返回,我们可以在MongoDB的查询结果集中排除它。
查询计划
索引已经建立了,但查询还是很慢怎么破?这时就得深入的分析下索引的使用情况了,可通过查看下详细的查询计划来决定如何优化。通过执行计划可以看出如下问题
- 根据某个/些字段查询,但没有建立索引
- 根据某个/些字段查询,但建立了多个索引,执行查询时没有使用预期的索引
查询计划语法是
db.集合名称.find().explain()
建立索引前,db.stu.find( {age:20} )必须执行COLLSCAN,即全表扫描。
建立索引后,通过查询计划可以看出,先进行IXSCAN(从索引中查找),然后FETCH,读取出满足条件的文档。
注意事项
既然索引可以加快查询速度,那么是不是只要是查询语句需要,就建上索引?答案是否定的。因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,数据库在运行时也要消耗资源维护索引,因此索引并不是越多越好。一般两种情况下不建议建索引。
第一种情况是表记录比较少,例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全表扫描就好了。至于多少条记录才算多,这个个人有个人的看法,我个人的经验是以2000作为分界线,记录数不超过 2000可以考虑不建索引,超过2000条可以酌情考虑索引。
另一种不建议建索引的情况是索引的选择性较低。所谓索引的选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值:
Index Selectivity = Cardinality / #T
索引限制
- 额外开销
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。 - 内存开销
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。 - 查询限制——————-检测你的语句是否使用索引是一个好的习惯,可以用explain来查看
索引不能被以下的查询使用:
正则表达式(最左匹配除外)及非操作符,如 $nin, $not, 等。
算术运算符,如 $mod, 等。 - 最大范围
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段