MongoDB
注意:本文中的#123where
都等于$where
介绍
1. MongoDB介绍
MongoDB 是一个高性能,开源,无模式的文档型数据库,是当前noSql数据库产品中最热门的一种。它在许多场景下用于替代传统的关系型数据库或键值对存储方式,MongoDB是用C++开发
2. 为什么要用 NoSQL
2.1 NoSQL 简介
NoSQL,全称是”Not Only Sql”,指的是非关系型的数据库。这类数据库主要有这些特点:非关系型的、分布式的、开源的、水平可扩展的。原始的目的是为了大规模 web 应用,这场全新的数据库革命运动早期就有人提出,发展至 2009 年趋势越发高涨。NoSQL 的拥护者们提倡运用非关系型的数据存储,通常的应用如:模式自由、支持简易复制、简单的 API、最终的一致性(非 ACID)、大容量数据等。NoSQL 被我们用得最多的当数 key-value 存储,当然还有其他的文档型的、列存储、图型数据库、xml 数据库等。相对于目前铺天盖地的关系型数据库运用,这一概念无疑是一种全新思维的注入
2.2 发展现状
现今的计算机体系结构在数据存储方面要求应用架构具备庞大的水平扩展性,而NoSQL正在致力于改变这一现状。目前新浪微博的 Redis和 Google 的 Bigtable 以及 Amazon的 SimpleDB使用的就是 NoSQL 型数据库 NoSQL 项目的名字上看不出什么相同之处,但是,它们通常在某些方面相同:它们可以处理超大量的数据 这场革命目前仍然需要等待。NoSQL 对大型企业来说还不是主流,但是,一两年之后很可能就会变个样子。在 NoSQL 运动的最新一次聚会中,来自世界各地的 150 人挤满了 CBS Interactive 的一间会议室。分享他们如何推翻缓慢而昂贵的关系数据库的暴政,怎样使用更有效和更便宜的方法来管理数据 关系型数据库给你强加了太多东西。它们要你强行修改对象数据,以满足数据库系统的需要。在 NoSQL 拥护者们来看,基于 NoSQL 的数据库替代方案“只是给你所需要的”
2.3 为什么是 NoSQL
随着互联网 web2.0 网站的兴起,非关系型的数据库现在成了一个极其热门的新领域,非关系数据库产品的发展非常迅速,而传统的关系型数据库在应付 web2.0 网站,特别是超大规模和高并发的 SNS 类型的 web2.0 纯动态网站已经显得力不从心,暴露了很多难以克服的问题,例如:
- High performance - 对数据库高并发读写的需求web2.0 网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求关系型数据库应付上万次 SQL 查询还勉强顶得住,但是应付上万次 SQL 写数据请求,硬盘IO 就已经无法承受了,其实对于普通的 BBS 网站,往往也存在对高并发写请求的需求
- Huge Storage - 对海量数据的高效率存储和访问的需求对于大型的 SNS 网站,每天用户产生海量的用户动态信息,以国外的 Friend feed 为例,一个月就达到了 2.5 亿条用户动态,对于关系数据库来说,在一张 2.5 亿条记录的表里面进行SQL 查询,效率是极其低下乃至不可忍受的。再例如大型 web 网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付
- High Scalability&&HighAvailability-对数据库的高可扩展性和高可用性的需求在基于 web 的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像 web server 和 app server 那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供 24 小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移,可是停机维护随之带来的就是公司收入的减少
在上面提到的“三高”需求面前,关系数据库遇到了难以克服的障碍,而对于 web2.0 网站来说,关系数据库的很多主要特性却往往无用武之地,例如:
- 数据库事务一致性需求很多 web 实时系统并不要求严格的数据库事务,对读一致性的要求很低,有些场合对写一致性要求也不高。因此数据库事务管理成了数据库高负载下一个沉重的负担
- 数据库的写实时性和读实时性需求对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多 web 应用来说,并不要求这么高的实时性
- 对复杂的SQL查询,特别是多表关联查询的需求任何大数据量的 web 系统,都非常忌讳多个大表的关联查询,以及复杂的数据分析类型的复杂 SQL 报表查询,特别是 SNS 类型的网站,从需求以及产品设计角度,就避免了这种情况的产生。往往更多的只是单表的主键查询,以及单表的简单条件分页查询,SQL 的功能被极大的弱化了
因此,关系数据库在这些越来越多的应用场景下显得不那么合适了,为了解决这类问题的NoSQL 数据库应运而生。 NoSQL 是非关系型数据存储的广义定义。它打破了长久以来关系型数据库与 ACID 理论大一统的局面。NoSQL 数据存储不需要固定的表结构,通常也不存在连接操作。在大数据存取上具备关系型数据库无法比拟的性能优势,该概念在 2009 年初得到了广泛认同。 当今的应用体系结构需要数据存储在横向伸缩性上能够满足需求。而 NoSQL 存储就是为了实现这个需求。Google 的 BigTable 与 Amazon 的 Dynamo 是非常成功的商业 NoSQL 实现。一些开源的 NoSQL 体系,如 Facebook的Cassandra,Apache的HBase,也得到了广泛认同。从这些NoSQL项目的名字上看不出什么相同之处:Hadoop、Voldemort、Dynomite,还有其它很多,但它们都有一个共同的特点,就是要改变大家对数据库在传统意义上的理解。
2.4 NoSQL 特点
- 它可以处理超大量的数据
- 它运行在便宜的 PC 服务器集群上 PC集群扩充起来非常方便并且成本很低,避免了传统商业数据库“sharding”操作的复杂性和成本。
- 它击碎了性能瓶颈NoSQL 的支持者称,通过 NoSQL 架构可以省去将 Web 或 Java 应用和数据转换成 SQL 格式的时间,执行速度变得更快。“SQL并非适用于所有的程序代码”,对于那些繁重的重复操作的数据,SQL 值得花钱。但是当数据库结构非常简单时,SQL 可能没有太大用处
- 它没有过多的操作虽然 NoSQL 的支持者也承认关系型数据库提供了无可比拟的功能集合,而且在数据完整性上也发挥绝对稳定,他们同时也表示,企业的具体需求可能没有那么复杂
- 它的支持者源于社区因为 NoSQL 项目都是开源的,因此它们缺乏供应商提供的正式支持。这一点它们与大多数开源项目一样,不得不从社区中寻求支持
2.5 MongoDB 特点
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似 json 的 bjson 格式,因此可以存储比较复杂的数据类型。MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库
- **面向集合(Collenction-Orented)**意思是数据被分组存储在数据集中, 被称为一个集合(Collenction)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库里的表(table),不同的是它不需要定义任何模式(schema)
- **模式自由(schema-free)**意味着对于存储在 MongoDB 数据库中的文件,我们不需要知道它的任何结构定义。提了这么多次"无模式"或"模式自由",它到是个什么概念呢?例如,下面两个记录可以存在于同一个集合里面:
- 文档型意思是我们存储的数据是键-值对的集合,键是字符串,值可以是数据类型集合里的任意类型,包括数组和文档. 我们把这个数据格式称作 “BSON” 即 “Binary Serialized document Notation.”
下面将分别介绍 MongoDB 的特点、功能和适用场合
- 特点
- 面向集合存储,易于存储对象类型的数据
- 模式自由
- 支持动态查询
- 支持完全索引,包含内部对象
- 支持查询
- 支持复制和故障恢复
- 使用高效的二进制数据存储,包括大型对象(如视频等)
- 自动处理碎片,以支持云计算层次的扩展性
- 支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++语言的驱动程序,社区
- 中也提供了对 Erlang 及.NET 等平台的驱动程序
- 文件存储格式为 BSON(一种 JSON 的扩展)
- 可通过网络访问
- 功能
- 面向集合的存储:适合存储对象及 JSON 形式的数据
- 动态查询:MongoDB 支持丰富的查询表达式。查询指令使用 JSON 形式的标记,可轻易
- 查询文档中内嵌的对象及数组
- 完整的索引支持:包括文档内嵌对象及数组。MongoDB 的查询优化器会分析查询表达式,并生成一个高效的查询计划
- 查询监视:MongoDB 包含一系列监视工具用于分析数据库操作的性能
- 复制及自动故障转移:MongoDB 数据库支持服务器之间的数据复制,支持主-从模式及
- 服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移
- 高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)
- 自动分片以支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器
- 适用场合
- 网站数据:MongoDB 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性
- 缓存:由于性能很高,MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后,由 MongoDB 搭建的持久化缓存层可以避免下层的数据源过载
- 大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储
- 高伸缩性的场景:MongoDB 非常适合由数十或数百台服务器组成的数据库。MongoDB的路线图中已经包含对 MapReduce 引擎的内置支持
- 用于对象及 JSON 数据的存储:MongoDB 的 BSON 数据格式非常适合文档化格式的存储及查询
3. MongoDB 下载
mongodb官网https://www.mongodb.com/download-center#communityhttps://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-3.0.6-signed.msi
4. MongoDB GUI工具
- Fang of Mongo – 网页式,由Django和jQuery所构成
- Futon4Mongo – 一个CouchDB Futon web的mongodb山寨版
- Mongo3 – Ruby写成
- MongoHub – 适用于OSX的应用程序
- Opricot – 一个基于浏览器的MongoDB控制台, 由PHP撰写而成
- Database Master — Windows的mongodb管理工具
- RockMongo — 最好的PHP语言的MongoDB管理工具,轻量级, 支持多国语言
安装
MongoDB官网下载
https://www.mongodb.com/download-center#communityMongoDB Community Download | MongoDB MongoDB的版本命名规范:x.y.z。 y为奇数时表示当前版本为开发版本,偶数时表示稳定版。 z是修正版本号。
解压
在当前目录中建立一个目录,用于存放数据文件
启动方式
方式1:命令行参数方式启动服务 在bin目录中打开cmd,输入下列命令
mongod --dbpath=..\data\db
mongoDB的默认端口是27017,如果我们想改变默认的启动端口,可以通过--port来指定端口。 为了方便我们每次启动,可以将安装目录的bin目录设置到环境变量的path中,bin目录下是一些常用命令,比如mongod启动服务用的,mongo客户端连接服务用的。 再打开一个cmd,输入
mongo
方式2:写脚本文件启动 创建 start.bat 文件,内容如下
D:\Environment\mongodb4.0.28\bin\mongod.exe --dbpath D:\Environment\mongodb4.0.28\data\db
双击该脚本就可启动服务
Robo 3T下载
数据库可视化工具,Navicat也可以 Robo 3T | Free, open-source MongoDB GUI (formerly Robomongo)
Linux下安装
上传完后解压
tar -zxf mongodb-linux-x86_64-rhel70-4.0.28.tgz
[root@base0 mongodb-4.0.28]# pwd
/opt/mongodb-4.0.28
编辑配置文件
[root@base0 mongodb-4.0.28]# vi /etc/profile
添加下面这句
export PATH=/opt/mongodb-4.0.28/bin:$PATH
[root@base0 mongodb-4.0.28]# source /etc/profile
配置MongoDB启动文件
https://www.jianshu.com/p/d5f0c2836421 在 mongoDB 服务的 bin 文件夹下创建 mongod.conf 文件
storage:
dbPath: "/var/lib/mongodb"
systemLog:
destination: file
path: "/var/log/mongodb/mongod.log"
logAppend: true
net:
port: 27017
bindIpAll: true
processManagement:
fork: true
fork:true 相当于后台启动
创建数据库目录
数据存储目录:/var/lib/mongodb 日志文件目录:/var/log/mongodb 我们在启动 MongoDB 前可以先创建这两个目录并设置读写权限,命名如下:
[root@base0 mongodb-4.0.28]# mkdir -p /var/lib/mongodb
[root@base0 mongodb-4.0.28]# mkdir -p /var/log/mongodb
[root@base0 mongodb-4.0.28]# chown `whoami` /var/lib/mongodb
[root@base0 mongodb-4.0.28]# chown `whoami` /var/log/mongodb
启动服务
mongod --config /opt/mongodb-4.0.28/bin/mongod.conf

