数据的存储--MongoDB文档存储

MongoDB文档存储

NoSQL,全称为Not Only SQL,意为不仅仅是SQL,泛指非关系型数据库。NoSQL是基于键值对的,而且不需要经过SQL层的解析,数据之间没有耦合性,性能非常高。

非关系行数据库又可细分如下。

  • 键值存储数据库:代表有Redis、Voldemort和Oracle BDB等。
  • 列存储数据库:代表有Cassandra、HBase和Riak等。
  • 文档型数据库:代表有CouchDB和MongoDB等。
  • 图形数据库:代表有Neo4J、InfoGrid和Infinite Graph等。

MongoDB是由C++语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容的存储形式类似JSON对象。它的字段值可以包含其他文档、数组及文档数组,非常灵活。

1. 准备工作

首先确保已经安装好了MongoDB并启动了其服务,还需要安装好Python的PyMongo库,可以使用pip3来安装:

python 复制代码
pip3 install pymongo

2.连接MongoDB

连接MongoDB时,需要使用PyMongo库里面的MongoClient方法,一般而言,传入MongoDB的IP及端口即可。MongoClient方法的第一个参数为地址host,第二个参数为端口port(如果不传入此参数,默认取值为27017):

python 复制代码
import pymongo
client = pymongo.MongoClient(host='localhost', port=27017)

这样就创建MongoDB第连接对象了。

另外,还可以直接给MongoClient的第一个参数host传入MongoDB的连接字符串,它以mongoldb开头,例如:

复制代码
client = MongoClient('mongodb://localhost:27017/')

这样可以达到同样的连接效果。

3.指定数据库

在MongoDB中,可以建立多个数据库,所以我们需要指定操作哪个数据库。这里我们以指定test数据库为例来说明:

python 复制代码
db = client.test

这里调用client的test属性即可返回test数据库。当然,也可以这样指定:

python 复制代码
db = client['test']

4.指定集合

MongoDB的每个数据库又都包含许多集合(collection),这些集合类似于关系型数据库中的表。下一步需要指定要操作哪些集合,这里指定一个集合,名称为students。与指定数据库类似,指定集合也有两种方式:

python 复制代码
collection = db.students

python 复制代码
collection = db['students']

这样我们便声明了一个集合对象。

5.插入数据

接下来,便可以插入数据了。在students这个集合中,新建一条学生数据,这条数据以字典形式表示:

python 复制代码
student = {
  'id':'20170101',
  'name': 'Jordan',
	'age': 20,
  'gender': 'male'
}

这里指定了学生的学号、姓名、年龄和性别。然后直接调用collection类的insert方法即可插入数据,代码如下:

python 复制代码
result = collection.insert_one(student)
print(result)

在MongoDB中,每条数据都有一个_id属性作为唯一标识。如果没有显式指明该属性,那么MongoDB会自动产生一个ObjectId类型的_ _id属性,insert方法会在执行后返回_id值。

运行结果如下:

shell 复制代码
6554af645035eca6670c261f

当然,也可以同时插入多条数据,只需要以列表形式传递即可,实例如下:

python 复制代码
student1 = {
    'id': '20170103',
    'name': 'Bob',
    'age': 28,
    'gender': 'male'
}

student2 = {
    'id': '20170204',
    'name': 'Mike',
    'age':32,
    'gender': 'male'
}

result = collection.insert_many([student1, student2])
print(result)
print(result.inserted_ids)

返回结果是对应的_id的集合:

shell 复制代码
[ObjectId('6554b13f43d77fb882124952'), ObjectId('6554b13f43d77fb882124953')]

6.查询

插入数据后, 我们可以利用find_one或find方法进行查询,用前者查询得到的是单个结果,后者则会返回一个生成器对象。实例代码如下:

python 复制代码
import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)

db = client.test
collection = db.students

result = collection.find_one({'name': 'Mike'})
print(result)
print(type(result))

这里我们查询name值为Mike的数据,运行结果如下:

python 复制代码
{'_id': ObjectId('65548291f1761603ce61adad'), 'id': '20170102', 'name': 'Mike', 'age': 21, 'gender': 'male'}
<class 'dict'>

