之前学过的有mysql数据库,现在我们学习一种非关系型数据库
一、简介
MongoDB是一款强大、灵活、且易于扩展的通用型数据库
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
在高负载的情况下,添加更多的节点,可以保证服务器性能。
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
1、易用性
MongoDB是由C++编写的,是一个基于分布式文件存储的开源数据库系统,它不是关系型数据库。在高负载的情况下,添加更多的节点,可以保证服务器的性能。
MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
不采用关系型主要是为了获得更好得扩展性。当然还有一些其他好处,与关系数据库相比,面向文档的数据库不再有“行“(row)
的概念取而代之的是更为灵活的“文档”(document)模型。通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录
来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小
。由于没有固定的模式,根据需要添加或删除字段变得更容易了。通常由于开发者能够进行快速迭代,所以开发进程得以加快。
而且,实验更容易进行。开发者能尝试大量的数据模型,从中选一个最好的。
卓越的性能
MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计。MongoDB把尽可能多的内存用作缓存cache,
视图为每次查询自动选择正确的索引。
总之各方面的设计都旨在保持它的高性能
虽然MongoDB非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。
只要有可能,数据库服务器就会将处理逻辑交给客户端
。这种精简方式的设计是MongoDB能够实现如此高性能的原因之一
二、MongoDB基础知识
1、文档是MongoDB的核心概念。文档就是键值对的一个有序集{'msg':'hello','foo':3}。类似于python中的有序字典
需要注意的是:
#1、文档中的键/值对是有序的。
#2、文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
#3、MongoDB区分类型和大小写。
#4、MongoDB的文档不能有重复的键。
#5、文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。 文档键命名规范:
#1、键不能含有\0 (空字符)。这个字符用来表示键的结尾。
#2、.和$有特别的意义,只有在特定环境下才能使用。
#3、以下划线"_"开头的键是保留的(不是严格要求的)。
2、集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据的一行,那么一个集合就是相当于一张表
#1、集合存在于数据库中,通常情况下为了方便管理,不同格式和类型的数据应该插入到不同的集合,但其实集合没有固定的结构,
这意味着我们完全可以把不同格式和类型的数据统统插入一个集合中。 #2、组织子集合的方式就是使用“.”,分隔不同命名空间的子集合。
比如一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors,这是为了使组织结构更清晰,
这里的blog集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。
在MongoDB中,使用子集合来组织数据非常高效,值得推荐 #3、当第一个文档插入时,集合就会被创建。合法的集合名:
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。
除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
3、数据库:在MongoDB中,多个文档组成集合,多个集合可以组成数据库
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串:
#1、不能是空字符串("")。
#2、不得含有' '(空格)、.、$、/、\和\0 (空字符)。
#3、应全部小写。
#4、最多64字节。 有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
#1、admin: 从身份认证的角度讲,这是“root”数据库,如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从admin数据库运行,如列出所有数据库或关闭服务器
#2、local: 这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中
#3、config: MongoDB用于分片设置时,分片信息会存储在config数据库
4、强调:把数据库名添加到集合名前,得到集合的完全限定名,即命名空间
例如:
如果要使用cms数据库中的blog.posts集合,这个集合的命名空间就是
cms.blog.posts。命名空间的长度不得超过121个字节,且在实际使用中应该小于100个字节
基本数据类型:
基于BSON结构
string 字符串,Utf8
integer 整型
double 双精度,包含float,MongoDB中不存在Float
Null 空数据类型
Date 时间类型 ISODate(2018-9-12 10:45:58)
Timestamp 时间戳类型 ISODate("") ObjectId 对象ID Documents 自生成的 _id ObjectId("5b98794ec34b9812bcabdde7")
#"5b151f85" 代指的是时间戳,这条数据的产生时间
#"364098" 代指某台机器的机器码,存储这条数据时的机器编号
#"09ab" 代指进程ID,多进程存储数据的时候,非常有用的
#"2e6b26" 代指计数器,这里要注意的是,计数器的数字可能会出现重复,不是唯一的
#以上四种标识符拼凑成世界上唯一的ObjectID
#只要是支持MongoDB的语言,都会有一个或多个方法,对ObjectID进行转换
#可以得到以上四种信息
基本数据类型
三、安装
(此处说的是windows的)
下载好mongoDB之后,选择文件夹安装 ,然后配置环境变量,就是把你的环境变量配置到电脑上,然后你需要再建立一个文件夹用来存放mpongodb的数据库
安装目录:
D:\MongoDB
然后找到bin目录加入到电脑的环境变量种 然后再创建一个目录用来存放你的数据库 D:\MongoDB\data\db
目录一半都是创建data\db 然后这个时候用你的管理员的身份来运行cmd然后切换到你的 新建立的存放数据库的文件夹下:
D:\MongoDB\data\db
然后执行 mongod --dbpath +你的文件夹路径
mongod --dbpath D:\MongoDB\data\db
就把你的数据库连接打开了 然后你就可以通过其他连接 进行与你的mongodb数据库连接了(切忌:上一次的和数据库的链接不可以断开 就是选定的链接文件夹和数据库相连接)
选好安装目录 |
D:\MongoDB |
选择数据库存放目录 |
D:\MongoDB\data\db |
以管理员身份运行 |
mongod --dbpath D:\MongoDB\data\db |
然后再次打开新的连接 | 上次的链接不可以断开 需要再次打开新的链接进行数据库访问 |
2、账号管理
#账号管理:https://docs.mongodb.com/master/tutorial/enable-authentication/
#1、创建有权限的用户
use admin
db.createUser(
{
user: "root", #这个root可以随便写
pwd: "123",
roles: [ { role: "root", db: "admin" } ] #权限,role是root说明是管理员,
}
)
use test
db.createUser(
{
user: "egon",
pwd: "123",
roles: [ { role: "readWrite", db: "test" }, #针对test库有读写权限,操作自己的库有读写权限
{ role: "read", db: "db1" } ] #针对db1库读权限,操作其他库有读权限
}
)
#2、重启数据库
mongod --remove
mongod --config "C:\mongodb\mongod.cfg" --bind_ip 0.0.0.0 --install --auth
或者
mongod --bind_ip 0.0.0.0 --port 27017 --logpath D:\MongoDB\log\mongod.log --logappend --dbpath
D:\MongoDB\data\db --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install --auth #3、登录:注意使用双引号而非单引号
mongo --port 27017 -u "root" -p "123" --authenticationDatabase "admin" 也可以在登录之后用db.auth("账号","密码")登录
mongo
use admin
db.auth("root","123") #推荐博客:https://www.cnblogs.com/zhoujinyi/p/4610050.html 创建账号密码+开启认证机制
数据库的连接工具:
简单操作:
以上是一个简单的连接工具,就是先连接本机的数据库 也可以选择连接其他ip地址的数据库(仅仅限于mongoDB数据库)
在mongodb种数据库还是数据库的含义,但是表的定义没有了,叫集合(其实还是表)
基本数据类型
1、在概念上,MongoDB的文档与Javascript的对象相近,因而可以认为它类似于JSON。JSON(http://www.json.org)是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含六种数据类型。
2、这样有很多好处:易于理解、易于解析、易于记忆。然而从另一方面说,因为只有null、布尔、数字、字符串、数字和对象这几种数据类型,所以JSON的表达能力有一定的局限。
3、虽然JSON具备的这些类型已经具有很强的表现力,但绝大数应用(尤其是在于数据库打交道时)都还需要其他一些重要的类型。例如,JSON没有日期类型,这使得原本容易日期处理变得烦人。另外,JSON只有一种数字类型,无法区分浮点数和整数,更别区分32位和64位了。再者JSON无法表示其他一些通用类型,如正则表达式或函数。
4、MongoDB在保留了JSON基本键/值对特性的基础上,添加了其他一些数据类型。在不同的编程语言下,这些类型的确切表示有些许差异。下面说明了MongoDB支持的其他通用类型,以及如何正在文档中使用它们
5、数据类型
1 #1、null:用于表示空或不存在的字段
2 d={'x':null}
3 #2、布尔型:true和false
4 d={'x':true,'y':false}
5 #3、数值
6 d={'x':3,'y':3.1415926}
7 #4、字符串
8 d={'x':'egon'}
9 #5、日期
10 d={'x':new Date()}
11 d.x.getHours()
12 #6、正则表达式
13 d={'pattern':/^egon.*?nb$/i}
14
15 正则写在//内,后面的i代表:
16 i 忽略大小写
17 m 多行匹配模式
18 x 忽略非转义的空白字符
19 s 单行匹配模式
20
21 #7、数组
22 d={'x':[1,'a','v']}
23
24 #8、内嵌文档
25 user={'name':'egon','addr':{'country':'China','city':'YT'}}
26 user.addr.country
27
28 #9、对象id:是一个12字节的ID,是文档的唯一标识,不可变
29 d={'x':ObjectId()}
数据类型
6、_id和ObjectId
1 MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任意类型,默认是个ObjectId对象。
2 在一个集合里,每个文档都有唯一的“_id”,确保集合里每个文档都能被唯一标识。
3 不同集合"_id"的值可以重复,但同一集合内"_id"的值必须唯一
4
5 #1、ObjectId
6 ObjectId是"_id"的默认类型。因为设计MongoDb的初衷就是用作分布式数据库,所以能够在分片环境中生成
7 唯一的标识符非常重要,而常规的做法:在多个服务器上同步自动增加主键既费时又费力,这就是MongoDB采用
8 ObjectId的原因。
9 ObjectId采用12字节的存储空间,是一个由24个十六进制数字组成的字符串
10 0|1|2|3| 4|5|6| 7|8 9|10|11
11 时间戳 机器 PID 计数器
12 如果快速创建多个ObjectId,会发现每次只有最后几位有变化。另外,中间的几位数字也会变化(要是在创建过程中停顿几秒)。
13 这是ObjectId的创建方式导致的,如上图
14
15 时间戳单位为秒,与随后5个字节组合起来,提供了秒级的唯一性。这个4个字节隐藏了文档的创建时间,绝大多数驱动程序都会提供
16 一个方法,用于从ObjectId中获取这些信息。
17
18 因为使用的是当前时间,很多用户担心要对服务器进行时钟同步。其实没必要,因为时间戳的实际值并不重要,只要它总是不停增加就好。
19 接下来3个字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以保证不同主机生成不同的ObjectId,不产生冲突
20
21 接下来连个字节确保了在同一台机器上并发的多个进程产生的ObjectId是唯一的
22
23 前9个字节确保了同一秒钟不同机器不同进程产生的ObjectId是唯一的。最后3个字节是一个自动增加的 计数器。确保相同进程的同一秒产生的
24 ObjectId也是不一样的。
25
26 #2、自动生成_id
27 如果插入文档时没有"_id"键,系统会自帮你创建 一个。可以由MongoDb服务器来做这件事。
28 但通常会在客户端由驱动程序完成。这一做法非常好地体现了MongoDb的哲学:能交给客户端驱动程序来做的事情就不要交给服务器来做。
29 这种理念背后的原因是:即便是像MongoDB这样扩展性非常好的数据库,扩展应用层也要比扩展数据库层容易的多。将工作交给客户端做就
30 减轻了数据库扩展的负担。
_id 和objectid
五、增删改查操作
增删改查:
增:
- db.user.info #user.info表
- db.user #user表
- db.user.auth ##user.auth表 当第一个文档插入时,集合就会被创建
> use database1
switched to db database1
> db.table1.insert({'a':1})
WriteResult({ "nInserted" : 1 })
> db.table2.insert({'b':2})
WriteResult({ "nInserted" : 1 })
查:
- show collections
- show tables #这两个是一样的
#只要是空不显示
删:
- db.user.info.help() #查看帮助
- db.user.info.drop()
1、数据库的增:
增加数据库:
use +数据库名 # 有则切换无则新增 当你用use 创建完之后,你需要在insert之后才能显示出这个数据库 db.创建的数据库名.insertOne()
eg:
db.db.insertOne() 然后就会显示出你所创建的数据库 或者你直接使用db.创建的数据库名.insertOne()然后也可以直接创建出数据库
mongodb数据库的操作不论事增、删、改、查 都是需要用到db.你的数据的名然后再加上要操作的内容
你使用use创建的只是数据库 而当你插入第一条数据的时候你的集合就会被创建
创建数据库 | use+数据库名 | 有则切换 无则创建 |
查看服务器中的所有的数据库 | show dbs | |
db | 查看当前所在的数据库 | |
show tables | 查看当前数据库的中的集合 |
1、集合的增加insert
增加 | db.test.insert({"name":"老王"}) | 这个是对你的数据中插入一个集合为test的数据 |
多行增加 | db.test.insert([{"name":"隔壁老王"},{"hobby":"找人"}]) | 对你的数据库中的test集合插入两条数据 |
单行增加 | db.db.inertOne({"name":"隔壁老王"}) | 为你的数据中插入一个集合为db的数据 |
多行插入 | d.db.insertMany([{"name":"隔壁老王"},{"name":"你媳妇"}]) | 为你的数据库中插入一个集合 db,db集合中有两条数据 |
增:
#1、没有指定_id则默认ObjectId,_id不能重复,且在插入后不可变
2
3 #2、插入单条
4 user0={
5 "name":"egon",
6 "age":10,
7 'hobbies':['music','read','dancing'],
8 'addr':{
9 'country':'China',
10 'city':'BJ'
11 }
12 }
13
14 db.test.insert(user0)
15 db.test.find()
16
17 #3、插入多条
18 user1={
19 "_id":1,
20 "name":"alex",
21 "age":10,
22 'hobbies':['music','read','dancing'],
23 'addr':{
24 'country':'China',
25 'city':'weifang'
26 }
27 }
28
29 user2={
30 "_id":2,
31 "name":"wupeiqi",
32 "age":20,
33 'hobbies':['music','read','run'],
34 'addr':{
35 'country':'China',
36 'city':'hebei'
37 }
38 }
39
40
41 user3={
42 "_id":3,
43 "name":"yuanhao",
44 "age":30,
45 'hobbies':['music','drink'],
46 'addr':{
47 'country':'China',
48 'city':'heibei'
49 }
50 }
51
52 user4={
53 "_id":4,
54 "name":"jingliyang",
55 "age":40,
56 'hobbies':['music','read','dancing','tea'],
57 'addr':{
58 'country':'China',
59 'city':'BJ'
60 }
61 }
62
63 user5={
64 "_id":5,
65 "name":"jinxin",
66 "age":50,
67 'hobbies':['music','read',],
68 'addr':{
69 'country':'China',
70 'city':'henan'
71 }
72 }
73 db.user.insertMany([user1,user2,user3,user4,user5])
增
2、集合的删
删:db.dropDatabase() # db.user.drop() #删除user集合
db.user.remove({})
// remove 方式 被官方不推荐了
db.user.deleteOne({"name":"白虎"})
db.user.deleteMany({"name":"翠花儿"})
1 #1、删除多个中的第一个
2 db.user.deleteOne({ 'age': 8 })
3
4 #2、删除国家为China的全部 这个是删除你的所有的满足条件的
5 db.user.deleteMany( {'addr.country': 'China'} )
6
7 #3、删除全部
8 db.user.deleteMany({})
可视化工具
3、查 find
db.+集合名.find() #查看这个集合内部的所有内容 db.test.find() #查看test集合中的所有内容 根据条件查找
db.test.find(条件) db.test.find("name":"老王") #查找test集合中的name是老王的信息 findOne() 查找单行
db.one.findOne({"name":"老王",age:1}) 逗号代表and
数字比较符:
$lt |
小于 |
$gt |
大于 |
$lte |
小于等于 |
$gte |
大于等于 |
$and | 和 |
$or | 或者 |
$not | 不是 |
$mod | 取模运算 |
$gt 大于 db.test.findOne({"id":{$gt:1}}) 求集合test中id大于1的信息 $lt 小于
db.test.findOne({"id":{$lt:5}}) 集合test中id小于5的信息 $lte小于等于
db.test.findOne({"id":{$lte:3}}) 集合test中id小于等于5的信息 $gte 大于等于
db.user.find({
"_id":{"$gte":3,"$lte":4},
"age":{"$gte":40} #结束就不要加逗号了
}) $and # 并列查询 以逗号分隔
db.user.find({"$and":[
{"_id":{"$gte":3,"$lte":4}}, #一个字典就是一个条件
{"age":{"$gte":40}}
]}
select * from db1.user where id >=0 and id <=1 or id >=4 or name = "yuanhao";
db.user.find({"$or":[
{"_id":{"$lte":1,"$gte":0}},
{"_id":{"$gte":4}},
{"name":"yuanhao"}
]})
select * from db1.user where id % 2 = 1; #奇数
db.user.find({"_id":{"$mod":[2,1]}}) #取模运算,id对2取模 #取反,偶数
db.user.find({
"_id":{"$not":{"$mod":[2,1]}}
})
$in | 在里面 |
$nin | 不在 |
# SQL:in,not in
2 # MongoDB:"$in","$nin"
3
4 #1、select * from db1.user where age in (20,30,31);
5 db.user.find({"age":{"$in":[20,30,31]}})
6
7 #2、select * from db1.user where name not in ('huahua','xxx');
8 db.user.find({"name":{"$nin":['hauhua','xxx']}})
$in | 在什么里面 |
$or | 或者 |
$all | 必须要包含所有的 |
$in: db.user.find({age:{$in:[1,2,3]}}) 字段符合in,array中的数值 $or: db.user.find({$or:[{age:{$in:[1,2,3,4]}},{name:666}]}) $all: db.user.find({test:{$all:[1,2]}}) array元素相同即可 或 包含所有$all中的元素
改:update
1 update() 方法用于更新已存在的文档。语法格式如下:
2 db.collection.update(
3 <query>,
4 <update>,
5 {
6 upsert: <boolean>,
7 multi: <boolean>,
8 writeConcern: <document>
9 }
10 )
11 参数说明:对比update db1.t1 set name='EGON',sex='Male' where name='egon' and age=18;
12
13 query : 相当于where条件。
14 update : update的对象和一些更新的操作符(如$,$inc...等,相当于set后面的
15 upsert : 可选,默认为false,代表如果不存在update的记录不更新也不插入,设置为true代表插入。
16 multi : 可选,默认为false,代表只更新找到的第一条记录,设为true,代表更新找到的全部记录。
17 writeConcern :可选,抛出异常的级别。
18
19 更新操作是不可分割的:若两个更新同时发送,先到达服务器的先执行,然后执行另外一个,不会破坏文档。
updat语法介绍
$set : 将某个key的value 修改为 某值
$unset: 删除字段 {$unset: {test: "" }} $inc:{$inc:{"age":100}} 原有值基础上增加 xxx array:
$push : 向数组Array最后位置插入一条元素 {$push:{"hobby":1234}} [].append(1234)
$pull : 删除数组中的某一个元素 {$pull:{"hobby":123}}
#删除字典 db.user.updateOne({"_id":ObjectId("5b98794ec34b9812bcabdde7"),"hobby.age":18},{$unset:{"hobby.$":1}})
$pop : 删除数组的第一个或最后一个
最后一个:{$pop:{"hobby":1}}
第一个:{$pop:{"hobby":-1}} $ :代指符合条件的某数据 array.$ :
db.user.updateOne({"_id":ObjectId("5b98794ec34b9812bcabdde7"),"hobby.name":"翠花儿"},{$set:{"hobby.$.name":"农妇山泉"}})
后面条件的$带表指的是前面的条件 就是告诉你要修改的是前面的条件
update修改:
updateOne 修改单条 db.test.updateOne({"name":"隔壁小姑娘"},{"name":"操场小妹妹"}) updateMany #修改多条数据
修改器:$set
$set | 讲某个key的value修改为某值 |
$unset | 删除字段 {$unset: {test: "" }} |
$inc | 在原值上增加 |
$push | 向数组Array最后位置插入一条元素 |
$pull | 删除数组中的某一个元素 |
$pop | 删除数组的第一个或最后一个 |
$ | 单独使用代指的是你的条件 |
db.test.update({"name":"操场小妹妹"},{"$set":{"name":"属于你的妹子"}}) 修改 修改单挑数据
db.user.updateOne({"name":"子龙"},{$set:{"name":"赵子龙"}})
修改多条数据
db.user.updateMany({"name":"小青龙"},{$set:{"name":"白虎"}})
混搭:
排序 跳过 显示条目
sort 排序 find().sort(id:-1/1) 1 升序 2 降序 desc
skip 跳过 find().skip(2) 从三条开始返回
limit 显示条目 find().limit(2)只显示前两条 混搭:
db.user.find({}).sort({age:-1 }).skip(1).limit(2)
根据age字段进行降序排列,并且跳过第一条,从第二条开始返回,两条数据
在python中上面的deleteone 都会变成delte_one 都会变成下划线之类的
res = mongo_db.user.find_one({"_id":ObjectId("5b98794ec34b9812bcabdde7")}) 查找单条的数据 res = mongo_db.content.insert_one({"name":""}) 插入单条 res = mongo_db.content.update_one({"name":""},{"$set":{"name":""}}) 修改单条 修改多条
res = mongo_db.content.update_many({"name":""},{"$set":{"name":""}})
mondoDB和python的连接:
首先要先安装pymongo 模块
pip3 install pymongo
python和mongodb数据库连接
.MongoDB的默认端口号:27017 (redis:6379 MySql:3306)
mclient = pymongo.MongoClient(host="127.0.0.1", port=27017) #端口号和地址
mongo_db = mclient[数据库] ========
client=MongoClient('mongodb://root:123@localhost:27017')
table=client['db1']['emp']