启动客户端
mongo
查看帮助 mongo -help
使用
1. 启动mongodb(Linux忽略)
- 创建数据库目录, 如 /data/db ,
- mongod --dbpath d:/data/db
- mongodb要求磁盘剩余空间>=4G,如果不够,可以用 --smallfiles 选项
2. MongoDB 与 Sql的概念对比
SQL | MongoDB |
---|---|
database | database |
table | collection |
row | document or BSON document |
colunm | field |
index | index |
primary key | _id (auto set) |
3. 数据类型
MongoDB中常用的几种数据类型
类型 | 名称 |
---|---|
Object ID | 文档ID |
String | 字符串,最常用,必须是有效的UTF-8 |
Boolean | 存储一个布尔值,true或false |
Integer | 整数可以是32位或64位,这取决于服务器 |
Double | 存储浮点值 |
Arrays | 数组或列表,多个值存储到一个键 |
Object | 用于嵌入式的文档,即一个值为一个文档 |
Null | 存储Null值 |
Timestamp | 时间戳 |
Date | 存储当前日期或时间的UNIX时间格式 |
3.1 Object ID
- 每个文档都有一个属性,为_id,保证每个文档的唯一性
- 可以自己去设置_id插入文档
- 如果没有提供,那么MongoDB为每个文档提供了一个独特的_id,类型为objectID
- objectID是一个12字节的十六进制数,每个字节两位,一共24位的字符串
- 前4个字节为当前时间戳
- 接下来3个字节的机器ID
- 接下来的2个字节中MongoDB的服务进程id
- 最后3个字节是简单的增量值
4. mongodb最基础命令
- show databases; #查看已有数据库
- use dataName; #选择数据库
- show tables; # 查看已有的表
- show collections # 同上,
- db.createCollection('表名');#建表
- db.表名.drop(); #删除表
注:table在mongodb里叫collections
5. collection与table的差异
5.1 table
id | title | content |
---|---|---|
1 | 天气 | 万里无云 |
2 | 心情 | 呵呵哒 |
特点:
- 有结构
- 行遵循结构
5.2 collection
{id:1,title:'晴空万里'}
{id:2,title:'额...',data:'2018'}
特点:
- collection无结构
- 文档相互独立,没有固定结构
操作
1 Mongo数据库的操作
1.1 查看数据库
列出所有在物理上存在的数据库 show dbs
1.2 切换数据库/创建数据库
如果数据库不存在,则指向数据库,但不创建,直到插入数据或创建集合时数据库才被创建 use 数据库名
1.3 删除数据库
删除当前指向的数据库如果数据库不存在,则什么也不做
use 数据库名
db.dropDatabase()
2 集合的操作
2.1 创建集合
db.createCollection("集合名",{capped:true,size:num})
- capped:默认值为false表示不设置上限,值为true表示设置上限
- size:当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时,会将之前的数据覆盖,单位为字节
db."集合名".isCapped() # 如果有容量上限,返回ture,否则返回false
2.2 删除集合
db.集合名称.drop()
2.3 查看集合
show tables
show collections
简单操作
1 数据的增加
注意:插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId
1.1 insert
而insert则会忽略操作,insert可以一次性插入一个列表,而不用遍历,效率高
db.集合名称.insert(document)
db.student.insert({"name":"刘备"})
1.2 save
使用save函数里,如果原来的对象不存在,那他们都可以向collection里插入数据,如果已经存在,save会调用update更新里面的记录,save则需要遍历列表,一个个插入,效率稍低
db.集合名称.save(document)
db.student.save({"name":"孙权"})
db.student.save([{"name":"曹金"},{"name":"曹植"}])
1.3 举例
已存在数据: {_id : 'abc123', " name " : " 小王 " }再次进行插入操作时 insert({_id : 'abc123', " name " : " 小李 " }) 会报主键重复的错误提示 save({ _id : 'abc123', " name " : " 小李 " }) 会把 小王 修改为 小李如果集合中不存在 _id : 'abc123',insert({_id : 'abc123', " name " : " 小李 " }) 增加一条数据save({ _id : 'abc123', " name " : " 小李 " }) 增加一条数据
1.4 增加多条
db.集合名.insert([{k1:v1},{k2:v2}])
2 数据的修改
2.1 格式
db.集合名称.update(
<query>,
<update>,
{multi: <boolean>}
)
- 参数query:查询条件,类似sql语句update中where部分
- 参数update:更新操作符,类似sql语句update中set部分
- 参数multi:可选,默认是false,表示只更新找到的第一条记录,值为true表示把满足条件的文档全部更新
2.2 举例
# 只更新找到的第一条,并且会修改结构
db.person.update({name:"zs"},{age:16})
# 只更新数据,不更新文档结构
db.person.update({name:"zs"},{$set:{age:123}})
# 更新所有找到匹配的数据
db.person.update({name:"zs"},{$set:{age:123}}, {multi: true})
3 数据的删除
# 删除所有匹配数据
db.student.remove({name:"zs"})
# 只更新一条
db.student.remove({name:"zss"},{justOne:true})
4 数据的查找
db.集合名.find()
查询
1. Mongo的简单查询
1.1 find
查找到所有匹配数据
db.集合名.find({条件文档})
db.student.find({"country":"魏国"})
1.2 findOne
只返回匹配的第一个数据
db.集合名.findOne({条件文档})
db.student.findOne({"country":"魏国"})
2. 运算符
语法 | 操作 | 格式 |
---|---|---|
$eq | 等于 | {<key>:<value>} |
$lt | 小于 | {<key>:{$lt:<value>}} |
$lte | 小于或等于 | {<key>:{$lte:<value>}} |
$gt | 大于 | {<key>:{$gt:<value>}} |
$gte | 大于或等于 | {<key>:{$gte:<value>}} |
$ne | 不等于 | {<key>:{$ne:<value>}} |
$or | 或 | {$or:[{},{}]} |
$in | 在范围内 | {age:{$in:[val1,val2]}} |
$nin | 不在范围内 | {age:{$nin:[val1,val2]}} |
举例
db.student.find({age:{$gt:16}})
db.student.find({$or:[{name:"曹操"},{country:"蜀国"}]})
db.student.find({country:{$in:['蜀国','吴国']}})
db.student.find({$or:[{age:{$gte:18}},{name:"zs"}])
3. 模糊匹配
使用//或$regex编写正则表达式
db.student.find({name:/^zs/})
db.student.find({name:{$regex:'^zs'}}})
//查找包含 曹 字的
db.student.find({name:/曹/})
db.student.find({name:{$regex:"曹"}})
4. 自定义查询
使用 #123where后面写一个函数,返回满足条件的数据
db.student.find({ #123where:function(){return this.age>20}})
db.student.find({ #123where:function(){return this.country == '吴国'}})
5. limit
用于读取指定数量的文档 db.集合名称.find().limit(NUMBER)
6. skip
用于跳过指定数量的文档 db.集合名称.find().skip(2)
7. sort
用于对结果集进行排序 db.集合名称.find().sort({字段:1,...})
- 参数1为升序排列
- 参数-1为降序排列
6. count
用于统计结果集中文档条数
db.集合名称.find({条件}).count()
MongoDB 与 Python 交互
安装环境
pip install pymongo
使用样例
1. 引入包pymongo
import pymongo
2. 连接,创建客户端
client = pymongo.MongoClient("localhost", 27017)
client = pymongo.MongoClient('mongodb://localhost:27017/')
3. 获得数据库test1
db = client.test1
db = client['test']
4. 获得集合movie
collection = db.movie
collection = db['movie']
5. 添加数据
#增加一条
m1={name:'300集',actor:'高总',level:10}
m1_id = movie.insert_one(s1).inserted_id
#增加多条
mids = movie.insert_many([movie1,movie2])
注意 :原insert方法也可以实现上面的功能,但是在PyMongo 3.x的版本已经不推荐使用了
6. 查找数据
- find() 返回一个生成器对象
- find_one() 返回一条数据
result = movie.find_one()
result = movie.find_one({'name':'300集'})
result = movie.find_one({'_id':OjectId('5932a80115c2606a59e8a049')})
result = movie.find_one({level:{$gt:1}})
results = movie.find()
比较符号
符号 | 含义 | 示例 |
---|---|---|
$lt | 小于 | {'age': {'$lt': 20}} |
$gt | 大于 | {'age': {'$gt': 20}} |
$lte | 小于等于 | {'age': {'$lte': 20}} |
$gte | 大于等于 | {'age': {'$gte': 20}} |
$ne | 不等于 | {'age': {'$ne': 20}} |
$in | 在范围内 | {'age': {'$in': [20, 23]}} |
$nin | 不在范围内 | {'age': {'$nin': [20, 23]}} |
功能符号
符号 | 含义 | 示例 | 示例含义 |
---|---|---|---|
$regex | 匹配正则表达式 | {'name': {'$regex': '^M.*'}} | name以M开头 |
$exists | 属性是否存在 | {'name': {'$exists': True}} | name属性存在 |
$type | 类型判断 | {'age': {'$type': 'int'}} | age的类型为int |
$mod | 数字模操作 | {'age': {'$mod': [5, 0]}} | 年龄模5余0 |
$text | 文本查询 | {'$text': {'$search': 'Mike'}} | text类型的属性中包含Mike字符串 |
#123where | 高级条件查询 | 自身粉丝数等于关注数 |
7. 获取文档个数
count = movie.count()
count = collection.find({'level': 10}).count()
8. 排序
results = collection.find().sort('name', pymongo.ASCENDING)
9. 偏移
collection.find().sort('name', pymongo.ASCENDING).skip(2).limit(2)
10. 更新
condition = {'name': '300集'}
movie = collection.find_one(condition)
movie['level'] = 8
result = collection.update_one(condition, movie)
result = collection.update_many(condition, {'$set': movie}) 注意: 也可以实现上面的功能,但是在PyMongo 3.x的版本已经不推荐使用了
11. 删除
result = collection.remove({'name': '300集'}) #符合条件的所有数据均会被删除
result = collection.delete_one({'name': '300集'})
result = collection.delete_many({'age': {'$lt': 25}})
例子
# pymongo 版本 3.12.3
import pymongo
# 连接数据库
client = pymongo.MongoClient()
# 选择实例
person = client.person
# 选择集合
student = person.student
# 操作数据
result = student.find()
# for r in result:
# print(r)
# print(result.next())
# print(result.next())
# print(result.next())
# 过滤条件
# result = student.find({"country":"蜀国"})
# for r in result:
# print(r)
# 排序
# result = student.find().sort("age", pymongo.DESCENDING)
# result = student.find().sort("age", pymongo.ASCENDING)
# for r in result:
# print(r)
# 分页
# result = student.find().limit(6).skip(6)
# for r in result:
# print(r)
# 已经废除 count()
# result = student.find().count()
# result = student.estimated_document_count()
# print(result)
# 增加数据
data = {"name": "小乔", "age": 18}
student.insert_one(data)
# 删除
# student.remove(data)
# 更新
# data = {"name": "小乔"}
result = student.find_one(data)
print(result)
result["country"] = "蜀国"
student.update(data, {"$set": result})
常用命令
1. 一般命令
- 显示数据库列表:show dbs
- 切换/创建数据库: use dabaseA (dabaseA为数据库名,如果该数据库不存在,则会创建)
- 删除当前数据库:db.dropDatabase() --当执行use dabaseA命令后,当前数据库就是dabaseA,所以再执行db.dropDatabase(),删除的当前数据库就是dabaseA。
- 显示当前数据库中的操作命令:db.help()
- 显示当前数据库中的集合:show collections (这里的集合类似关系数据库中的表)
- 显示数据库中某集合的操作命令: db.table.help() (这里的table是当前数据库中一个集合)
- 往某一集合中插入数据:db.person.insert({'name':'小王', 'age' : 20, 'sex':'男'}) 或者 db.person.save({'name':'小王', 'age' : 20, 'sex':'男'})注意: mongodb的save和insert函数都可以向collection里插入数据,但两者是有两个区别:
- 使用save函数里,如果原来的对象不存在,那他们都可以向collection里插入数据,如果已经存在,save会调用update更新里面的记录,save则需要遍历列表,一个个插入,效率稍低
- 而insert则会忽略操作,insert可以一次性插入一个列表,而不用遍历,效率高 例如:已存在数据: {_id : 'abc123', " name " : " 小王 " }再次进行插入操作时 insert({_id : 'abc123', " name " : " 小李 " }) 会报主键重复的错误提示save({ _id : 'abc123', " name " : " 小李 " }) 会把 小王 修改为 小李 。如果集合中不存在 _id : 'abc123',insert({_id : 'abc123', " name " : " 小李 " }) 增加一条数据save({ _id : 'abc123', " name " : " 小李 " }) 增加一条数据
- 查看当前使用的数据库:db 或 db.getName() 两者效果一样
- 显示当前数据库的状态:db.stats()
- 显示当前数据库的版本 :db.version()
- 显示当前数据库链接的地址:db.getMongo()
- 在指定的机器上,从数据库A,负责数据到B:db.copyDatabase("mydb", "temp", "127.0.0.1") 将本机的mydb的数据复制到temp数据库中
- 显示当前数据库中所有集合: db.getCollectionNames()
- 显示数据库的状态:db.table.stats()
- 删除当前数据库中某个集合:db.table.drop() 删除集合 table
- 删除当前数据库某个集合中的所有数据:db.table.remove({}) 删除集合 table中所有数据
- 删除当前数据库某个集合中name='test'的记录:db.table.remove({name:'test'})
- 删除当前数据库某个集合中所有数据:db.Information.remove({})
2. 查看集合基本信息命令
- 查看当前数据库中某集合中的帮助:db.table.help()
- 查看某集合的数据条数:db.table.count()
- 查看某集合数据空间大小: db.table.dataSize() 单位是字节
- 查看某集合的总空间大小:db.table.storageSize()
3. 查询命令
- 查询索引记录:相当于select * from table db.table.find()
- 查询age = 22的记录 db.table.find({age:22})
- 查询age >22的记录
- 查询age>=22的记录
- 查询age <30的记录
- 查询age <=30的记录
- 查询age >20 并且age< 30的记录
- 查询集合中name 包含mongo的数据,相当于like '%mongo%' 模糊查询 db.table.find({name:/mongo/})
- 查询集合中 name中以mongo开头的数据,相当于like 'mongo%' 模糊查询 db.table.find({name:/^mongo/})
- 查询集合中,只查询,name和age两列 db.table.find({},{name:1,age:1})
- 查询结合中age>10 ,并且只查询 name 和 age两列 db.table.find({age:{$gt:10}},{name:1,age:1})
- 按年龄排序
- 查询前10条数据,相当于select top 10 from table db.table.find().limit(10)
- 查询10条以后的数据,相当于 select * from table where id not in (select top * from table ) db.table.find().skip(10)
- 查询5-10条之间的数据 db.table.find().skip(5).limit(10)
- 查询 age =10 or age =20的记录 db.table.find({$or:[{age:20},{age:30}]})
- 查询age >20的记录条数 db.table.find({age:{$gt:20}}).count()
- 查询age>30 or age <20 的记录
- 查询age > 40 or name ='mike'的记录 db.table.find({or:[{age:{gt:40}},{name:'mike'}]})
- 查询age > 40 or name ='mike'的记录,只查询name 和age两列,并且按照name升序,age降序 db.table.find({or:[{age:{gt:40}},{name:'mike'}]},{name:1,age:-1})查询age > 40 并且 name ='mike'的记录,只查询name 和age两列,并且按照name升序,age降序 db.table.find({and:[{age:{gt:40}},{name:'mike'}]},{name:1,age:-1})python 代码
- 查询age 在[30,40] 内的记录 db.table.find({age:{$in:[30,40]}})
- 查询age不在[30,40]范围内的记录 db.table.find({age:{$nin:[30,40]}})
- 查询age能被3整除的记录 db.table.find({age:{$mod:[3,0]}})
- 查询age能被3整除余2的记录 db.table.find({age:{$mod:[3,2]}})
db.table.find({age:{$gt:22}})
db.table.find("this.age>22")
db.table.find({age:{$gte:22}})
db.table.find("this.age>=22")
db.table.find({age:{$lt:30}})
db.table.find("this.age < 30")
db.table.find({age:{$lte:30}})
db.table.find("this.age<=30")
db.table.find({age:{gt:20,lt :30}})
db.table.find("this.age>20 && this.age< 30")
db.table.find().sort({age:1,name:1}) 按照年龄和姓名升序
db.table.find().sort({age:-1,name:1}) 按照年龄降序,姓名升序
#python
db.table.find().sort(‘age‘,pymongo.ASCENDING)
或
db.table.find().sort(‘age‘,1)升序;
db.table.find().sort(‘age‘,pymongo.DESCENDING)
或
db.table.find().sort(‘age‘,-1)降序
db.table.find().sort([(‘age‘,pymongo.DESCENDING),('name',pymongo.ASCENDING)]) 年龄降序,姓名升序
或
db.table.find().sort([(‘age‘,-1),('name':1)])年龄降序,姓名升序
注意mongo和python里面命令的区别是冒号,python是逗号
db.table.find({or:[{age:{gt:30}},{age:{$lt:20}}]})
db.table.find("this.age>30 || this.age < 20")
db.table.find({'and':[{age:{'$gt':40}},{'name':'mike'}]},{'name':1,'age':-1})
像and和字段名必须在引号内,否则报错
//假如有以下文档 { 'name' : { 'first' : 'Joe', 'last' : 'Schmoe' } 'age' : 45 }
- 查询姓名为询姓名是Joe Schmoe的记录
- 如果需要多个元素来匹配数组,就需要使用$all了
db.table.find({'name.first':'Joe','name.last':'Schmoe'})
db.table.find({name:{first:'Joe',last:'Schmoe'}})
//假设在我们表中3个下面的文档:
db.food.insert({'_id' : 1,'fruit' : ['apple', 'banana', 'peach']})
db.food.insert({'_id' : 2,'fruit' : ['apple', 'kumquat', 'orange']})
db.food.insert({'_id' : 3,'fruit' : ['cherry', 'banana', 'apple']})
要找到既有apple又有banana的文档: db.food.find({fruit:{$all:['apple', 'banana']}})
- 查询age不是30并且性别不是‘男’的记录,就用到 $nor db.person.find({$nor:[{age:30},{sex:'男'}]})
- 查询age不大于30的记录,用到not,not执行逻辑NOT运算,选择出不能匹配表达式的文档 ,包括没有指定键的文档。 db.person.find({age:{not:{gt:30}}})
- 如果$exists的值为true,选择存在该字段的文档;若值为false则选择不包含该字段的文档。
选择age存在,且不在[30,40]只能的记录 db.person.find({age:{exists:true,nin:[30,40]}})
- 查询name中包括字母t的记录,类似 name like '%t%'
- 查询name中以t字母结尾的记录,类似 name like '%t', 要用到符号$ db.person.find({name:/t$/})
- 如果在查询的时候需要多个元素来匹配数组,就需要用到$all了,这样就匹配一组元素。例如:假如创建了包含3个元素的如下集合:
db.person.find({name:/t/})
db.person.find({name:{$regex:/t/}})
db.person.find({name:/t/i}) i在这里是不区分大小写
db.person.find({name:{regex:/t/,options:'i'}}) i在这里是不区分大小写
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "pear", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
要找到既有apple, 又有banana的文档,就要用到$all db.food.find({fruit:{$all:['apple','banana']}}) 查询结果如下:
db.food.find({fruit:{$all:['apple','banana']}})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
注意两条结果记录的apple和banana的顺序是不一样的,也就是说,顺序无关紧要。 要是想查询指定数组位置的元素,则需要用key.index语法指定下标 db.food.find({'fruit.2':'peach'}),结果为:
- null
null比较奇怪,它确实能匹配本身,假如有下面的数据:
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "pear", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
{ "_id" : 4, "fruit" : null }
db.food.find({fruit:null}) 查询结果: { "_id" : 4, "fruit" : null } 但是null不仅能匹配本身,而且能匹配“不存在的” ,例如: db.food.find({x: null}) ,food集合中本来不包含x键的,结果如下: db.food.find({x:null})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "pear", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
{ "_id" : 4, "fruit" : null }
- $size 对于查询来说也是意义非凡,顾名思义就是可用它来查询指定长度的数组比如有以下数据:
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "pear", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
{ "_id" : 4, "fruit" : null }
{ "_id" : 5, "fruit" : [ "apple", "orange" ] }
db.food.find({fruit:{$size:3}}) 查询结果如下:fruit对应的数组的长度为3
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "pear", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
db.food.find({fruit:{$size:1}}) 查询结果如下:fruit对应的数组的长度为1
- slice 可以按偏移量返回记录,针对数组。如{"slice":10}返回前10条,{"$slice":{[23,10]}}从24条取10条例如在集合food中有数据如下:{ "_id" : 1, "fruit" : [ "apple", "pear", "orange", "strawberry", "banana" ], "name" : "fruitName1" }{ "_id" : 2, "fruit" : [ "apple", "orange", "pear", "banana" ], "name" : "fruitName2" }{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ], "name" : "fruitName3" }{ "_id" : 4, "fruit" : null }针对fruit 如何只获取该键对应数据前个数据,{"$slice":2}查询语句:db.food.find({},{fruit:{$slice:2}}) 查询结果为:{ "_id" : 1, "fruit" : [ "apple", "pear" ], "name" : "fruitName1" } 注意fruit对应的数据,只获取了,前两个数据,后面的去掉了{ "_id" : 2, "fruit" : [ "apple", "orange" ], "name" : "fruitName2" }{ "_id" : 3, "fruit" : [ "cherry", "banana" ], "name" : "fruitName3" }{ "_id" : 4, "fruit" : null }针对fruit 如何只获取该键对应数据从第二个数据开始取,取两个,{"$slice":[1,2]}查询语句:db.food.find({},{fruit:{$slice:[1,2]}}) 查询结果为:{ "_id" : 1, "fruit" : [ "pear", "orange" ], "name" : "fruitName1" }{ "_id" : 2, "fruit" : [ "orange", "pear" ], "name" : "fruitName2" }
- $elemMatch如果对象有一个元素是数组,那么$elemMatch可以匹配内数组内的元素。例如数据集school中有如下数据:
{ "_id" : 1, "zipcode" : "63109", "students" : [ { "name" : "john", "school" : 102, "age" : 10 }, { "name" : "jess", "school" : 102, "age" : 11 },
{ "name" : "jeff", "school" : 108, "age" : 15 } ] }
{ "_id" : 2, "zipcode" : "63110", "students" : [ { "name" : "ajax", "school" : 100, "age" : 7 }, { "name" : "achilles", "school" : 100, "age" : 8 } ] }
{ "_id" : 3, "zipcode" : "63108", "students" : [ { "name" : "ajax", "school" : 100, "age" : 7 }, { "name" : "achilles", "school" : 100, "age" : 8 } ] }
{ "_id" : 4, "zipcode" : "63109", "students" : [ { "name" : "barney", "school" : 102, "age" : 7 }, { "name" : "ruth", "school" : 102, "age" : 16 } ] }
{ "_id" : 5, "zipcode" : "63109", "students" : [ { "name" : "barney", "school" : 102, "age" : 12 }, { "name" : "ruth", "school" : 102, "age" : 16 } ] }
要查询 zipcode="63109" ,school= ‘102’ 并且 age>10的记录 db.school.find( { zipcode: "63109" },{ students: { elemMatch: { school: 102 ,age:{gt: 10}} } } ) 查询结果:
{ "_id" : 1, "students" : [ { "name" : "jess", "school" : 102, "age" : 11 } ] }
{ "_id" : 4, "students" : [ { "name" : "ruth", "school" : 102, "age" : 16 } ] }
{ "_id" : 5, "students" : [ { "name" : "barney", "school" : 102, "age" : 12 } ] }
- $exists 判断某个字段是否存在,查询school集合中存在zipcode字段的记录 db.school.find({zipcode:{$exists:1}})
- 假如有集合school ,数据如下:
{ "_id" : 1, "zipcode" : "63109", "students" : [ { "name" : "john", "school" : 102, "age" : 10 }, { "name" : "jess", "school" : 102, "age" : 11 }, { "name" : "jeff", "school" : 108, "age" : 15 } ] }
{ "_id" : 2, "zipcode" : "63110", "students" : [ { "name" : "ajax", "school" : 100, "age" : 7 }, { "name" : "achilles", "school" : 100, "age" : 8 } ] }
{ "_id" : 3, "zipcode" : "63109", "students" : [ { "name" : "ajax", "school" : 100, "age" : 7 }, { "name" : "achilles", "school" : 100, "age" : 8 } ] }
{ "_id" : 4, "zipcode" : "63109", "students" : [ { "name" : "barney", "school" : 102, "age" : 7 }, { "name" : "ruth", "school" : 102, "age" : 16 } ] }
{ "_id" : 5, "zipcode" : "63109", "students" : [ { "name" : "barney", "school" : 102, "age" : 12 }, { "name" : "ruth", "school" : 102, "age" : 16 } ] }
db.school.find({'students.school':102},{'students.name':1,'students.age':1})` db.school.find({_id:{$gt:5}},{'students.name':1,'students.age':1}) 如果只查询students字段里面的内容,并且只查询school =102 的姓名和年龄信息: 查询语句为: 结果如下: 假设school 集合中包含一些记录:students字段对应一个数据字典 如果只查询字段students对应name和age信息,则查询语句如下: 结果为:这里_id是必须要显示的
{ "_id" : 7, "students" : { "name" : "jike", "age" : 45 } }
{ "_id" : 8, "students" : { "name" : "Marry", "age" : 75 } }
增删改查
1. mongodb插入数据
命令:db.集合名称.insert(document)
db.stu.insert({name:'gj', gender:1})
db.stu.insert({_id:"20170101", name:'gj', gender:1})
插文档时,如果不指定_id参数,MongoDB会为文档自动分配一个唯一的ObjectId
2. mongodb的保存
命令:db.集合名称.save(document)
db.stu.save({_id:'20170101', name:'gj', gender:2})
db.stu.save({name:'gj', gender:2})
db.stu.find()
如果文档的_id已经存在则修改,如果_id不存在则添加
3 mongodb的查询
命令:db.集合名称.find() 可以使用以下数据进行练习
db.stu.insert([{"name" : "郭靖", "hometown" : "蒙古", "age" : 20, "gender" : true },
{"name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false },
{"name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false },
{"name" : "黄药师", "hometown" : "桃花岛", "age" : 40, "gender" : true },
{"name" : "段誉", "hometown" : "大理", "age" : 16, "gender" : true },
{"name" : "段王爷", "hometown" : "大理", "age" : 45, "gender" : true },
{"name" : "洪七公", "hometown" : "华筝", "age" : 18, "gender" : true }])
3.1 简单查询
- 方法find(): 查询db.集合名称.find({条件文档})
- 方法findOne():查询,只返回第一个db.集合名称.findOne({条件文档})
- 方法pretty(): 将结果格式化;不能和findOne()一起使用!db.集合名称.find({条件文档}).pretty()
3.2 比较运算符
- 等于: 默认是等于判断, 没有运算符
- 小于:$lt (less than)
- 小于等于:$lte (less than equal)
- 大于:$gt (greater than)
- 大于等于:$gte
- 不等于:$ne
查询年龄大于18的所有学生
db.stu.find({age:{$gte:18}})
3.3 逻辑运算符
逻辑运算符主要指与、或逻辑
- and:在json中写多个条件即可
查询年龄大于或等于18, 并且性别为true的学生
db.stu.find({age:{$gte:18},gender:true})
- or:使用$or, 值为数组, 数组中每个元素为json
查询年龄大于18, 或性别为false的学生
db.stu.find({$or:[{age:{$gt:18}},{gender:false}]})
查询年龄大于18或性别为男生, 并且姓名是郭靖
db.stu.find({$or:[{age:{$gte:18}},{gender:true}],name:'gj'})
3.4 范围运算符
使用$in, $nin 判断数据是否在某个数组内
查询年龄为18、 28的学生
db.stu.find({age:{$in:[18,28,38]}})
3.5 支持正则表达式
使用$regex编写正则表达式
查询name以'黄'开头的数据
db.stu.find({name:{$regex:'^黄'}})
3.6 自定义查询
mongo shell 是一个js的执行环境使用 #123where
写一个函数, 返回满足条件的数据
查询年龄大于30的学生
db.stu.find({
#123where:function() {
return this.age>30;}
})
3.7 skip和limit
- 方法limit(): 用于读取指定数量的文档
db.集合名称.find().limit(NUMBER)
查询2条学生信息
db.stu.find().limit(2)
- 方法skip(): 用于跳过指定数量的⽂档
db.集合名称.find().skip(NUMBER)
db.stu.find().skip(2)
- 同时使用
db.stu.find().limit(4).skip(5)
db.stu.find().skip(5).limit(4)
注意:先使用skip在使用limit的效率要高于前者
3.8 投影
在查询到的返回结果中, 只选择必要的字段 命令:db.集合名称.find({},{字段名称:1,...}) 参数为字段与值, 值为1表示显示, 值为0不显特别注意:
- 对于_id列默认是显示的, 如果不显示需要明确设置为0
- 对于其他不显示的字段不能设置为0
db.stu.find({},{_id:0,name:1,gender:1})
3.9 排序
方法sort(), 用于对查询结果按照指定的字段进行排序 命令:db.集合名称.find().sort({字段:1,...}) 参数1为升序排列参数-1为降序排列
根据性别降序, 再根据年龄升序
db.stu.find().sort({gender:-1,age:1})
3.10 统计个数
方法count()用于统计结果集中文档条数 命令:db.集合名称.find({条件}).count()命令:db.集合名称.count({条件})
db.stu.find({gender:true}).count()
db.stu.count({age:{$gt:20},gender:true})
3.11 数据去重
方法 distinct(),用于对结果集进行去重 命令:db.集合名称.distinct("字段") 命令:db.集合名称.distinct("字段",{条件})
db.stu.distinct("hometown")
db.stu.distinct("hometown",{age:18})
4 mongodb的更新
db.集合名称.update({query}, {update}, {multi: boolean})
- 参数query:查询条件
- 参数update:更新操作符
- 参数multi:可选,默认是false,表示只更新找到的第一条数据,值为true表示把满足条件的数据全部更新
db.stu.update({name:'hr'},{name:'mnc'}) # 全文档进行覆盖更新
db.stu.update({name:'hr'},{$set:{name:'hys'}}) # 指定键值更新操作
db.stu.update({},{$set:{gender:0}},{multi:true}) # 更新全部
db.stu.update({name:'hr'},{$set:{age:'100'}},{upsert:true}) # 如果有name为hr的,修改其年龄为100,如果年龄本来就100就不进行任何操作。如果没有name为hr的,就添加一条新数据
注意:"multi update only works with $ operators"
- multi参数必须和$set一起使用!
5 mongodb的删除
db.集合名称.remove({query}, {justOne: boolean})
- 参数query:可选,删除的⽂档的条件
- 参数justOne:可选, 如果设为true或1,则只删除一条,默认false,表示删除全部
聚合操作
1 mongodb的聚合是什么
聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。 语法:db.集合名称.aggregate({管道:{表达式}})
2 mongodb的常用管道和表达式
知识点:
- 掌握mongodb中管道的语法
- 掌握mongodb中管道命令
2.1 常用管道命令
在mongodb中,⽂档处理完毕后, 通过管道进⾏下⼀次处理 常用管道命令如下:
- $group: 将集合中的⽂档分组, 可⽤于统计结果
- $match: 过滤数据, 只输出符合条件的⽂档
- $project: 修改输⼊⽂档的结构, 如重命名、 增加、 删除字段、 创建计算结果
- $sort: 将输⼊⽂档排序后输出
- $limit: 限制聚合管道返回的⽂档数
- $skip: 跳过指定数量的⽂档, 并返回余下的⽂档
2.2 常用表达式
表达式:处理输⼊⽂档并输出 语法:表达式:'$列名' 常⽤表达式:
- $sum: 计算总和, $sum:1 表示以⼀倍计数
- $avg: 计算平均值
- $min: 获取最⼩值
- $max: 获取最⼤值
- $push: 在结果⽂档中插⼊值到⼀个数组中
3 管道命令之$group
3.1 按照某个字段进行分组
$group是所有聚合命令中用的最多的一个命令,用来将集合中的文档分组,可用于统计结果 使用示例如下
db.stu.aggregate(
{$group:
{
_id:"$gender",
counter:{$sum:1}
}
}
)
{ "_id" : false, "counter" : 2 }
{ "_id" : true, "counter" : 5 }
> db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1},sum_age:{$sum:"$age"}}})
{ "_id" : false, "counter" : 2, "sum_age" : 36 }
{ "_id" : true, "counter" : 5, "sum_age" : 139 }
> db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1},sum_age:{$sum:"$age"},avg_age:{$avg:"$age"}}})
{ "_id" : false, "counter" : 2, "sum_age" : 36, "avg_age" : 18 }
{ "_id" : true, "counter" : 5, "sum_age" : 139, "avg_age" : 27.8 }
> db.stu.aggregate({$group:{_id:"$gender",counter:{$sum:1},sum_age:{$sum:"$age"},avg_age:{$avg:"$age"},name_list:{$push:"$name"}}})
{ "_id" : false, "counter" : 2, "sum_age" : 36, "avg_age" : 18, "name_list" : [ "黄蓉", "华筝" ] }
{ "_id" : true, "counter" : 5, "sum_age" : 139, "avg_age" : 27.8, "name_list" : [ "郭靖", "黄药师", "段誉", "段王爷", "洪七公" ] }
其中注意点:
- db.db_name.aggregate是语法,所有的管道命令都需要写在其中
- _id 表示分组的依据,按照哪个字段进行分组,需要使用$gender表示选择这个字段进行分组
- $sum:1 表示把每条数据作为1进行统计,统计的是该分组下面数据的条数
3.2 group by null
当我们需要统计整个文档的时候,$group 的另一种用途就是把整个文档分为一组进行统计 使用实例如下:
db.stu.aggregate(
{$group:
{
_id:null,
counter:{$sum:1}
}
}
)
其中注意点:
- _id:null 表示不指定分组的字段,即统计整个文档,此时获取的counter表示整个文档的个数
3.3 数据透视
正常情况在统计的不同性别的数据的时候,需要知道所有的name,需要逐条观察,如果通过某种方式把所有的name放到一起,那么此时就可以理解为数据透视 使用示例如下:
- 统计不同性别的学生
- 使用$$ROOT可以将整个文档放入数组中(一般不用)
db.stu.aggregate(
{$group:
{
_id:null,
name:{$push:"$name"}
}
}
)
db.stu.aggregate(
{$group:
{
_id:null,
name:{$push:"$$ROOT"}
}
}
)
3.4 动手
对于如下数据,需要统计出每个country/province下的userid的数量(同一个userid只统计一次)
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "b" }
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "c" }
{ "country" : "china", "province" : "bj", "userid" : "da" }
{ "country" : "china", "province" : "bj", "userid" : "fa" }
参考答案
db.tv3.aggregate(
{$group:{_id:{country:'$country',province:'$province',userid:'$userid'}}},
{$group:{_id:{country:'$_id.country',province:'$_id.province'},count:{$sum:1}}}
4 管道命令之$match
$match用于进行数据的过滤,是在能够在聚合操作中使用的命令,和find区别在于$match 操作可以把结果交给下一个管道处理,而find不行 使用示例如下:
- 查询年龄大于20的学生
- 查询年龄大于20的男女学生的人数
db.stu.aggregate(
{$match:{age:{$gt:20}}
)
db.stu.aggregate(
{$match:{age:{$gt:20}}
{$group:{_id:"$gender",counter:{$sum:1}}}
)
5 管道命令之$project
$project用于修改文档的输入输出结构,例如重命名,增加,删除字段 使用示例如下:
- 查询学生的年龄、姓名,仅输出年龄姓名
- 查询男女生人生,输出人数
db.stu.aggregate(
{$project:{_id:0,name:1,age:1}}
)
db.stu.aggregate(
{$group:{_id:"$gender",counter:{$sum:1}}}
{$project:{_id:0,counter:1}}
)
动手练习 对于如下数据:统计出每个country/province下的userid的数量(同一个userid只统计一次),结果中的字段为{country:"",province:"",counter:"*"}
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "b" }
{ "country" : "china", "province" : "sh", "userid" : "a" }
{ "country" : "china", "province" : "sh", "userid" : "c" }
{ "country" : "china", "province" : "bj", "userid" : "da" }
{ "country" : "china", "province" : "bj", "userid" : "fa" }
参考答案
db.tv3.aggregate(
{$group:{_id:{country:'$country',province:'$province',userid:'$userid'}}},
{$group:{_id:{country:'$_id.country',province:'$_id.province'},count:{$sum:1}}},
{$project:{_id:0,country:'$_id.country',province:'$_id.province',counter:'$count'}}
)
6 管道命令之$sort
$sort用于将输入的文档排序后输出 使用示例如下:
- 查询学生信息,按照年龄升序 db.stu.aggregate({$sort:{age:1}})
- 查询男女人数,按照人数降序
db.stu.aggregate(
{$group:{_id:"$gender",counter:{$sum:1}}},
{$sort:{counter:-1}}
)
7 管道命令之$skip 和 $limit
- $limit限制返回数据的条数
- $skip 跳过指定的文档数,并返回剩下的文档数
- 同时使用时先使用skip在使用limit
使用示例如下:
- 查询2条学生信息
- 查询从第三条开始的学生信息
- 统计男女生人数,按照人数升序,返回第二条数据
db.stu.aggregate(
{$limit:2}
)
db.stu.aggregate(
{$skip:3}
)
db.stu.aggregate(
{$group:{_id:"$gender",counter:{$sum:1}}},
{$sort:{counter:-1}},
{$skip:1},
{$limit:1}
)
索引操作
1. 为什么mongdb需要创建索引
- 加快查询速度
- 进行数据的去重
2. mongodb创建简单的索引方法
- 语法:db.集合名.ensureIndex({属性:1}),1表示升序, -1表示降序
3. 创建索引前后查询速度对比
测试:插入10万条数据到数据库中 插入数据: for(i=0;i<100000;i++){db.t1.insert({name:'test'+i,age:i})} 创建索引前:
db.t1.find({name:'test10000'})
db.t1.find({name:'test10000'}).explain('executionStats') # 显示查询操作的详细信息
创建索引: db.t1.ensureIndex({name:1}) 创建索引后: db.t1.find({name:'test10000'}).explain('executionStats') 前后速度对比
4. 索引的查看
默认情况下_id是集合的索引查看方式:db.集合名.getIndexes()
5. 删除索引
语法:db.集合名.dropIndex({'索引名称':1})
db.t1.dropIndex({name:1})
db.t1.getIndexes()
6. mongodb创建唯一索引
在默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据,但是创建索引仅仅能够提高查询速度,同时降低数据库的插入速度。
6.1 添加唯一索引的语法:
db.集合名.ensureIndex({"字段名":1}, {"unique":true})
6.2 利用唯一索引进行数据去重
根据唯一索引指定的字段的值,如果相同,则无法插入数据
db.t1.ensureIndex({"name":1}, {"unique":true})
db.t1.insert({name: 'test10000'})
7. 建立复合索引
在进行数据去重的时候,可能用一个域来保证数据的唯一性,这个时候可以考虑建立复合索引来实现。 例如:抓全贴吧信息,如果把帖子的名字作为唯一索引对数据进行去重是不可取的,因为可能有很多帖子名字相同 建立复合索引的语法:db.collection_name.ensureIndex({字段1:1,字段2:1})
8. 建立索引注意点
- 根据需要选择是否需要建立唯一索引
- 索引字段是升序还是降序在单个索引的情况下不影响查询效率,但是带复合索引的条件下会有影响
- 数据量巨大并且数据库的读出操作非常频繁的时候才需要创建索引,如果写入操作非常频繁,创建索引会影响写入速度例如:在进行查询的时候如果字段1需要升序的方式排序输出,字段2需要降序的方式排序输出,那么此时复合索引的建立需要把字段1设置为1,字段2设置为-1