可以发现,结果是字典类型,它多了_id属性,这就是MongoDB在插入数据过程中自动添加的。此外,我们也可以根据ObjectId来查询数据,此时需要使用bson库里面的objectid:

python 复制代码
from bson.objectid import ObjectId

result = collection.find_one({'_id': ObjectId('6554b13f43d77fb882124952')})
print(result)

其查询结果依然是字典类型,具体如下:

shell 复制代码
{'_id': ObjectId('6554b13f43d77fb882124952'), 'id': '20170103', 'name': 'Bob', 'age': 28, 'gender': 'male'}

当然, 如果查询结果不存在,则会返回None。

若要查询多条数据,可以使用find方法。例如,查找age为20的数据,实例代码如下:

python 复制代码
results = collection.find({'age': 20})
print(results)
for result in results:
    print(result)

运行结果如下:

shell 复制代码
<pymongo.cursor.Cursor object at 0x108fdbc10>
{'_id': ObjectId('65548291f1761603ce61adad'), 'id': '20170102', 'name': 'Mike', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('6554af645035eca6670c261f'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('6554b13f43d77fb882124952'), 'id': '20170103', 'name': 'Bob', 'age': 20, 'gender': 'male'}

返回结果是Cursor类型,相当于一个生成器,通过遍历能够获取所有的结果,其中每个结果都是字典类型。

如果要查询age大于20的数据,则写法如下:

python 复制代码
results = collection.find({'age': {'$gt':20}})
print(results[0])

这里查询条件中的键值已经不是单纯的数字了,而是一个字典,其键名为比较符号$gt,意思是大于;键值为20。

这里将比较符号归纳为表如下:

复制代码
											#### 									比较符号
符 号 含 义 实 例
$lt 小于 {'age': {'$lt': 20}}
$gt 大于 {'age': {'$gt': 20}}
$lte 小于等于 {'age': {'$lte': 20}}
$gte 大于等于 {'age': {'$gte': 20}}
$ne 不等于 {'age': {'$ne': 20}}
$in 在范围内 {'age': {'$in': [20, 23]}}
¥nin 不在范围内 {'age': {'nin': [20, 23]}}

另外,还可以进行正则匹配查询。例如,执行以下代码查询name以M开头的学生数据:

shell 复制代码
results = collection.find({'$regex': '^M.*'})

这里使用$regex来指定正则匹配,^M.*代表以M为开头的正则表达式。

下面将一些功能符号归类为下表:

功能符号
符 号 含 义 实 例 实例含义
$regex 匹配正则表达式 {'name': {'regex': '^M.*'}} name以M开头
$exists 属性是否存在 {'name': {'$exists': True}} 存在name属性
$type 类型判断 {'age': {'$type': 'int'}} age的类型为int
$mod 数字模操作 {'age': {'$mod': {5, 0}}} age模5余0
$text 文本查询 {'KaTeX parse error: Expected '}', got 'EOF' at end of input: text': {'search': 'Mike'}} text类型的属性中包含Mike字符串
$where 高级条件查询 {'$where': 'obj.fans_count == obj.follows_count'} 自身粉丝数等于关注数

7.计数

要统计查询结果包含多少条数据,可以调用count方法。例如统计所有数据条数,代码如下:

python 复制代码
count = collection.find().count()
print(count)

统计符合某个条件的数据有多少条,代码如下:

python 复制代码
count = collection.find({'age': 20}).count()
print(count)

运行结果是一个数值,即符合条件的数据条数。

8.排序

排序时,直接调用sort方法,并传入排序的字段及升降序标志即可。实例代码如下:

python 复制代码
import pymongo
# 连接数据库
client = pymongo.MongoClient(host='localhost', port=27017)
# 指定数据库
db = client.test
# 指定集合
collection = db.students
# 调用sort方法
results = collection.find().sort('name', pymongo.ASCENDING)
print([result['name'] for result in results])

运行结果如下:

shell 复制代码
['Bob', 'Jordan', 'Mike', 'Nani']

这里我们调用pymongo.ASCENDING指定按生序排序。如果要降序,可以传入pymongo.DESCENDINg。

9.偏移

在某些情况下,可能只想取某几个元素,这时可以利用skip方法偏移几个位置,例如偏移2,即可忽略前两个元素,获取第三个及以后的元素:

