我们在上一文中,介绍了关系型数据库的相关使用,本文接着来介绍另一重要的数据库类型,非关系型数据库(NoSQL)。NoSQL 是基于键值对的存储方式,不需要经过 SQL 层的解析,数据之间也没有耦合性,因此其性能非常高。常见的非关系型数据库有 MongoDB,Neo4J,HBase,Redis 等,本文使用常用的 MongoDB 来讲解非关系型数据库的使用。
MongoDB 是由 C++编写而成,是一个基于分布式文件存储的开源数据库系统,其存储形式类似于 json 对象,因此在使用时非常灵活,被广泛使用。
初始工作
Python 使用 MongoDB 数据库需要第三方库来支持,因此需要先安装 pymongo 库。
pip install pymongo
快速上手
import pymongo
# 建立连接
client = pymongo.MongoClient(host="localhost", port=27017)
# 引用数据库名称,这里假设要创建名为mydatabase的数据库
db = client['mydatabase']
# 要验证数据库是否已存在,可以列出所有数据库名称
print(client.list_database_names())
# 若要进一步操作,比如创建集合并插入数据
collection_name = 'mycollection'
data = {'name': 'example document'}
# 新增数据
db[collection_name].insert_one(data)
开始使用
非关系型数据库和关系型数据库在概念上存在一定差异,如下表:
指定使用的数据库(database)
一般在应用中会存在多个数据库,并且在操作数据的时候需要指定是哪个具体的数据库,如下:
# 下方client为建立连接后的client对象
# 使用属性的方式
db = client['mydatabase']
# 使用点模式
db = client.mydatabase
这里我们选择了“mydatabase”这个数据库,需要注意的是,如果这个数据库不存在,后续在执行数据插入的时候会自动创建该库(非常给力的一个功能)。
指定集合
在 MongoDB 的一个数据库中,可以包含多个 collection(集合, 可以类比为 MySQL 中的表),因此在操作数据的时候还需要指定集合。我们只需要使用上边获取的数据并使用属性就可以获取到指定集合。
# 下边db指获取到的库
# 属性获取
collection = db['mycollection']
# 点模式获取
collection = db.mycollection
新增数据
在获取到数据后就可以使用 insert* 方法来新增数据了。
collection.insert_one(
{'name': 'example document',
'age': 18,
'address': 'beijing',
'phone': '1234567890'})
如上图所示使用 insert_one 方法新增一条数据。
还可以使用 insert_many 来同时新增多条数据
collection.insert_many(
[{'name': 'example1', 'age': 18, 'address': 'beijing'}
,{'name': 'example2', 'age': 18, 'address': 'beijing'}])
从上边运行结果可以看出, MongoDB 在新增数据时,只需要新增即可,数据库会自动设置一个 _id 属性来表示主键。此 _id 值由 MongoDB 来维护。并且新增成功后,方法会返回一个InsertOneResult 对象,其中包含了数据 _id 值,需要注意的是,这里的_id是ObjectId对象。
数据查询
数据存入后,最重要的就是要根据合适的条件取出来,此时可以使用 find*方法来获取已经存入的数据,当然,pymongo 中提供了更多的方法来支持这些功能,如下。
find_one 查找一条结果
# 获取一条数据,其中name为"example document"
res = collection.find_one({"name": "example document"})
print(res)
# {'_id': ObjectId('65a8c06271f39c34e5e98579'), 'name': 'example document'}
print(type(res))
# <class 'dict'>
如上述代码中,我们使用 find_one 查找一条 name 为"example document"的内容,并返回一个 dict,如果没有匹配的条件则返回 None。
需要特别注意的是,如果使用数据主键 _id 查询,需要使用 ObjectId 构建 id 值,直接使用字符串查找会无法查询到结果。
# 需要使用ObjectId构建id
res = collection.find_one({"_id": ObjectId("65a8c06271f39c34e5e98579")})
find 查找多条
res = collection.find({'name': 'example document'})
print(res)
for row in res:
print(row)
# <pymongo.cursor.Cursor object at 0x000001DCE4578E90>
# {'_id': ObjectId('65a8c06271f39c34e5e98579'), 'name': 'example document'}
# {'_id': ObjectId('65a8c098a6c8916e18ee8e80'), 'name': 'example document'}
使用 find 方法将会查找出所有符合条件的数据,并封装到了 Cursor 对象中,因此需要遍历当前对象才可以获取到每条数据。MongoDB提供了非常丰富的条件查询,以下是一些常用示例:
res = collection.find({'age': {'$gt': 20}})
如上代码中, 查找 age 大于 20 的数据,需要注意的是,条件是一个对象的结构。
其他操作
计数
早先的版本中可以使用 find 返回的结果使用 count 方法来计数, 但是这个方法在后续版本中被废弃了, 现在需要使用 count_documents 来统计条件下的查询的数量。
count = collection.count_documents({})
print("count:", count) #10
排序
与关系型数据库相似的, MongoDB 也支持排序,find 结果后调用 sort,并指定排序方式即可完成结果排序
# ASCENDING = 1
# """Ascending sort order."""
# DESCENDING = -1
# """Descending sort order."""
res = collection.find()
rows = res.sort("name", pymongo.ASCENDING)
排序参数可以使用pymongo 中定义的常量,也可以直接赋值 1 或-1。
偏移
熟悉后台开发的一定使用过分页, 在 MongoDB 中也支持这样的操作, 但是需要搭配 skip 和 limit 两个方法来完成
res = collection.find()
row = res.skip(2).limit(2)
如上所示,使用 skip 跳过前两条, 使用 limit 方法获取结果中的 2 条数据, 结果中最终会返回第 3,4 条数据。不过需要注意的是,这样的查找在数据量较大的情况下需要慎用,可能会导致内存不足,一般的解决方式是记录上一次查询最后一条数据的 id,然后查找此大于此 id 值的,再进行 limit 限制。
更新数据
更新数据时,需要使用 update 方法来完成。
collection.update_one({'name': 'example document'},
{"$set": {"name": "测试数据", "age": 30}})
如上述代码中,update_one 接受两个参数, 第一个参数为查询条件, 第二个参数为需要设置的内容。
在上方数据查询中有find_one_and_update 方法也可以在完成更新的操作
clt.find_one_and_update({'name': 'example document'},
{"$set": {"name": "测试数据2", "age": 30}})
两者执行本质是一样的,都是先查询,后更新。
删除数据
删除数据时,需要使用 delete_one 方法来完成,返回DeleteResult 结果中包含了操作成功的条数,如果操作不成功则返回 None。
res = clt.delete_one({'name': 'example1'})
# DeleteResult({'n': 1, 'ok': 1.0}, acknowledged=True)
在上方数据查询中有find_one_and_delete 方法也可以完成删除操作, 不过该方法不返回任何结果
clt.find_one_and_delete({'name': 'example2'})