四时宝库

程序员的知识宝库

四十九:Python操作MongoDB快速入门


本篇文档旨在介绍快速使用Python程序使用PyMongo驱动程序操作MongoDB数据库。

一、前提

1.MongoDB的服务已成功启动

在开始前, 请确保MongoDB的实例已经成功运行,为了简便起见,本人使用的是本机的MongoDB,用的是默认的localhost与端口27017。

在Ubuntu系统上启动MongoDB的实例:

# 查看MongoDB状态
$ sudo systemctl status mongodb
# 如果没有启动,可以使用下面命令启动
$ sudo systemctl start mongodb
# 设置开机启动
$ sudo systemctl enable mongodb

1.2.确保PyMongo成功安装

在开始前,请确保PyMongo已经被安装,可以在shell窗口输入下面命令安装PyMongo:

$ pip install pymongo

当你在Python shell中输入下面命令不报错时,说明pymongo已安装完好:

>>> import pymongo


二、通过PyMongo的MongoClient创建连接

使用PyMongo操作MongoDB的第一步是创建MongoClient的实例去连接运行的mongod实例:

from pymongo import MongoClient
# 下面命令用于连接默认的host和port的mongod实例
client = MongoClient()

# 也可以指定host和port
client = MongoClient(host='localhost', port=27017)

# 也可以通过MongoDB URI格式创建连接
client = MongoClient('mongodb://localhost:27017/')


三、获取数据库 (Databases)

一个MongoDB的实例支持运行多个独立的数据库。当使用PyMongo访问MongoDB时,可以在MongoClient实例上使用属性样式访问数据库:

db = client.testdb # testdb为数据库名称

如果MongoDB的数据库名通过属性风格无法访问(比如:test-db), 那么可以使用字典风格的访问方式:

db = client['test-db']


四、获取集合(Collections)

集合是存储在MongoDB中的一组文档,可以看作和关系数据库中表的等价。在PyMongo中获取集合与获取数据库的工作原理相同:

collection = db.test_collection

使用字典风格访问:

collection = db['test_collection']


关于MongoDB中集合(和数据库)的一个重要注意事项是: 它们是延迟创建的——上面的命令都没有在MongoDB服务器上实际执行任何操作。在向集合和数据库中插入第一个文档时,将创建这些集合和数据库。


五、文档(Documents)

MongoDB中的数据使用类JSON样式的文档表示(并存储)。在PyMongo中,我们使用字典来表示文档。例如,可以使用以下字典来表示博客文章:

import datetime
post = {
    'author':'Mike',
    'text': 'My first blog post! The post is mostly introducing how to use PyMongo to interact with MongoDB',
    'tag':['mongodb','python','pymongo'],
    'date': datetime.datetime.utcnow()
}

注意,文档可以包含基本Python类型(比如日期时间。日期时间实例),它将自动与相应的BSON类型进行转换。


六、插入文档

将一个文档插入到集合中,我们可以使用insert_one()方法:

In [9]: posts = db.posts                                                        

In [10]: post_id = posts.insert_one(post).inserted_id                           

In [11]: post_id                                                                
Out[11]: ObjectId('5f282900d28216c1cebfacd8')

在向集合中插入文档时如果没有指定"_id"键,会默认自动添加一个"_id"。"_id"必须在这个集合中是唯一的。

在插入第一个文档之后,posts集合实际上已经在服务器上创建了。我们可以通过列出数据库中的所有集合来验证这一点:

In [12]: db.list_collection_names()                                             
Out[12]: ['posts']


七、使用find_one()获取一个文档

MongoDB中可以执行的最基本的查询类型是find_one()。此方法返回与查询匹配的单个文档(如果没有匹配项,则返回无)。当您知道只有一个匹配的文档,或者只对第一个匹配的文档感兴趣时,它非常有用。这里我们使用find_one()从posts集合获取第一个文档:

In [13]: db.posts.find_one()                                                    
Out[13]: 
{'_id': ObjectId('5f282900d28216c1cebfacd8'),
 'author': 'Mike',
 'text': 'My first blog post! The post is mostly introducing how to use PyMongo to interact with MongoDB',
 'tag': ['mongodb', 'python', 'pymongo'],
 'date': datetime.datetime(2020, 8, 3, 15, 10, 1, 40000)}

如果我们查询一个不存在的author,比如“Eliot”,我们不会得到任何结果:

In [17]: db.posts.find_one({"author":"Eliot"})                                  

In [18]:    


八、通过ObjectId查询

可以通过"_id"查询一个post:

In [20]: import pprint                                                          

In [21]: post_id                                                                
Out[21]: ObjectId('5f282900d28216c1cebfacd8')

In [22]: pprint.pprint(posts.find_one({"_id":post_id}))                         
{'_id': ObjectId('5f282900d28216c1cebfacd8'),
 'author': 'Mike',
 'date': datetime.datetime(2020, 8, 3, 15, 10, 1, 40000),
 'tag': ['mongodb', 'python', 'pymongo'],
 'text': 'My first blog post! The post is mostly introducing how to use '
         'PyMongo to interact with MongoDB'}

请注意,ObjectId与其字符串表示形式不同:

In [23]: type(post_id)
Out[23]: bson.objectid.ObjectId
In [24]: post_id_as_str = str(post_id)

