📜  使用PyMongo将MongoDB与Python集成

📅  最后修改于: 2020-08-25 09:11:42             🧑  作者: Mango

介绍

在本文中,我们将从Python的角度深入研究MongoDB作为数据存储。为此,我们将编写一个简单的脚本来展示我们可以实现的目标以及可以从中获得的任何好处。

与许多其他软件应用程序一样,Web应用程序也由数据提供动力。这些数据的组织和存储非常重要,因为它们决定了我们如何与各种应用程序交互。处理的数据类型也可能影响我们执行此过程的方式。

数据库使我们可以组织和存储这些数据,同时还可以控制我们如何存储,访问和保护信息。

NoSQL数据库

数据库有两种主要类型- 关系数据库和非关系数据库

关系数据库允许我们存储,访问和操作与数据库中另一数据相关的数据。数据以行和列的形式存储在有组织的表中,这些表具有在表之间链接信息的关系。要使用这些数据库,我们使用结构化查询语言(SQL),示例包括MySQLPostgreSQL

与关系数据库一样,非关系数据库也不以关系或表格形式存储数据。它们也被称为NoSQL数据库,因为我们不使用SQL与它们进行交互。

此外,NoSQL数据库可以分为MongoDB所属的键值存储图存储列存储文档存储

MongoDB及其使用时间

MongoDB是文档存储非关系数据库。它允许我们将数据存储在由文档组成的集合中。

在MongoDB中,文档只是类似于JSON的二进制序列化格式,称为BSON或Binary-JSON,最大大小为16兆字节。此大小限制已到位,以确保在传输期间有效地使用内存和带宽。

如果需要存储大于设置限制的文件,则MongoDB还提供GridFS规范

文档由字段值对组成,就像常规JSON数据一样。但是,此BSON格式也可以包含更多数据类型,例如Date类型和Binary Data类型。BSON被设计为轻巧,易于遍历并且在与BSON之间进行数据编码和解码时效率很高。

作为NoSQL数据存储,MongoDB使我们能够享受与关系数据库相比使用非关系数据库所带来的优势。一个优点是,它通过对数据进行分片或分区并在多台计算机上进行有效的水平扩展来提供高可伸缩性。

MongoDB还允许我们存储大量结构化,半结构化和非结构化数据,而无需维护它们之间的关系。作为开源,实现MongoDB的成本仅靠维护和专业知识就能保持在较低水平。

像任何其他解决方案一样,使用MongoDB也有缺点。第一个是它不维护存储数据之间的关系。因此,很难执行确保一致性的ACID事务

尝试支持ACID事务时,复杂性会增加。像其他NoSQL数据存储区一样,MongoDB还不如关系数据库成熟,因此很难找到专家。

MongoDB的非关系性质使其非常适合在特定情况下通过其关系副本存储数据。例如,当数据格式灵活且没有关系时,MongoDB比关系数据库更适合的情况。

使用灵活/非关系数据,与关系数据库相比,我们在存储数据时无需维护ACID属性。MongoDB还允许我们轻松地将数据扩展到新节点。

但是,尽管具有所有优势,但当我们的数据本质上是关系型时,MongoDB并不是理想的选择。例如,如果我们要存储客户记录及其订单。

在这种情况下,我们将需要一个关系数据库来维护我们数据之间的关系,这一点很重要。如果我们需要遵守ACID属性,那么也不适合使用MongoDB。

通过Mongo Shell与MongoDB进行交互

要使用MongoDB,我们将需要安装MongoDB服务器,可以从官方主页下载该服务器。对于此演示,我们将使用免费的社区服务器。

MongoDB服务器带有一个Mongo Shell,我们可以使用它通过终端与服务器进行交互。

要激活外壳,只需输入mongo您的终端即可。您会在服务器URL旁看到有关MongoDB服务器设置的信息,包括MongoDB和Mongo Shell版本。

例如,我们的服务器运行在: 

mongodb://127.0.0.1:27017

在MongoDB中,数据库用于保存包含文档的集合。通过Mongo Shell,我们可以使用以下use命令创建新数据库或切换到现有数据库: 

> use SeriesDB

此后我们执行的每个操作都将在我们的SeriesDB数据库中生效。在数据库中,我们将存储集合,这些集合类似于关系数据库中的表。

例如,出于本教程的目的,让我们向数据库添加一些系列:

> db.series.insertMany([
... { name: "Game of Thrones", year: 2012},
... { name: "House of Cards", year: 2013 },
... { name: "Suits", year: 2011}
... ])

我们受到欢迎:

{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("5e300724c013a3b1a742c3b9"),
        ObjectId("5e300724c013a3b1a742c3ba"),
        ObjectId("5e300724c013a3b1a742c3bb")
    ]
}

要获取存储在series集合中的所有文档,我们使用db.inventory.find({}),其SQL等效项是SELECT * FROM series。传递一个空查询(即{})将返回所有文档:

> db.series.find({})

{ "_id" : ObjectId("5e3006258c33209a674d1d1e"), "name" : "The Blacklist", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3b9"), "name" : "Game of Thrones", "year" : 2012 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3ba"), "name" : "House of Cards", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3bb"), "name" : "Suits", "year" : 2011 }

我们还可以使用相等条件查询数据,例如,返回2013年首播的所有电视连续剧:

> db.series.find({ year: 2013 })
{ "_id" : ObjectId("5e3006258c33209a674d1d1e"), "name" : "The Blacklist", "year" : 2013 }
{ "_id" : ObjectId("5e300724c013a3b1a742c3ba"), "name" : "House of Cards", "year" : 2013 }

