菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
227
0

MongoDB

原创
05/13 14:22
阅读数 60402

前言

我们对一些关系型数据库肯定不会陌生,比如Oracle、MySQL、SQL Server、SQLite 、PostgreSQL。

 

什么是SQL?

SQL:结构化查询语言(Structured Query Language)简称SQL,用户使用SQL操作管理数据库系统。

 

什么是NoSQL?

Not Only SQL 非关系型数据库的意思,它和关系型数据库的区别是:不需要使用SQL去查询数据库内容。

NoSQL没有Table和Filed这些概念,所以数据和数据之间也不存在任何关联关系(1对1、1对多、多对多),每条记录的列可以不一致。

NoSQL数据库的操作都是通过指令/程序语言完成的。

 

没有了SQL我们如何操作管理MongoDB?

以上我们得知MySQL使用SQL操作管理数据数据库系统。

MongoDB使用JavaScript语法、JSON数据结构来操作管理数据库。

这样以来MongoDB的查询指令和前端JavaScript的数据JSON 进行了数据类型统一 

后端程序接收到前端传来的JSON数据,无需复杂处理,直接把JSON数据当做MySQL中的SQL一样去执行就能从数据库中返回查询结果,所以MongoDB很适用于web开发

 

MongoDB和redis的区别?

MongoDB是1个使用C++编写的;1个NoSQL文档型数据库,MongoDB有点像关系型数据,它支持组合/分组聚合查询,而Redis是key,value存储。

 

MongoDB为什么比MySQL更加灵活?

关系型数据库表中的列基本固定很难扩展,而在MobgoDB中存储的文就是各种不同的json对象,所以列可以进行灵活扩展,在{}里加个key的事

 

MongoDB应用场景?

如果我们要开发的是1个新的web项目,它的功能后期会不断迭代,无法确定1个具体的数据模型不妨试试MongoDB。

我们可以使用MongoDB存储 工单信息、运维审计日志、报警信息,我认为MongoDB适用于无法确定表结构、高并发量、 读写频繁对事务要求较低的开发场景。

 

MongoDB中的概念

1.数据库(Database):相当于关系型数据存数据库                                        

2.集合(Collection):数据库中有集合(collection),相当于关系型数据库中表的概念。

3.文档(Document):文档数据库中最小单位,也就是各种各样的json。相当于关系型数据库中记录的概念。

 

 

安装MongoDB

1.配置MongoDB的yum源

vim /etc/yum.repos.d/mongodb-org-3.4.repo
#添加以下内容:
[mongodb-org-3.4]  
name=MongoDB Repository  
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/  
gpgcheck=1  
enabled=1  
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

#这里可以修改 gpgcheck=0, 省去gpg验证
[root@localhost ~]# yum makecache      

 

2.yum 安装MongoDB

yum -y install mongodb-org

 

3.配置mongoDB

mkdir -p /data/mongo
chown -R mongod /data/mongo

配置MongoDB的存储和日志路径以及监听端口

# mongod.conf
# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# Where and how to store data.
storage:
  dbPath: /data/mongo
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/mongod.pid  # location of pidfile

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0  # Listen to local interface only, comment to listen on all interfaces.


#security:

#operationProfiling:

#replication:

#sharding:

## Enterprise-Only Options

#auditLog:

#snmp:

 

操作数据库

1.查看现有数据库

> show databases;
admin  0.000GB
local  0.000GB
> show dbs;
admin  0.000GB
local  0.000GB

2.创建数据库

> use web;
switched to db web
> show dbs            //注意如果数据库中没有Collections数据库不会显示。
admin  0.000GB
local  0.000GB

3.创建集合(collection)

> db.createCollection("blogs")
{ "ok" : 1 }
> show dbs
admin  0.000GB
local  0.000GB
web    0.000GB
> show collections
blogs

4.查看当前数据库的状态

