Mongodb数据库
mongodb的介绍
-
mongodb的介绍
- 什么是mongodb
- mongodb 是一个功能最丰富的NoSQL非关系数据库。由 C++ 语言编写。 mongodb 本身提供S端存储数据,即server;也提供C端操作处理(如查询等)数据,及client
- 什么是mongodb
-
SQL和NoSQL的主要区别
- 在SQL中层级关系: 数据库>表>数据 而在NoSQL中则是: 数据库>集合>文档
-
数据之间无关联性
-
SQL中如何需要增加外部关联数据的话,规范化做法是在原表中增加一个外键,关联外部数据表 NoSQL则可以把外部数据直接放到原数据集中,以提高查询效率。缺点也比较明显,对关联数据做更新时会比较麻烦。
SQL中在一个表中的每条数据的字段是固定的。而NoSQL中的一个集合(表)中的每条文档(数据)的key(字段)可以是互不相同的。
-
-
拓展阅读
-
mongodb作为非关系型数据库相较于关系型数据库的优势
-
易扩展: NoSQL数据库种类繁多, 但是一个共同的特点都是去掉关系数据库的关系型特性。 数据之间无关系, 这样就非常容易扩展
大数据量,高性能: NoSQL数据库都具有非常高的读写性能, 尤其在大数据量下表现优秀。 这得益于它的非关系性,数据库的结构简单
灵活的数据模型: NoSQL无需事先为要存储的数据建立字段, 随时可以存储自定义的数据格式。 而在关系数据库中, 增删字段是一件非常麻烦的事情。 如果是非常大数据量的表, 增加字段简直就是一个噩梦
-
mongodb的安装
以ubuntu18.04为例
-
命令安装
-
在ubuntu中使用apt-get工具安装
sudo apt-get install -y mongodb-org
-
-
源码安装
-
选择相应版本和操作系统并下载
-
解压
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中
cd ~ sudo vi .bashrc export PATH=/usr/local/mongodb/bin:$PATH
-
mongodb的中文文档
-
mongodb的简单使用
-
mongodb服务端的使用
- 默认端口:27017
- 默认配置文件的位置:
- /etc/mongod.conf
- 默认日志的位置
- /var/log/mongodb/mongod.log
-
mongodb服务端启动分别两种方式:
- 本地测试方式的启动(只具有本地数据增删改查的功能)
- 生产环境启动(具有完整的全部功能)
-
测试方式启动
- 启动:sudo service mongodb start
- 停止:sudo service mongodb stop
- 重启:sudo service mongodb 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: 以权限认证的方式启动,我们会在后边的课程中学习该内容
-
查看是否启动成功
ps aux | grep mongod
-
mongodb数据库命令
- 查看当前数据库
- db(没有切换数据库的情况下默认使用test数据库)
- 查看所有的数据库
- show dbs /show databases
- 切换数据库
- use db_name
- 删除当前的数据库
- db.dropDatabase()
- 查看当前数据库
-
mongodb集合命令
- 手动创建集合:
- db.createCollection(name,options)
- db.createCollection(“stu”)
- db.createCollection(“sub”, { capped : true, size : 10 } )
- 参数capped:默认值为false表示不设置上限,值为true表示设置上限
- 参数size:集合所占用的字节数。 当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时, 会将之前的数据覆盖,单位为字节
- 查看集合:
- show collections
- 删除集合:
- db.集合名称.drop()
- 检查集合是否设定上限:
- db.集合名.isCapped()
- 手动创建集合:
-
简单练习
show dbs # 查看所有数据库 use test # 切换test 数据库 show collections # 查看当前数据库集合 db #查看当前数据库 db.stu.insert({'name':'晓峰', 'age':22}) #添加一条记录 show dbs show collections db.stu.find() db.stu.drop() show collections db.dropDatabase() show dbs exit
-
简单练习
- 常见类型
- Object ID: 文档ID/数据的ID,数据的主键
- String: 字符串,最常用,必须是有效的UTF-8
- Boolean: 存储一个布尔值,true或false
- Integer: 整数可以是32位或64位,这取决于服务器
- Double: 浮点数
- Arrays: 数组/列表
- Object: mongodb中的一条数据/文档,即文档嵌套文档
- Null: 存储null值
- Timestamp: 时间戳,表示从1970-1-1到现在的总秒数
- Date: 存储当前日期或时间的UNIX时间格式
- 注意点
- 每个文档都有一个属性,为_id,保证每个文档的唯一性,mongodb默认使用_id作为主键
- 可以手动设置_id的值,如果没有提供,那么MongoDB为每个文档提供了一个独特的_id, 类型为objectID
- objectID是一个12字节的十六进制数,每个字节两位,一共是24位的字符串:
- 前4个字节为当前时间戳
- 接下来3个字节的机器ID
- 接下来的2个字节中MongoDB的服务进程id
- 最后3个字节是简单的增量值
- 常见类型
mongodb的增删改查
-
mongodb插入数据
-
命令:
- db.集合名称.insert(document)
db.stu.insert({name:'joker',gender:1}) db.stu.insert({_id:"20110101",name:'tom',gender:1}) #测试数据 db.stu.insert({"name" : "郭靖", "hometown" : "蒙古", "age" : 20, "gender" : true }) db.stu.insert({"name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false }) db.stu.insert({"name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false }) db.stu.insert({"name" : "黄药师", "hometown" : "桃花岛", "age" : 40, "gender" : true }) db.stu.insert({"name" : "段誉", "hometown" : "大理", "age" : 16, "gender" : true }) db.stu.insert({"name" : "段王爷", "hometown" : "大理", "age" : 45, "gender" : true }) db.stu.insert({"name" : "洪七公", "hometown" : "华筝", "age" : 18, "gender" : true })
注意:插入文档是,如果不指定_id参数,MongoDB会为文档自动分配一个唯一的ObjID
-
-
mongodb的保存
-
命令:
- db.集合名称.save(document)
db.stu.save({_id:'20180101',name:'zhangsan',gender:2}) db.stu.save({name:'zhangsan',gender:2}) db.stu.find()
如果文档的_id已经存在则修改,如果\_id不存在则添加
-
-
mongodb的查询
-
命令:
- db.集合名称.find()
-
方法find():查询
db.集合名称.find({条件文档})
-
方法findOne():查询,只返回第一个
db.集合名称.findOne({条件文档})
-
方法pretty(): 将结果格式化;不能和findOne()一起使用!
db.集合名称.find({条件文档}).pretty()
-
-
比较运算符
方法名 描述 $lte 小于等于 $lt 小于 $gte 大于等于 $gt 大于 $eq 等于 $ne 不等于 $in 包含 $min 不包含 -
用法:
-
$lte-小于等于
db.collection.find({"field":{$lte:value}});
-
示例
db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于8的数据,如下面的代码所示:
db.c1.find({age:{$lte:7}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
$lt -小于
db.collection.find({"field":{$lt:value}});
-
示例
db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于8的数据,如下面的代码所示:
>db.c1.find({age:{$lt:8}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
$gte - 大于等于
db.collection.find({"field":{$gte:value}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于8的数据,如下面的代码所示:
>db.c1.find({age:{$gte:7}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30}
可以看出,只显示age大于等于7的数据,age等于6的数据没有显示出来。
-
$gt -大于
db.collection.find({"field":{$gt:value}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于8的数据,如下面的代码所示:
>db.c1.find({age:{$gt:6}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30}
可以看出,只显示age大于6的数据,age等于6的数据没有显示出来。
-
$eq - 等于
db.collection.find({"field":{$eq:value}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于8的数据,如下面的代码所示:
>db.c1.find({age:{$eq:7}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30}
可以看出,只显示age等于7的数据,其他数据没出来。
-
$ne - 不等于
db.things.find({x:{$ne:3}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值不等于7的数据,如下面的代码所示:
>db.c1.find({age:{$ne:7}}) {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
可以看出,只显示age不等于7的数据,age等于7的数据没有显示出来。
-
$in -包含
db.things.find({x:{$in:[2,4,6]}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值在7、8范围内的数据,如下面的代码所示:
>db.c1.find({age:{$in:[7,8]}}) {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30}
可以看出只显示age等于7或8的数据,其他不符合规则的数据并没有显示出来。
-
$min -不包含
db.things.find({x:{$nin:[2,4,6]}});
-
示例
>db.c1.find() {"_id":ObjectId("4fb4af85afa87dc1bed94330"),"age":7,"length_1":30} {"_id":ObjectId("4fb4af89afa87dc1bed94331"),"age":8,"length_1":30} {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
-
查询age的值在7、8范围外的数据,如下面的代码所示:
>db.c1.find({age:{$nin:[7,8]}}) {"_id":ObjectId("4fb4af8cafa87dc1bed94332"),"age":6,"length_1":30}
通过结果可以看出只显示age不等于7或8的数据,其他不符合规则的数据并没有显示出来。
-
-
逻辑运算符
-
and: 在son 中写多个条件即可
#查询年龄大于或等于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'})
-
-
范围运算符
-
使用
$in
,$nin
判断数据是否在某个数组内#查询年龄为18、 28的学生 db.stu.find({age:{$in:[18,28,38]}})
-
-
支持正则表达式
-
使用$regex编写正则表达式
查询name以'黄'开头的数据 db.stu.find({name:{$regex:'^黄'}})
-
-
自定义查询
-
mongo shell 是一个js的执行环境 使用$where 写一个函数, 返回满足条件的数据
#查询年龄大于30的学生 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的效率要高于前者
-
-
投影
-
在查询到的返回结果中, 只选择必要的字段
-
命令:
db.集合名称.find({},{字段名称:1,…})
-
参数为字段与值, 值为1表示显示, 值为0不显
特别注意:
- 对于_id列默认是显示的, 如果不显示需要明确设置为0
- 对于其他不显示的字段不能设置为0
-
db.stu.find({},{_id:0,name:1,gender:1})
-
-
排序
-
方法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})
-
-
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}) # 更新全部
-
-
注意:"multi update only works with $ operators"
- multi参数必须和$set一起使用!
-
mongodb删除
-
db.集合名称.remove({query}, {justOne: boolean})
- 参数query:可选,删除的⽂档的条件 - 参数justOne:可选, 如果设为true或1,则只删除一条,默认false,表示删除全部
-
mongodb的索引操作
-
为什么mongodb要创建索引
- 加快查询速度
- 进行数据的去重
-
mongodb创建简单的索引方法
``db.集合名.ensureIndex({属性:1})
,1表示升序, -1表示降序
-
索引查看
db.集合名.getIndexes()
-
删除索引
-
db.集合名.dropIndex({'索引名称':1})
db.t1.dropIndex({name:1}) db.t1.getIndexes()
-
-
mongodb创建唯一索引
- 在默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据,但是创建索引仅仅能够提高查询速度,同时降低数据库的插入速度。
-
添加唯一索引的语法:
db.集合名.ensureIndex({"字段名":1}, {"unique":true})
-
利用唯一索引进行数据去重
- 根据唯一索引指定的字段的值,如果相同,则无法插入数据
db.t1.ensureIndex({"name":1}, {"unique":true}) db.t1.insert({name: 'test10000'})
-
建立复合索引
- 建立复合索引的语法
db.collection_name.ensureIndex({字段1:1,字段2:1})
- 建立复合索引的语法
mongodb的权限索引
-
为什么要进行权限管理的设置
- 刚安装完毕的mongodb默认不使用权限认证方式启动,与MySQL不同,mongodb在安装的时候并没有设置权限,然而公网运行系统需要设置权限以保证数据安全,所以我们要学习mongodb的权限管理
-
mongodb的权限管理方案
- MongoDB是没有默认管理员账号,所以要先添加管理员账号,并且mongodb服务器需要在运行的时候开启验证模式
- 用户只能在用户所在数据库登录(创建用户的数据库),包括管理员账号
- 管理员可以管理所有数据库,但是不能直接管理其他数据库,要先认证后才可以。
-
mongodb超级管理员账号的创建
-
创建超级用户
- 进入mongo shell
sudo mongod
-
使用admin数据库(超级管理员账号必须创建在该数据库上)
user admin
-
创建超级用户
db.createUser({"user":"python","pwd":"python","roles":["root"]})
-
创建成功会显示如下信息
Successfully added user: { "user" : "python", "roles" : [ "root" ] }
-
-
以权限认证的方式启动mongodb数据库
sudo mongod —auth
-
启动之后在启动信息中会有如下信息,说明mongodb以权限认证的方式启动成功
[initandlisten] options: { security: { authorization: "enabled" } }
-
-
登录验证
-
此时再使用数据库各命令的时候会报权限错误,需要认证才能执行相应操作、
use admin db.auth('python','python')
-
python用户是创建在admin数据库上的所以必须来到admin数据库上进行认证
-
认证成功会返回1,失败返回0
-
-
创建普通用户
-
在使用的数据库上创建普通用户
-
选择需要创建的用户数据库
use test
-
-
创建用户
db.createUser("user":"user1", "pwd":"pwd1", roles:["read"]) #创建普通用户user1,该用户在test1上的权限是只读 db.createUser("user":"user1", "pwd":"pwd1", roles:["readWrite"]) #创建普通用户user1,该用户在test1上的权限是读写
-
在admin用户数据库上创建普通用户
use admin db.createUser({"user":"python1", "pwd":"python1", roles:[{"role":"read","db":"dbname1"},{"role":"readWrite","db":"dbname2"} ]})
-
在admin上创建python1用户,python1用户的权限有两个,一个再dbname1上的只读,另一个是在dbname2上的读写
-
-
查看创建的用户
show users { "_id" : "admin.python", "user" : "python", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] }
-
删除用户
-
进入账号数据所在的数据库
use db_name
-
删除用户
db.dropUser('python')
-
mongodb和python交互
-
mongodb和python交互的模块
pymongo
提供了mongdb和python交互的所有方法 安装方式:pip install pymongo
-
导入pymongo并选择要操作的集合
数据库和集合能够自动创建
-
无需谦虚认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClient client = MongoClient(host,post) #如果本地连接host,post参数可以忽略 collection = client[db名][集合名] #collection = client.db名.集合名
-
需要权限认证的方式创建连接对象以及集合操作对象
from pymongo import MongoClient from urllib.parse import quote_plus user = 'python' #账号 password = 'python'#密码 host = '127.0.0.1' #host port = 27017 uri = "mongodb://%s:%s@%s" % (quote_plus(user), quote_plus(password), host) # quote_plus函数:对url进行编码 # uri = mongodb://python:python@127.0.0.1 client = MongoClient(uri, port=port) collection = client.db名.集合名
-
Insert()添加数据
-
insert可以批量的插入数据列表,也可以插入一条数据
collection.insert({一条数据}) collection.insert([{数据一},{数据二}])
-
-
添加一条数据
-
返回插入数据的_id
ret = collection.insert({"name":"joker","age":18}) print(ret)
-
添加多条数据
- 返回ObjectId对象构成的列表
item_list = [{"name":"test1000{}".format(i)} for i in range(10)] rets = collection.insert(item_list) print(rets) for ret in rets: print(ret)
-
-
find_one()查找一条数据
-
接收一个字典形式的条件,返回字典形式的整条数据 如果条件为空,则返回第一条
ret = client.test.test.find_one({'name': 'joker'}) print(ret) #包含mongodb的ObjectId对象的字典 _ = ret.pop('_id') # 清除mongodb的ObjectId对象的k,v print(ret)
-
find()查找全部数据
返回所有满足条件的结果,如果条件为空,则返回全部 结果是一个Cursor游标对象,是一个可迭代对象,可以类似读文件的指针,但是只能够进行一次读取
rets = collection.find({"name":"test10005"}), for ret in rets: print(ret) for ret in rets: #此时rets中没有内容 print(ret)
-
-
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.test.test.update({'haha': 'heihei'}, {'$set':data}, upsert=True)
-
更新多条数据;全文档覆盖;存在就更新,不存在就插入
data = {'msg':'这是一条完整的数据2','name':'哈哈'} # 该完整数据是先查询后获取的 client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
-
更新一条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___1'} client.test.test.update({}, {'$set':data}, upsert=True)
-
更新多条数据;指定键值;存在就更新,不存在就插入
data = {'msg':'指定只更新msg___2'} client.test.test.update({}, {'$set':data}, multi=True, upsert=True)
-
delete_one()删除一条数据
collection.delete_one({"name":"test10010"})
-
delete_many()删除全部数据
collection.delete_many({"name":"test10010"})