python 复制代码
import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)

db = client.test

collection = db.students

results = collection.find().sort('name', pymongo.ASCENDING).skip(2)
print([result['name'] for result in results])

运行结果如下:

shell 复制代码
['Mike', 'Nani']

另外,还可以使用limit方法指定要获取的结果个数,实例代码如下:

python 复制代码
results = collection.find().sort('name', pymongo.ASCENDING).skip(1).limit(3)
print([result['name'] for result in results])

运行结果如下:

shell 复制代码
['Jordan', 'Mike', 'Nani']

值得注意的是, 在数据库中数据量非常庞大的时候(例如千万、亿级别),最好不要使用大偏移量来查询数据,因为这样很可能导致内存溢出。此时可以使用类似如下操作来查询:

python 复制代码
from bson.objectid import ObjectId
collection.find({'_id': {'$gt': ObjectId('65548291f1761603ce61adad')}})

这里需要记录好上次查询的_id。

10.更新

对于数据更新,我们可以使用update_one方法,在其中指定更新的条件和更新后的数据即可。例如:

python 复制代码
import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)

db = client.test

collection = db.students

condition = {'name': 'Jordan'}
student = collection.find_one(condition)
student['age'] = 25
result = collection.update_one(condition, {'$set': student})
print(result)
print(result.matched_count, result.modified_count)

这里调用的是update_one方法,其第二个参数不能再直接传入修改后的字典,而是需要使用{'$set': student}这种形式的数据。然后分别调用matched_count和modified_count属性,可以获得匹配的数据条数和影响的数据条数。

运行结果如下:

shell 复制代码
<pymongo.results.UpdateResult object at 0x10ae5bbe0>
1 0

可以发现update_one 方法的返回结果是UpdateResult类型。我们再看一个例子:

python 复制代码
condition = {'age': {'$gt': 20}}
result = collection.update_one(condition, {'$inc': {'age': 1}})
print(result)
print(result.matched_count, result.modified_

这里查询条件为age大于20,然后更新条件是{'$inc': {'age': 1}}, 也就是对age加1,因此执行update_one方法之后,会对第一条符合查询条件的学生数据的age加1。

运行结果如下:

shell 复制代码
<pymongo.results.UpdateResult object at 0x10aa59c70>
1 1

可以看到匹配条数为1条,影响条数也是1条。

但如果调用update_many方法,则会更新所有符合条件的数据,实例代码如下:

python 复制代码
condition = {'age': {'$gt': 20}}
result = collection.update_many(condition, {'$inc': {'age': 1}})
print(result)
print(result.matched_count, result.modified_count)

运行结果如下:

shell 复制代码
<pymongo.results.UpdateResult object at 0x10dfd9c70>
2 2
11.删除

删除操作比较简单,直接调用delete_one方法和delete_many。delete_one删除第一条符合条件的数据,delete_many删除所有符合条件的数据均会被删除。实例代码如下:

python 复制代码
import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)

db = client.test

collection = db.students

result = collection.delete_one({'name': '赵玲薇'})
print(result)

删除多条数符合条件的数据,实例代码如下:

python 复制代码
result = collection.delete_many({'age': {'$gt': 19}})
print(result.deleted_count)

运行结果如下:

shell 复制代码
3

调用delete_count属性获取删除的数据条数。

12.其他操作

除了以后操作,PyMongo还提供了一些组合方法,例如find_one_and_delete、find_one_and_replace和find_one_and_update,分别是查找后删除、替换和更新操作,用法与上述方法基本一致。

相关推荐
似水流年流不尽思念19 分钟前
MongoDB 有哪些索引?适用场景?
后端·mongodb
数据智能老司机1 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机1 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机1 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i1 小时前
drf初步梳理
python·django
每日AI新事件1 小时前
python的异步函数
python
这里有鱼汤2 小时前
miniQMT下载历史行情数据太慢怎么办?一招提速10倍!
前端·python
databook11 小时前
Manim实现脉冲闪烁特效
后端·python·动效
程序设计实验室12 小时前
2025年了,在 Django 之外,Python Web 框架还能怎么选?
python
倔强青铜三13 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试