MongoDB
1. 简介
- MongoDB是一个基于 分布式 文件存储 的 NoSQL数据库
- 由C++语言编写,运行稳定,性能高
- 旨在为Web提供可扩展的高性能存储解决方案
名词
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
三元素: 数据库,集合,文档
- 集合就是关系数据库中的表
- 文档对应着关系数据库中的行
文档,就是一个对象,由键值对构成,是Json的扩展Bson形式
集合: 类似于关系数据库中的表,存储多个文档,结构不固定,如可以存储如下文档在一个集合中
{ "name":"guojing","gender":"男"} { "name":"huangrong","age":18} { "book":"shuihuzhuan","heros":"108"}
数据库: 是一个集合的物理容器,一个数据库中可以包含多个文档
一个服务器通常有多个数据库
2. 基本操作
2.1 安装
下载mongodb,注意两点
- 根据业界规则,偶数为稳定版,奇数为开发板
- 32位的mongodb最大只能存放2G数据(默认是已经不提供32位的了)
启动服务端
安装完成后,进入到mongodb的安装路径,使用mongod命令来启动服务端:
有一条错误信息,意思是没有找到指定的目录,这是mongodb的数据存储路径
安装MongoDB服务
配置MongoDB服务
管理员模式打开命令行窗口
创建目录,执行下面的语句来创建数据库和日志文件的目录
mkdir c:\data\db mkdir c:\data\log
创建配置文件
创建一个配置文件。该文件必须设置 systemLog.path 参数,包括一些附加的配置选项更好。
例如,创建一个配置文件位于 C:\data\mongod.cfg,其中指定 systemLog.path 和 storage.dbPath。具体配置内容如下:
systemLog: destination: file path: c:\data\log\mongod.log storage: dbPath: c:\data\db
安装MongoDB服务
通过执行mongod.exe,使用--install选项来安装服务
mongod --config c:\data\mongo.cfg --install --serviceName MongoDB
启动MongoDB服务
net start MongoDB
连接MongoDB服务
mongo
或者使用MongoDB的GUI工具,在安装MongoDB的时候回一并安装
2.2 数据库操作
数据库切换
- 查看当前数据库名称 : db
- 查看所有数据库名称/列出所有在物理上存在的数据库 : show dbs
- 切换数据库 : use 数据库名称
- 如果数据库不存在,则指向数据库,但不创建,直到插入数据或创建集合时数据库才被创建
- 默认的数据库为test,如果没有创建新的数据库,集合将存放在test数据库中
数据库删除
db.dropDatabase()
- 会删除当前指向的数据库
- 如果数据库不存在,则什么也不做
2.3 集合操作
创建集合
语法:
db.createCollection(name, options)
- name是要创建的集合的名称
- options是一个文档,用于指定集合的配置
- 选项参数是可选的,所以只要指定集合名称
例1:
不限制集合的大小
db.createCollection("stu")
例2:
限制集合大小
db.createCollection("sub", {capped : true, size : 10})
- capped : 默认值为false表示不设置上限,值为true表示设置上限
- size : 当capped值为true时, 需要指定此参数, 表示上限大小,当文档达到上限时, 会将之前的数据覆盖, 单位是字节
查看当前数据库的集合
show collections
删除集合
db.集合名称.ddrop()
2.4 数据类型
下表为MongoDB中常用的几种数据类型
数据类型 | 说明 |
---|---|
object id | 文档ID |
String | 字符串,最常用,必须是有效的UTF-8 |
Boolean | 存储一个布尔值,true或false |
Integer | 整数可以是32位或64位,这取决于服务器 |
Double | 存储浮点值 |
Arrays | 数组或列表,多个值存储到一个键 |
Object | 用于嵌入式的文档,即一个值为一个文档 |
Null | 存储Null值 |
Timestamp | 时间戳 |
Date | 存储当前日期或时间的UNIX时间格式 |
- Object id
- 每个文档都有一个属性,为_id,保证每个文档的唯一性
- 可以自己去设置_id插入文档
- 如果没有提供,那么MongoDB为每个文档提供了一个独特的_id,类型为objectID
- objectID是一个12字节的十六进制数
- 前4个字节为当前时间戳
- 接下来3个字节的机器ID
- 接下来的2个字节中MongoDB的服务进程id
- 最后3个字节是简单的增量值
2.5 数据操作
插入
db.集合名称.insert(document)
- 插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId
- 集合如果还没有被创建,则会自动创建集合
例子:
db.stu.insert({name:'gj',gender:1})
简单查询
db.集合名词.find()
更新
db.集合名称.update( <query>, <update>, {multi: <boolean>} )
- 参数query:查询条件,类似sql语句update中where部分
- 参数update:更新操作符,类似sql语句update中set部分
- 参数multi:可选,默认是false,表示只更新找到的第一条记录,值为true表示把满足条件的文档全部更新
例子
全文档更新
db.stu.update({name:'hr'},{name:'mnc'})
默认只修改一条数据,并且可以去修改文档结构
指定属性更新,通过操作符$set
db.stu.insert({name:'hr',gender:0}) db.stu.update({name:'hr'},{$set:{name:'hys'}})
修改多条匹配到的数据
db.stu.update({},{$set:{gender:0}},{multi:true})
想要多行修改,必须配合$set一同使用
保存
db.集合名称.save(document)
如果文档的_id已经存在则修改,如果文档的_id不存在或没有指定_id,则添加
例子
db.stu.save({_id:'20160102','name':'yk',gender:1})
删除
db.集合名称.remove( <query>, { justOne: <boolean> } )
- 参数query:删除的文档的条件
- 参数justOne:可选,如果设为true或1,则只删除一条,默认false,表示删除多条
例子
只删除匹配到的第一条
db.stu.remove({gender:0},{justOne:true})
全部删除
db.stu.remove({})
2.6 数据查询
基本查询
方法 | 说明 | 语法 |
---|---|---|
find() | 查询 | db.集合名称.find({条件文档}) |
findOne() | 查询,只返回第一个 | db.集合名称.findOne({条件文档}) |
pretty() | 将结果格式化,在终端能看出效果 | db.集合名称.find({条件文档}).pretty() |
比较运算符
- 等于,默认是等于判断,没有运算符
- 小于$lt
- 小于或等于$lte
- 大于$gt
- 大于或等于$gte
- 不等于$ne
例子
查询年龄大于等于18的学生
db.stu.find({age:{$gte:18}})
逻辑运算符
查询时可以有多个条件,多个条件之间需要通过逻辑运算符连接
- 逻辑与:默认是逻辑与的关系,没有单独的运算符
- 逻辑或:使用$or[],中括号内为多个条件
例子
查询年龄大于18 或 性别为 0的学生,并且学生的姓名需要时gj
db.stu.find({$or:[{age:{$gte:18}},{gender:1}],name:'gj'})
范围运算符
使用"ParseError: KaTeX parse error: Expected 'EOF', got ',' at position 4: in",̲"nin" 判断是否在某个范围内
例子
查询年龄为18~28的学生
db.stu.find({age:{$in:[18,28]}})
支持正则表达式
使用 /表达式/
或 $regex
编写正则表达式
例子
查询姓黄的学生
db.stu.find({name:/^黄/}) db.stu.find({name:{$regex:'^黄'}}})
自定义查询
使用$where后面写一个函数,返回满足条件的数据,这个函数需要返回一个boolean类型的值,实际上就是写一个js的代码
例子
查询年龄大于30的学生
db.stu.find({$where:function(){return this.age>20}})
this: 就是每一行的文档对象
Limit与Skip
方法 | 说明 | 语法 |
---|---|---|
limit() | 用于读取指定数量的文档 | db.集合名称.find().limit(NUMBER) |
skip() | 用于跳过指定数量的文档 | db.集合名称.find().skip(NUMBER) |
- limit
- 参数NUMBER表示要获取文档的条数
- 如果没有指定参数则显示集合中的所有文档
- skip
- 参数NUMBER表示跳过的记录条数,默认值为0
一起使用可以完成分页功能,例如:
db.stu.find().limit(4).skip(5)
投影
在查询到的返回结果中,只选择必要的字段,而不是选择一个文档的整个字段
语法:
db.集合名称.find({查询条件},{字段名称:1,...})
- 参数为字段与值,值为1表示显示,值为0不显示
- 对于需要显示的字段,设置为1即可,不设置即为不显示
- 特殊:对于_id列默认是显示的,如果不显示需要明确设置为0
排序
方法sort(),用于对结果集进行排序
语法
db.集合名称.find().sort({字段:1,...})
- 参数1为升序排列
- 参数-1为降序排列
例1:根据性别降序,再根据年龄升序
db.stu.find().sort({gender:-1,age:1})
统计个数
方法count()用于统计结果集中文档条数
语法:
db.集合名称.find({条件}).count()
也可以:
db.集合名称.count({条件})
消除重复
方法distinct()对数据进行去重
db.集合名称.distinct('去重字段',{条件})
3. 高级操作
3.1 聚合aggregate
聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
语法:
db.集合名称.aggregate([{管道:{表达式}}])
管道
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入
例如:
ps ajx | grep mongo
在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理
常用管道:
- $group:将集合中的文档分组,可用于统计结果
- $match:过滤数据,只输出符合条件的文档
- $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
- $sort:将输入文档排序后输出
- $limit:限制聚合管道返回的文档数
- $skip:跳过指定数量的文档,并返回余下的文档
- $unwind:将数组类型的字段进行拆分
$group
例子: 统计男生,女生的总人数
db.stu.aggregate([ {$group: { _id:'$gender', counter:{$sum:1} } } ])
- _id: 表示分组的一句,使用某个字段的格式为
$字段
- counter: 是聚合后的数据,相当于列名,可以自定义
- $sum: 求和
- 如果是常数,则统计条数,相当于1条数据 结果加几
- 也可以基于某个字段求和,例如 { sum:′sum:'sum:′age'},则会将结果的年龄进行求和操作
Grouvp by null
将集合中所有文档 分为一组
例子: 求学生总人数,平均年龄
db.stu.aggregate([ {$group: { _id:null, counter:{$sum:1}, avgAge:{$avg:'$age'} } } ])
透视数据
例子 : 统计学生性别及学生姓名
db.stu.aggregate([ {$group: { _id:'$gender', name:{$push:'$name'} } } ])
使用 $$ROOT
可以将整个文档内容加入到结果集的数组中:
db.stu.aggregate([ {$group: { _id:'$gender', name:{$push:'$$ROOT'} } } ])
$match
用于过滤数据,只输出符合条件的文档
使用MongoDB的标准查询操作
例: 查询年龄大于20的学生v
db.stu.aggregate([ {$match:{age:{$gt:20}}} ])
例: 查询年龄大于20的男生,女生人数
db.stu.aggregate([ {$match:{age:{$gt:20}}}, {$group:{_id:'$gender',counter:{$sum:1}}} ])
将第一个管道的输出作为第二个管道的输入
$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}} ])
$sort
将输入文档排序后输出
查询学生信息,按年龄升序
db.stu.aggregate([{$sort:{age:1}}])
查询男生,女生人数,按人数升序
db.stu.aggregate([ {$group:{_id:'$gender',counter:{$sum:1}}}, {$sort:{counter:-1}} ])
$limit
限制聚合管道返回的文档数
查询2条学生信息
db.stu.aggregate([{$limit:2}])
skip
跳过指定数量的文档,并返回余下的文档
查询从第3条开始的学生信息
db.stu.aggregate([{$skip:2}])
统计男生,女生人数,按人数升序,取第二条数据
db.stu.aggregate([ {$group:{_id:'$gender',counter:{$sum:1}}}, {$sort:{counter:1}}, {$skip:1}, {$limit:1} ])
注意顺序:先写skip,再写limit
$unwind
将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
语法1
对某字段值进行拆分,相当于$push的逆操作
db.集合名称.aggregate([{$unwind:'$字段名称'}])
构造数据
db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L']})
查询
db.t2.aggregate([{$unwind:'$size'}])
语法2
对某字段值进行拆分
处理空数组、非数组、无字段、null情况
db.inventory.aggregate([{ $unwind:{ path:'$字段名称', preserveNullAndEmptyArrays:<boolean>#防止数据丢失 } }])
构造数据
db.t3.insert([ { "_id" : 1, "item" : "a", "size": [ "S", "M", "L"] }, { "_id" : 2, "item" : "b", "size" : [ ] }, { "_id" : 3, "item" : "c", "size": "M" }, { "_id" : 4, "item" : "d" }, { "_id" : 5, "item" : "e", "size" : null } ])
使用语法1查询
db.t3.aggregate([{$unwind:'$size'}])
发现对于空数组,无字段,null的文档,都被丢弃了,要想不被丢弃,使用语法2查询
db.t3.aggregate([{$unwind:{path:'$size',preserveNullAndEmptyArrays:true}}])
表达式
处理输入文档并输出
语法:
表达式:'$列名'
常用表达式:
表达式名称 | 描述 |
---|---|
$sum: | 计算总和,$sum:1同count表示计数 |
$avg: | 计算平均值 |
$min: | 获取最小值 |
$max: | 获取最大值 |
$push: | 在结果文档中插入值到一个数组中 |
$first: | 根据资源文档的排序获取第一个文档数据 |
$last: | 根据资源文档的排序获取最后一个文档数据 |
- $push:
3.2 索引
mogodb中也支持索引来提升查询速度
步骤一: 创建大量数据
向集合中插入10万条数据
for(i=0;i<100000;i++){ db.t1.insert({name:'test'+i,age:i}) }
mongodb 的shell 是js的编译器,是可以执行js代码的,插入的时间会稍微有些长
步骤二: 数据查找分析性能
查找名为test10000
的文档,使用explain()
命令进行性能分析
db.t1.find({name:'test10000'}).explain('executionStats')
- executionStats下的executionTimeMillis表示整体查询时间,单位是毫秒
步骤三: 创建索引
语法:
db.集合.ensureIndex({属性:1})
- 1表示升序,-1表示降序
db.t1.ensureIndex({name:1})
步骤四: 验证查询时间
索引的命令
- 建立唯一索引,实现唯一约束的功能
db.t1.ensureIndex({"name":1},{"unique",true})
- 联合索引,对多个属性建立一个索引,按照find()出现的顺序
db.t1.ensureIndex({name:1,age:1})
- 查看文档所有索引
db.t1.getIndexes()
- 删除索引
db.t1.dropIndexes('索引名称')
3.3 安全
超级管理员
- 为了更安全的访问mongodb,需要访问者提供用户名和密码,于是需要在mongodb中创建用户
- 采用了角色-用户-数据库的安全管理方式
- 常用系统角色如下:
- root:只在admin数据库中可用,超级账号,超级权限
- Read:允许用户读取指定数据库
- readWrite:允许用户读写指定数据库
创建超级管理用户
use admin db.createUser({ user:'admin', pwd:'123', roles:[{role:'root',db:'admin'}] })
启用安全认证
在mongod.conf中添加:
security: authorization: enabled
冒号后有空格
然后重启服务
连接:
mongo -u 'admin' -p '123' --authenticationDatabase 'admin'
普通用户
使用超级管理员登录,然后创建用户,先use要创建用户的数据库
db.createUser({ user:'t1', pwd:'123', roles:[{role:'readWrite',db:'test1'}] })
修改用户:
db.updateUser('t1',{pwd:'456'})
整个流程
4 与python交互
安装python包
pip install pymongo
引入包
from pymongo import *
连接,创建客户端
client=MongoClient('mongodb://localhost:27017') # 无安全验证
client=MongoClient('mongodb://用户名:密码@loacalhost:27017/数据库名称') # 有安全验证
获得数据库test1
db=client.test1
类collection
主要方法有:
- insert_one
- insert_many
- update_one
- update_many
- delete_one
- delete_many
- find_one
- find
获得集合stu
stu=db.stu
添加文档
s1={
name:'gj',age:18}
s1_id = stu.insert_one(s1).inserted_id
查找一个文档
s2=stu.find_one()
查找多个文档
for cur in stu.find():
print cur
获取文档个数
print stu.count()
#coding=utf-8
from pymongo import MongoClient
# 获得客户端,建立连接
client = MongoClient('mongodb://localhost:27017')
# 切换数据库
db = client.py3
# 获取集合
stu = db.stu
# 增加
s1 = stu.insert_one({
'name': 'PYTHON','age': '18', 'gender': 0})
print(s1.inserted_id)
print(s1.acknowledged) # 是否插入成功
# 修改
stu.update_one({
'name':'PYTHON'},{
'$set':{
'name:':'abc'}})
# 删除
stu.delete_many({
'name':'abc'})
# 查询
cursor = stu.find({
'age':{
'$gt':18}}).skip(1).limit(1)
for s in cursor:
print(s)