SQL等效为SELECT * FROM series WHERE year=2013

MongoDB还允许我们使用来更新单个文档db.collection.UpdateOne(),或使用来执行批量更新db.collection.UpdateMany()。例如,更新发布年份Suits

> db.series.updateOne(
{ name: "Suits" },
{
    $set: { year: 2010 }
}
)
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

最后,要删除文档,Mongo Shell提供db.collection.deleteOne()db.collection.deleteMany()功能。

例如,要删除在中首映的所有系列2012,我们可以运行:

> db.series.deleteMany({ year: 2012 })
{ "acknowledged" : true, "deletedCount" : 2 }

在联机参考中找到有关MongoDB上CRUD操作的更多信息,包括更多示例,使用条件,原子性执行操作以及将SQL概念映射到MongoDB概念和术语。

将Python与MongoDB集成

MongoDB提供了使用各种编程语言(包括Python,JavaScript,Java,Go和C#等)与MongoDB数据存储进行交互的驱动程序和工具。

PyMongo是Python的官方MongoDB驱动程序,我们将使用它来创建一个简单的脚本,该脚本将用于处理存储在SeriesDB数据库中的数据。

随着Python的3.6+VIRTUALENV安装在我们的机器,让我们为我们的应用程序创建一个虚拟环境,并通过PIP安装PyMongo:

$ virtualenv --python=python3 env --no-site-packages
$ source env/bin/activate
$ pip install pymongo

使用PyMongo,我们将编写一个简单的脚本,我们可以执行该脚本以对MongoDB数据库执行不同的操作。

连接到MongoDB

首先,我们导入pymongo我们mongo_db_script.py创造连接到我们的本地运行的MongoDB实例的客户端:

import pymongo

# Create the client
client = MongoClient('localhost', 27017)

# Connect to our database
db = client['SeriesDB']

# Fetch our series collection
series_collection = db['series']

到目前为止,我们已经创建了一个客户端,该客户端连接到我们的MongoDB服务器,并使用它来获取我们的“ SeriesDB”数据库。然后,我们获取“系列”集合并将其存储在对象中。

建立文件

为了使脚本更加方便,我们将编写环绕的函数,PyMongo使我们能够轻松地操作数据。我们将使用Python字典来表示文档,并将这些字典传递给函数。首先,让我们创建一个将数据插入“系列”集合的函数:

# Imports truncated for brevity

def insert_document(collection, data):
    """ Function to insert a document into a collection and
    return the document's id.
    """
    return collection.insert_one(data).inserted_id

该函数接收一个集合和一个数据字典,并将数据插入提供的集合中。然后,该函数返回一个标识符,我们可以使用该标识符从数据库中准确查询单个对象。

我们还应该注意_id,在创建数据时,如果未提供MongoDB,则会在我们的文档中添加一个附加键。

现在,让我们尝试使用我们的函数添加一个节目:

new_show = {
    "name": "FRIENDS",
    "year": 1994
}
print(insert_document(series_collection, new_show))

输出为:

5e4465cfdcbbdc68a6df233f

当我们运行脚本时,_id新节目的便会打印在终端上,我们可以使用此标识符稍后再获取该节目。

我们可以提供一个_id值,而不是让它自动分配,这将在字典中提供:

new_show = {
    "_id": "1",
    "name": "FRIENDS",
    "year": 1994
}

而且,如果我们尝试使用现有文件存储文档,则会_id遇到类似于以下内容的错误:

DuplicateKeyError: E11000 duplicate key error index: SeriesDB.series.$id dup key: { : 1}

检索文件

要从数据库中检索文档,我们将使用find_document(),它会查询集合中的单个或多个文档。我们的函数将接收一个字典,其中包含我们要作为过滤依据的元素,以及一个可选参数,用于指定我们要一个文档还是多个文档:

# Imports and previous code truncated for brevity

def find_document(collection, elements, multiple=False):
    """ Function to retrieve single or multiple documents from a provided
    Collection using a dictionary containing a document's elements.
    """
    if multiple:
        results = collection.find(elements)
        return [r for r in results]
    else:
        return collection.find_one(elements)

现在,让我们使用此功能查找一些文档:

result = find_document(series_collection, {'name': 'FRIENDS'})
print(result)

在执行函数时,我们没有提供multiple参数,结果是一个文档:

{'_id': ObjectId('5e3031440597a8b07d2f4111'), 'name': 'FRIENDS', 'year': 1994}

multiple设置参数,其结果是我们所有的集合在具有文档列表name属性设置为FRIENDS

更新文件

我们的下一个功能,update_document()将用于更新单个特定文档。_id查找文档时,我们将使用文档的和所属的集合:

# Imports and previous code truncated for brevity

def update_document(collection, query_elements, new_values):
    """ Function to update a single document in a collection.
    """
    collection.update_one(query_elements, {'$set': new_values})

现在,让我们插入一个文档:

new_show = {
    "name": "FRIENDS",
    "year": 1995
}
id_ = insert_document(series_collection, new_show)

完成之后,让我们更新文档,我们将使用_id添加文档后的返回值来指定该文档:

update_document(series_collection, {'_id': id_}, {'name': 'F.R.I.E.N.D.S'})

最后,让我们获取它以验证是否已放置新值并打印结果:

result = find_document(series_collection, {'_id': id_})
print(result)

执行脚本时,我们可以看到文档已更新:

{'_id': ObjectId('5e30378e96729abc101e3997'), 'name': 'F.R.I.E.N.D.S', 'year': 1995}