> db.stats()
{
    "db" : "web",
    "collections" : 1,
    "views" : 0,
    "objects" : 0,
    "avgObjSize" : 0,
    "dataSize" : 0,
    "storageSize" : 4096,
    "numExtents" : 0,
    "indexes" : 1,
    "indexSize" : 4096,
    "ok" : 1
}

5.删库跑路

> db.dropDatabase()
{ "dropped" : "web", "ok" : 1 }
> show dbs
admin  0.000GB
local  0.000GB
> exit
bye

 

操作集合(Collection)

//创建集合
> db.createCollection("blog") //重命名集合
> db.blog.renameCollection("blogs") //展示集合
> show collections //删除集合
> db.blogs.drop(); > show collections > db.dropDatabase();

 

操作文档

1.insert文档

> db.blogs.insert(
... {
... title:"我的第一篇博客",
... content:"已经开始写博客了好激动呀!!!"
... }
... );
WriteResult({ "nInserted" : 1 })

2.find查看所有文档

> db.blogs.find();
{ "_id" : ObjectId("60160d64999c1f14d31784e8"), "title" : "我的第1篇博客", "content" : "已经开始写博客了好激动呀!!!" }
{ "_id" : ObjectId("60160e74999c1f14d31784e9"), "title" : "我的第2篇博客,我会玩MongoDB啦!好激动呀!!!" }
{ "_id" : ObjectId("60160f03999c1f14d31784ea"), "title" : "我的第3篇博客,写点什么好呢????", "tag" : "情感" }
> 

此时我们发现MongoDB的灵活性,以上3条记录的列均不一样,而关系型数据库中所有记录的列是固定和一致的,这些约束限制了我们的数据结构

3.排序显示

升序sort({ _id:1})