In [26]: posts.find_one({"_id":post_id_as_str}) # 没有结果返回
...:

web应用程序中的一个常见任务是从请求URL获取ObjectId并找到匹配的文档。在这种情况下,有必要在调用find_one()方法时传递字符串前将其转换成ObjectId:

In [29]: from bson.objectid import ObjectId 
    ...:  
    ...: # web应用从URL中获取post_id并以字符串形式传递 
    ...: def get(post_id): 
    ...:     # 从字符串转换成ObjectId 
    ...:     document = client.posts.find_one({'_id':ObjectId(post_id)}) 


关于Unicode字符串

您可能注意到,从服务器检索时,我们先前存储的常规Python字符串看起来不同(例如,u'Mike'而不是'Mike')。一个简短的解释是合理的。

MongoDB以BSON格式存储数据。BSON字符串是UTF-8编码的,因此PyMongo必须确保它存储的任何字符串只包含有效的UTF-8数据。常规字符串(<type'str'>)将被验证并存储为原样。Unicode字符串(<type'Unicode'>)首先编码为UTF-8。我们的示例字符串在python shell中表示为u'Mike'而不是'Mike',原因是PyMongo将每个BSON字符串解码为Python unicode字符串,而不是常规str。


九、批量插入

为了使查询更加有趣,让我们再插入一些文档。除了插入单个文档外,我们还可以执行大容量插入操作,方法是将列表作为第一个参数传递给insert_many()。这将插入列表中的每个文档,只向服务器发送一个命令:

In [29]: new_posts = [{"author": "Mike", 
    ...:               "text": "Another post!", 
    ...:               "tags": ["bulk", "insert"], 
    ...:               "date": datetime.datetime(2009, 11, 12, 11, 14)}, 
    ...:              {"author": "Eliot", 
    ...:                "title": "MongoDB is fun", 
    ...:               "text": "and pretty easy too!", 
    ...:               "date": datetime.datetime(2009, 11, 10, 10, 45)}]        
?
In [30]: result = db.posts.insert_many(new_posts)                               
?
In [31]: result.inserted_ids                                                    
Out[31]: [ObjectId('5f283257d28216c1cebfacd9'), ObjectId('5f283257d28216c1cebfacda')]
?

关于这个例子,有几个有趣的事情需要注意:

  • insert_many()的结果现在返回两个ObjectId实例,每个插入的文档一个。
  • posts[1]的结构与其它的posts不同(其它posts没有tags),我们添加了一个新字段“title”。这就是我们所说的MongoDB是无模式的。


十、查询多个文档

要获取多个文档作为查询的结果,我们使用find()方法。find()返回一个游标实例,它允许我们迭代所有匹配的文档。例如,我们可以迭代posts集合中的每个文档:

In [32]: for post in db.posts.find(): 
    ...:     pprint.pprint(post) 
    ...:      
    ...:                                                                        
{'_id': ObjectId('5f282900d28216c1cebfacd8'),
 'author': 'Mike',
 'date': datetime.datetime(2020, 8, 3, 15, 10, 1, 40000),
 'tag': ['mongodb', 'python', 'pymongo'],
 'text': 'My first blog post! The post is mostly introducing how to use '
         'PyMongo to interact with MongoDB'}
{'_id': ObjectId('5f283257d28216c1cebfacd9'),
 'author': 'Mike',
 'date': datetime.datetime(2009, 11, 12, 11, 14),
 'tags': ['bulk', 'insert'],
 'text': 'Another post!'}
{'_id': ObjectId('5f283257d28216c1cebfacda'),
 'author': 'Eliot',
 'date': datetime.datetime(2009, 11, 10, 10, 45),
 'text': 'and pretty easy too!',
 'title': 'MongoDB is fun'}


就像find_one()一样,我们可以向find()传递一个文档来限制返回的结果。这里,我们只得到那些作者是“Mike”的文档:

In [33]: for post in db.posts.find({'author':'Mike'}): 
    ...:     pprint.pprint(post) 
    ...:                                                                        
{'_id': ObjectId('5f282900d28216c1cebfacd8'),
 'author': 'Mike',
 'date': datetime.datetime(2020, 8, 3, 15, 10, 1, 40000),
 'tag': ['mongodb', 'python', 'pymongo'],
 'text': 'My first blog post! The post is mostly introducing how to use '
         'PyMongo to interact with MongoDB'}
{'_id': ObjectId('5f283257d28216c1cebfacd9'),
 'author': 'Mike',
 'date': datetime.datetime(2009, 11, 12, 11, 14),
 'tags': ['bulk', 'insert'],
 'text': 'Another post!'}


十一、文档计数

如果我们只想知道有多少文档与一个查询匹配,我们可以执行estimated_document_count()操作,而不是执行完整的查询。我们可以获得集合中所有文档的计数:

In [38]: db.posts.estimated_document_count()                                    
Out[38]: 3
    
# 或者, 注:{}不能省略

In [41]: db.posts.count_documents({})                                           
Out[41]: 3

如果是有条件的查询:

In [42]: db.posts.count_documents({'author':'Mike'})                            
Out[42]: 2

注:老版本通过db.collection.count(),现在在新版本中已过时,建议使用上面两种方法。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接