Mongodb
安装与启动:
- 特征:
- 端口号:27017
- 默认配置文件位置:
/etc/mongod.conf
,本机配置位置:~/yezixuannao/mongo/config/mongod.conf
- 默认日志位置:
/var/log/mongodb/mongod.log
,本机日志位置:~/yezixuannao/mongo/logs/mongod.log
- 类型:
Nosql
即非关系型数据库 - 特点: 适用于存储海量数据,广泛应用于爬虫数据存储
- 安装:
- 选择相应版本和操作系统并下载
https://www.mongodb.com/download-center/community?jmp=docs
- 解压
tar -zxvf mongodb-linux-x86_64-ubuntu1804-4.0.3.tgz
- 移动到/usr/local/目录下
sudo mv -r mongodb-linux-x86_64-ubuntu1804-4.0.3/ /usr/local/mongodb
- 在shell的初始化脚本.bashrc中添加mongodb可执行文件到环境变量PATH中
- a. 进入.bashrc文件中
1、cd ~ 2、sudo vi .bashrc
- b. 在.bashrc文件的最后添加:
export PATH=/usr/local/mongodb/bin:$PATH
- a. 进入.bashrc文件中
- 选择相应版本和操作系统并下载
- 启动服务端:
- 测试情况下启动:
sudo service mongod start (sudo service mongod start)
、停止:sudo service mongod stop
、重启:sudo service mongod restart
。 - 生产情况下启动:
sudo mongod [--auth --dbpath=dbpath --logpath=logpath --append --fork] [-–f logfile ]
- 只以
sudo mongod
命令启动时,默认将数据存放在了/data/db
目录下,需要手动创建 --dbpath
: 指定数据库的存放路径--logpath
: 指定日志的存放路径--append
: 或--logappend
设置日志的写入形式为追加模式--fork
: 或-fork
开启新的进程运行mongodb服务--f
: 或-f
配置文件路径(可以将上述配置信息写入文件然后通过该文件中的参数进行加载启动)--auth
: 以权限认证的方式启动- 本机启动:
mongod -f /home/yezixuannao/mongodb/config/mongo.conf
- 只以
- 启动客户端:
mongo
- 测试情况下启动:
- 查看帮助:
mongo –help
- 退出:
exit
或者ctrl+c
语法:
- 数据库操作:
- 查看当前的数据库:
db
(没有切换数据库的情况下默认使用test数据库) - 查看所有的数据库:
show dbs /show databases
- 切换数据库:
use db_name
(db_name为show dbs后返回的数据库名) - 删除当前的数据库:
db.dropDatabase()
(想要删除数据库,就只能在当前的数据库中删除当前的数据库)
- 查看当前的数据库:
- 集合操作:
- 无需手动创建集合: 向不存在的集合中第一次添加数据时,集合会自动被创建出来
- 手动创建集合:
db.createCollection(name,options)
db.createCollection("stu")
db.createCollection("sub", { capped : true, size : 10 } )
- 参数
capped
:默认值为false表示不设置上限,值为true表示设置上限,当设置上限但是不指定上限为多少时,默认为256个字节,当设定的上限大小低于256时,系统自动设置为256个字节(一个字节=2个英文字母,一个汉字需要2.5到3个字节才能表示) - 参数
size
:集合所占用的字节数。 当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时, 会将之前的数据覆盖,单位为字节 - 固定大小的数据库,当已满时:新数据能存,同时会将最老的数据删除。固定大小的数据库不允许修改,只能删除,一般用于log日志,其他地方用的不多
- 参数
- 查看集合:
show collections
- 删除集合:db.集合名称.drop()
- 检查集合是否设定上限: db.集合名.isCapped()
- 查询:
- 简单查询
db.stu.find()
显示stu
集合中的所有文档db.stu.findOne()
显示stu
集合中的第一条文档db.stu.find().pretty()
以多行形式显示一条文档,显示stu
中的全部文档一次默认显示20条文档,超过则需要输入
it
继续展示
- 比较运算符:
db.stu.find({age:{$gte:18}})
- 等于: 默认是等于判断, 没有运算符
- 小于:
$lt (less than)
- 小于等于:
$lte (less than equal)
- 大于:
$gt (greater than)
- 大于等于:
$gte
- 不等于:
$ne
- 逻辑运算符:
- and:
db.stu.find({age:{$gte:18},gender:true})
- or:
db.stu.find({$or:[{age:{$gte:18}},{gender:true}],name:'gj'})
- and:
- 范围运算符:
- $in:
db.stu.find({age:{$in:[18,28,38]}})
- $nin
- $in:
- 正则表达式:
db.stu.find({name:{$regex:'^黄'}})
或者db.stu.find({name:{$regex:/黄/}})
双斜杠中间的字符串表示正则
- 自定义查询:
-
db.stu.find({ $where:function() { return this.age>30;} })
-
- 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的效率要高于前者。两者无论位置先后,都是先执行skip
- 方法limit(): 用于读取指定数量的文档
- 投影
- 在查询到的返回结果中, 只选择必要的字段
- 命令:db.集合名称.find({},{字段名称:1,…}),参数为字段与值, 值为1表示显示, 值为0不显
db.stu.find({},{_id:0,name:1,gender:1})
特别注意:对于_id列默认是显示的, 如果不显示需要明确设置为0.对于其他不显示的字段不设置即可。整个式子中同时只能全部写0或是1,但是_id字段可以随意写
- 排序
- 方法sort(), 用于对查询结果按照指定的字段进行排序
- 命令:db.集合名称.find().sort({字段:1,…}),参数1为升序排列 参数-1为降序排列
- 例子:根据性别降序, 再根据年龄升序
db.stu.find().sort({gender:-1,age:1})
排序在前的条件优先执行
- 统计个数
- 方法count()用于统计结果集中文档条数
- 命令:db.集合名称.find({条件}).count() 命令:db.集合名称.count({条件})
-
db.stu.find({gender:true}).count() db.stu.count({age:{$gt:20},gender:true})
- 去重
- 示例:
db.stu.distinct("age",{查询条件})
先按查询条件进行查询,之后去重
- 示例:
- 简单查询
- 增加:
- insert
db.stu.insert({name:'gj', gender:1}) db.stu.insert([{name:'gj', gender:1},{name:"laief", gender:1}])使用列表插入多个 db.stu.insert({_id:"20170101", name:'gj', gender:1})
插文档时,如果不指定
_id
参数,MongoDB
会为文档自动分配一个唯一的ObjectId
- save
db.stu.save({_id:'20170101', name:'gj', gender:2})
如果文档的_id已经存在则覆盖,如果_id不存在则添加
- update
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/upsert:true}) # 第一个参数留空,表示匹配全部。第三个参数multi:true表示更新全部。第三个参数upsert:true,表示如果按第一个参数匹配到数据,则更新数据,如果没有匹配到,则新增一条文档
注意:multi参数必须和$set一起使用!
- insert
- 删除
db.集合名称.remove({query}, {justOne: boolean})
- 参数query:可选,使用该条件筛选删除的文档
- 参数justOne:可选, 如果设为true或1,则只删除一条,默认false,表示删除全部
聚合操作:
- 定义
- 聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
- 语法
db.集合名称.aggregate({管道:{表达式}})
- 常用命令
- $group
- 用来将集合中的文档分组,可用于统计结果
- 示例:
db.stu.aggregate( {$group: { _id:"$gender", counter:{$sum:1} } } )
注意:
db.db_name.aggregate
是语法,所有的管道命令都需要写在后面的小括号中_id
表示分组的依据,即按照哪个字段进行分组。这里表示需要使用gender
这个字段进行分组$sum:1
表示把每多1条数据总数增加1,counter
统计的是每个分组下面数据的总条数- 像
list
这样的用法称为数据透视。表示一个列表,该列表中的每一个元素,记录了分组后,每个组中被合并成同组的,每一个文档中指定的字段。上述例子中:表示使用name
作为指定字段,以此为依据构建列表,分组后的每一组中都有list
这个键,该键对应的值就是一个由name
字段构建的列表。
group by null
- 当我们需要统计整个文档的时候,$group 的另一种用途就是把整个文档分为一组进行统计
- 示例:
db.stu.aggregate( {$group: { _id:null, counter:{$sum:1} } } )
注意:
_id:null
表示不指定分组的字段,即统计整个文档,此时获取的counter表示整个文档的个数- 数据透视
- 示例:依据性别进行分组,并且统计两组中所有学生的姓名
使用db.stu.aggregate( {$group: { _id:"$gender", name:{$push:"$name"} } } )
$$ROOT
可以将整个集合放入数组中db.stu.aggregate( {$group: { _id:null, name:{$push:"$$ROOT"} } } )
- 示例:依据性别进行分组,并且统计两组中所有学生的姓名
- $match
- 用于进行数据的过滤,是能够在聚合操作中使用的命令,和
find
区别在于$match
操作可以把结果交给下一个管道处理,而find
不行 - 示例:
db.stu.aggregate( {$match:{age:{$gt:20}} # gt:大于,great then {$group:{_id:"$gender",counter:{$sum:1}}} )
- 用于进行数据的过滤,是能够在聚合操作中使用的命令,和
- $project
- 用于修改文档的输入输出结构,例如重命名,增加,删除字段
- 示例:
db.stu.aggregate( {$group:{_id:"$gender",counter:{$sum:1}}} {$project:{_id:0,counter:1}} )
注意:用法与投影完全一致
- $sort
- 用于将输入的文档排序后输出
- 示例:
_id:"$gender"
按照性别进行分组,counter:{$sum:1}
并且统计两组人数,{$sort:{counter:-1}}
两组按各自总人数降序排列db.stu.aggregate( {$group:{_id:"$gender",counter:{$sum:1}}}, {$sort:{counter:-1}} )
- $skip 和 $limit
$limit
限制返回数据的条数,$skip
跳过指定的文档数,并返回剩下的文档数,同时使用时先使用skip
再使用limit
- 示例:统计男女生人数,按照人数升序,
{$skip:1}
跳过第一条数据,{$limit:1}
只返回一条数据db.stu.aggregate( {$group:{_id:"$gender",counter:{$sum:1}}}, {$sort:{counter:-1}}, {$skip:1}, {$limit:1} )
- $group
索引:
- _id字段默认带有索引
- 创建索引
- 语法:
db.集合名.ensureIndex({属性:1})
1表示升序, -1表示降序 - 检验速度:
db.t1.find({name:'test10000'}).explain('executionStats')
指定查找name
字段为text10000
的文档,输出查找时间等结果
- 语法:
- 查看索引:
- 语法:
db.集合名.getIndexes()
- 语法:
- 删除索引
- 语法:
db.集合名.dropIndex({索引所在的字段:1})
- 语法:
- 创建唯一索引
- 在默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据
- 语法:
db.集合名.ensureIndex({"字段名":1}, {"unique":true})
- 建立复合索引
- 语法:
db.collection_name.ensureIndex({字段1:1,字段2:1})
- 语法:
注意:
- 删除复合索引与删除单字段索引类似,删除时传入创建索引时使用的所有字段即可
- 复合索引默认允许多个文档重复,如果需要创建复合唯一索引,参考上面的创建单一字段唯一索引
- 复合索引中,单独使用字段1进行查询,性能会有提升,但是单独使用字段2进行查询,却不会有性能的提升。同时使用两个字段进行查询,性能也有提升
- 索引字段是升序还是降序在单个索引的情况下不影响查询效率,但是带复合索引的条件下会有影响
权限管理:
- MongoDB权限情况:
- MongoDB是没有默认不开启权限认证,所以要先添加管理员账号,并且在运行时开启验证模式(或者在配置文件中添加一行:
auth=True
) - 用户只能在用户所在数据库登录(创建用户的数据库),包括管理员账号。
- 管理员可以管理所有数据库,但是不能直接管理其他数据库,要先认证登录后才可以。
- MongoDB是没有默认不开启权限认证,所以要先添加管理员账号,并且在运行时开启验证模式(或者在配置文件中添加一行:
- 创建超级用户:
- 进入mongo shell:
sudo mongod
- 使用admin数据库(超级管理员账号必须创建在该数据库上,同时该数据库不需要手动创建):
use admin
- 创建超级用户:
db.createUser({"user":"python","pwd":"python","roles":["root"]})
- 进入mongo shell:
- 以权限认证的方式启动:
sudo mongod --auth
(如果已经在配置文件中添加auth=true
,那么就不需要再使用--auth
启动),本机启动方式:mongod -f 配置文件位置
- 登录验证:
- 此时再使用数据库各命令的时候会报权限错误,需要认证才能执行相应操作
use admin
db.auth('python','python')
第一个python
是用户名,第二个是密码
注意:
python
用户是创建在admin
数据库上的所以必须来到admin数据库上进行认证。认证成功会返回1,失败返回0
- 此时再使用数据库各命令的时候会报权限错误,需要认证才能执行相应操作
- 创建普通用户:
- 第一种:在普通数据库上创建
- 选择需要创建用户的数据库:
use test1
- 创建用户:
db.createUser("user":"user1", "pwd":"pwd1", roles:["read"]) 创建普通用户user1,该用户在test1上的权限是只读 db.createUser("user":"user1", "pwd":"pwd1", roles:["readWrite"]) 创建普通用户user1,该用户在test1上的权限是读写
- 选择需要创建用户的数据库:
- 第二种:在admin数据库上创建
- 选择admin数据库:
use admin
- 创建用户:
db.createUser({"user":"python1", "pwd":"python1", roles:[{"role":"read","db":"dbname1"},{"role":"readWrite","db":"dbname2"}]}) 创建python1用户,该用户在dbname1上具有读权限,在dbname2上具有读写权限
- 选择admin数据库:
- 第一种:在普通数据库上创建
注意:推荐使用第二种方法创建
- 查看创建的用户
show users
- 删除用户
- 进入账号数据所在的数据库:
use db_name
全部用户创建在admin数据库上的好处在这里就体现出来了:更方便管理 - 删除用户:
db.dropUser('python')
- 进入账号数据所在的数据库:
mongodb和python交互-------pymongo模块
- 安装方法:
pip3 install pymongo
- 无需权限认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClient
client = MongoClient(host,port) # 如果是本地连接host,port参数可以省略
collection = client[db名][集合名]
- 需要权限认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClient
client = MongoClient(host, port)
# 选择数据库
db = client["创建有用户的数据库名"]
db.authenticate("python", "python")
# 选择集合
col = client["要操作的数据库名"]["要操作的集合名"]
- 增加/修改:
- insert()
- 增加数据
# 该方法返回插入数据的_id collection.insert({"字段名":字段值}) # 该方法返回_id构成的列表 collection.insert([{"字段名":字段值},{"字段名":字段值}])
- update():
- 更新数据(全文档覆盖或指定键值,更新一条或多条)
collection.update({条件}, {'$set':{指定的kv或完整的一条数据}}, multi=False/True, upsert=False/True) multi参数:默认为False,表示更新一条; multi=True则更新多条; multi参数必须和$set一起使用 upsert参数:默认为False; upsert=True则先查询是否存在,存在则更新;不存在就插入 $set表示指定字段进行更新
- 更新一条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据1','name':'哈哈'} client.["要操作的数据库名"].["要操作的集合名"].update({'haha': 'heihei'}, {'$set':data}, upsert=True)
- 更新多条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据2','name':'哈哈'} # 该完整数据是先查询后获取的 client.["要操作的数据库名"].["要操作的集合名"].update({}, {'$set':data}, multi=True, upsert=True)
- 更新一条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___1'} client.["要操作的数据库名"].["要操作的集合名"].update({}, {'$set':data}, upsert=True)
- 更新多条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___2'} client.["要操作的数据库名"].["要操作的集合名"].update({}, {'$set':data}, multi=True, upsert=True)
- 更新数据(全文档覆盖或指定键值,更新一条或多条)
- insert()
- 查询:
- find_one():
- 接收一个字典形式的条件,返回字典形式的整条数据 如果条件为空,则返回第一条
ret = client.["数据库名"].["集合名"].find_one({'name': 'test10001'}) 如果使用了前面的用户认证登录后,可以直接查询指定集合:ret = col.find_one({"name":"test10001"}) print(ret) # 输出查询获得的文档
- 接收一个字典形式的条件,返回字典形式的整条数据 如果条件为空,则返回第一条
- find():
- 查找全部符合条件的数据,如果条件为空,则返回全部。返回值是一个Cursor游标对象,可迭代,可以类似读文件的指针,但是只能够进行一次读取
rets = collection.find({"name":"test10005"}), for ret in rets: print(ret) for ret in rets: #此时rets中没有内容 print(ret)
- 查找全部符合条件的数据,如果条件为空,则返回全部。返回值是一个Cursor游标对象,可迭代,可以类似读文件的指针,但是只能够进行一次读取
- find_one():
- delete_one()
- 删除符合条件的一条数据:
collection.delete_one({"name":"test10010"})
- 删除符合条件的全部数据:
collection.delete_many({"name":"test10010"})
- 删除符合条件的一条数据:
文中本机使用
都是我自己的电脑上的使用方法,与常用方法可能会有区别,仅供参考