> db.blogs.find({},{_id:0}).sort({_id:1})
{ "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" }
{ "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }

降序.sort({ _id:-1})

> db.blogs.find({},{_id:0}).sort({_id:-1})
{ "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
{ "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" }
> 

4.修改文档

> db.blogs.update({"tag":"linux"},{$set:{"tag":"Linux"}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blogs.find()
{ "_id" : ObjectId("601616c3a050f1d61c63303d"), "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" }
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }

5.删除文档

> db.blogs.remove({})
WriteResult({ "nRemoved" : 11 })
> db.blogs.count()
0

 

MongoDB高级查询

我们在MySQL中常用的组合查询、分组聚合查询在MongoDB中基本都可以支持。

1.根据json对象的key查询

> db.blogs.find("tag":"Linux"); 
2021-01-31T10:35:58.917+0800 E QUERY    [thread1] SyntaxError: missing ) after argument list @(shell):1:19
> db.blogs.find({"tag":"Linux"});
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
> 

2.大于/小于/等于

> db.blogs.find({"rank":{"$gte":4}});
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> db.blogs.find({"rank":{"$gt":4}});
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> db.blogs.find({"rank":{"$lte":4}});
{ "_id" : ObjectId("601616c3a050f1d61c63303d"), "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "linux" }
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
> db.blogs.find({"rank":{"$lt":4}});
{ "_id" : ObjectId("601616c3a050f1d61c63303d"), "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "linux" }
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }

 3.模糊查询

MongoDB的模糊查询支持正则表达式

> db.blogs.find({"title":/n/});
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> 

查询title包含Python的文档

> db.blogs.find({"title":/Python/});
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
> 

查询title以G开头的文档

> db.blogs.find({"title":/^G/});
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> 

4.去重复

> db.blogs.distinct("tag");
[ "linux", "Linux", "Python", "Go", "Gin" ]
> db.blogs.distinct("title");
[
    "如何杀死僵尸进行",
    "Centos7设置服务开机自启动",
    "Python引用数据类型",
    "Python调用Golang",
    "Golang的指针",
    "Golang的web框架Gin"
]

 

5.组合条件查询

之前我们经常在根据用户输入 在前端组织1个搜索条件(json字典)然后传到后端,后端程序再处理json进行SQL拼接,发送给MySQL去执行。

如果使用了MongoDB直接拿着前端传来的JSON数据进行查询即可。所以MongoDB很适合快速开发web应用。

查询title中包含P 和 rank大于等于2的博客。(And)

> db.blogs.find({title:/P/,rank:{$gte:2}});
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
> 

查询title包含O或者rank大于等于2的文档。(Or)

> db.blogs.find({$or:[{title:/O/},{rank:{$gte:2}}]});
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> 

 

6.in查询

> db.blogs.find({rank:{$in:[3,4]}});
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
> 

 

7.分组聚合查询

根据tag进行分组,查询每个tag组的文章个数

> db.blogs.aggregate([{"$group":{"_id":"$tag","数量":{"$sum":1}}}]);
{ "_id" : "Go", "数量" : 2 }
{ "_id" : "Gin", "数量" : 1 }
{ "_id" : "Python", "数量" : 1 }
{ "_id" : "Linux", "数量" : 2 }
> 

查看每个tag组rank的最小、最大、平均值

> db.blogs.find()
{ "_id" : ObjectId("601616c3a050f1d61c63303d"), "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" }
{ "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" }
{ "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{ "_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" }
{ "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" }
{ "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
> db.blogs.aggregate([{"$group":{"_id":"$tag","平均分":{"$avg":"$rank"},"最大分":{"$max":"$rank"},"最小分":{"$min":"$rank"},"总分":{"$sum":"$rank"},"数量":{"$sum":1}}}])
{ "_id" : "Go", "平均分" : 4.5, "最大分" : 5, "最小分" : 4, "总分" : 9, "数量" : 2 }
{ "_id" : "Gin", "平均分" : 6, "最大分" : 6, "最小分" : 6, "总分" : 6, "数量" : 1 }
{ "_id" : "Python", "平均分" : 3, "最大分" : 3, "最小分" : 3, "总分" : 3, "数量" : 1 }
{ "_id" : "Linux", "平均分" : 1.5, "最大分" : 2, "最小分" : 1, "总分" : 3, "数量" : 2 }
> 

 

8.分页查询

我们可以使用skip(0).limit(3).sort({_id:1})进行分页查询。

//展示全部数据
> db.blogs.find(); { "_id" : ObjectId("601616c3a050f1d61c63303d"), "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" } { "_id" : ObjectId("601616e4a050f1d61c63303e"), "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" } { "_id" : ObjectId("601616fca050f1d61c63303f"), "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }
{
"_id" : ObjectId("6016170da050f1d61c633040"), "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" } { "_id" : ObjectId("6016171da050f1d61c633041"), "title" : "Golang的指针", "rank" : 5, "tag" : "Go" } { "_id" : ObjectId("6016172da050f1d61c633042"), "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }
 

  //总条目数为6

  > db.blogs.find().count();

  6

//从0开始向后获取3条数据
> db.blogs.find({},{_id:0}).skip(0).limit(3).sort({_id:1}); { "title" : "如何杀死僵尸进行", "rank" : 1, "tag" : "Linux" } { "title" : "Centos7设置服务开机自启动", "rank" : 2, "tag" : "Linux" } { "title" : "Python引用数据类型", "rank" : 3, "tag" : "Python" }

//从3开始向后获取3条数据 > db.blogs.find({},{_id:0}).skip(3).limit(3).sort({_id:1}); { "title" : "Python调用Golang", "rank" : 4, "tag" : "Go" } { "title" : "Golang的指针", "rank" : 5, "tag" : "Go" } { "title" : "Golang的web框架Gin", "rank" : 6, "tag" : "Gin" }

 

python操作MongoDB

在日常开发中我们肯定会使用程序去操作数据库而并非命令行。在Python中我们可以使用pymongo模块。

 

连接数据库 

我们使用MySQL的前提是需要创建好数据库和用户然后进行授权。

MongoDB只需要保证MongoDB服务运行正常即可。不需要手动创建数据库和授权。

#Python操作MongoDB的API
from pymongo import MongoClient
# 创建连接
conn = MongoClient("192.168.56.18", 27017)
# 创建数据库对象(没有自动创建)
db = conn.web
# 创建集合对象(若没有自动创建)
blog_set = db.blogs

 

关闭数据连接

我们在连接完MongoDB之后,最后一定要记得关闭数据连接。

conn.close()

 

插入数据

# 插入数据到MongoDB
# insert_one插入1个文档
blog_set.insert_one({"title": "Linux启动过程", "rank": 18, "tag": "Linux"})
# save插入1个文档:可以使用_id字段,对文档进行修改。
blog_set.save(
    {"_id": 1, "title": "Linux一切皆文件思想2", "rank": 28, "tag": "Linux"}
)
# insert插入1个文档
blog_set.insert({"title": "JavaScript从入门到放弃", "rank": 10, "tag": "JavaScript"})

# 批量插入多个文档1
blog_set.insert(
    [
        {"title": "JavaScript ES6语法", "rank": 18, "tag": "JavaScript"},
        {"title": "JavaScript原型", "rank": 10, "tag": "JavaScript"},
        {"title": "JavaScript闭包", "rank": 9, "tag": "JavaScript"},
    ]
)
# insert_many批量插入多个文档2
blog_set.insert_many([
    {"title": "Golang的函数", "rank": 10, "tag": "Go"},
    {"title": "Golang面向接口编程", "rank": 5, "tag": "Go"},
    {"title": "Golang channel", "rank": 6, "tag": "Go"},
])

 

操作游标

pymongo调用find()查询方法之后会返回1个游标,我们对这个游标进行操作,可以获取到我们想要的数据。

# 获取1个文档
# print(cursor.next())
# 跳过前2个文档
cursor.skip(2)
# 限制获取文档个数
cursor.limit(2)
# 文档复合排序
cursor.sort([("_id", -1), ("rank", 1)])
# 查看文档总条目
print(cursor.count())

 

find()查询操作

find()返回1个游标对象,find()方法查询操作指令和在shell中的指令一致。

# find()查询之后会返回1个游标
cursor = blog_set.find({"rank": {"$gt": 9}}, {'_id': 0})
# 查询所有
for i in cursor:
    print(i)
    

 

find_one()查询

find_one()直接返回1个字典而不是游标对象。

# 查询rank大于10 且tag=Linux的文档
search_dict = {"$and": [{"rank": {"$gt": 10}}, {"tag": "Linux"}]}
ret = blog_set.find_one(search_dict, {"_id": 0})
print(ret)

 

aggregate()分组聚合查询

#聚合查询
ret = blog_set.aggregate([{"$group": {"_id": "$tag", "数量": {"$sum": 1}}}])
for i in ret:
    print(i)
aggregate_search = [{"$group": {"_id": "$tag", "平均分": {"$avg": "$rank"}, "最大分": {"$max": "$rank"},
                                "最小分": {"$min": "$rank"}, "总分": {"$sum": "$rank"}, "数量": {"$sum": 1}}}]
ret = blog_set.aggregate(aggregate_search)
for i in ret:
    print(i)
conn.close()

 

修改操作

upsert=True在查询不到的情况下不去更新而是新增1条新数据

multi=True可以控制修改的范围是所有

# 1.给文档扩展1个域(key)
blog_set.update({"title": "Linux一切皆文件思想2"}, {"$set": {"author": "张根"}})
# 2.如果查询不到就新增一条记录:upsert=True来控制
blog_set.update({"title": "Linux一切皆文件思想250"}, {"$set": {"author": "Tom"}}, upsert=True)
# 3.multi=True修改多个文档
blog_set.update({"title": "Linux一切皆文件思想250"}, {"$set": {"title": "Linux一切皆文件思想666"}}, multi=True)

 

删除操作

# 删除所有title=Linux一切皆文件思想666的文档
blog_set.remove({"title": "Linux一切皆文件思想666"}, multi=True)

 

 

 

 

 

 

参考

发表评论

0/200
227 点赞
0 评论
收藏