Python3 操作 MongoDB
一、安装
pip3 install pymongo
二、基本使用
1. 导入模块
import pymongo
2. 建立本地链接
cl = pymongo.MongoClient('localhost', 27017)
# 或者
cl = Mongocl('mongodb://localhost:27017/')
3. 获取数据库
一个MongoDB实例可以支持多个独立的 数据库。在使用PyMongo时,您可以使用Mongocl实例上的属性样式访问来访问数据库:
# 查看当前都有那些库
cl.database_names()
# 获取数据库对象
db = cl.test_database
或者
db = cl['test_database']
4. 获取集合
一个集合是一组存储在MongoDB中的文档,并且可以被认为是大致在关系数据库中的表。
在 PyMongo 中获取集合与获取数据库的工作方式相同:
# 查看当前库中都有那些集合
db.collection_names()
# 获取集合对象
collection = db.test_collection
# 或者
collection = db['test-collection']
合法的集合名:
- 集合名不能是空字符串""。
- 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
- 集合名不能以"system."开头,这是为系统集合保留的前缀。
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
三、惰性机制
关于MongoDB中的集合(和数据库)的一个重要注意事项是它们是懒惰地创建的。
上述命令都没有在MongoDB服务器上实际执行过任何操作。集合和数据库在第一个文档被插入时创建。
四、 文档
MongoDB中的数据使用JSON样式的文档来表示(并存储)。
在PyMongo中,我们使用字典来表示 Mongo 中文档。例如,以下字典可能用于表示博客文章:
In [34]: import datetime
In [35]: post = {"author": "yangge",
"text": "云计算-NoSQL",
"tags": ["mongodb", "python", "pymongo"],
"date": datetime.datetime.now()}
请注意,文档可以包含本机Python类型(如datetime.datetime实例),这些类型 将自动转换为适当的BSON类型并从其中转换
文档特点:
- 文档中的键/值对是有序的。
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- MongoDB区分类型和大小写。
- MongoDB的文档不能有重复的键。
- 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档键命名规范:
- 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
- .和$有特别的意义,只有在特定环境下才能使用。
- 以下划线"_"开头的键是保留的(不是严格要求的)。
五、 插入文件数据
# 使用、创建了 posts 集合
In [36]: posts_col = db.posts
# 向 psts 集合中插入一个文档,并返回了 _id 对象
In [37]: post_id = posts_col.insert_one(post).inserted_id
In [38]: post_id
Out[38]: ObjectId('5a9f8eb2e138232b02854093')
In [41]: print(post_id)
5a9f8eb2e138232b02854093
In [42]: post_id.__str__()
Out[42]: '5a9f8eb2e138232b02854093'
验证
插入第一个文档后,posts集合实际上已经在服务器上创建。
我们可以通过列出数据库中的所有集合来验证这一点:
In [45]: db.collection_names(include_system_collections=False)
Out[45]: ['posts_col']
六、 使用find_one()获取单个文档
可以在MongoDB中执行的最基本的查询类型是 find_one()。
此方法返回与查询匹配的单个文档(如果没有匹配,则返回None)。
当您知道只有一个匹配的文档,或者只对第一个匹配感兴趣时,它非常有用。
这里我们使用 find_one()从posts集合中获取第一个文档:
In [47]: posts_col.find_one()
Out[47]:
{'_id': ObjectId('5a9f8a6de138232b02854092'),
'author': 'Mike',
'date': datetime.datetime(2018, 3, 7, 6, 44, 54, 672000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': 'My first blog post!'}
指定匹配查询
In [47]: posts_col.find_one(post)
Out[47]:
{'_id': ObjectId('5a9f8eb2e138232b02854093'),
'author': 'yangge',
'date': datetime.datetime(2018, 3, 7, 15, 1, 0, 355000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': '云计算-NoSQL'}
# --------------------------------------------------
In [88]: posts_col.find_one({'author': 'yangge'})
Out[88]:
{'_id': ObjectId('5a9f8eb2e138232b02854093'),
'author': 'yangge',
'date': datetime.datetime(2018, 3, 7, 15, 1, 0, 355000),
'tags': ['mongodb', 'python', 'pymongo'],
'text': '云计算-NoSQL'}
最好通过 _id_obj 查找
假如你创建了同样的数据在 Mongo 里,他会同时存在,但是 _id 不一样。
所以建议使用创建数据时返回的 _id 对象来查询。
In [93]: posts_col.insert_one({"shark": "鲨鱼"}).inserted_id
Out[93]: ObjectId('5a9f97d1e138232b02854094')
In [94]: posts_col.insert_one({"shark": "鲨鱼"})
Out[94]: <pymongo.results.InsertOneResult at 0x7f74d652e388>
In [95]: shark_id = posts_col.insert_one({"shark": "鲨鱼", "age": "18"}).inserted_id
In [96]: shark_id_obj = posts_col.insert_one({"shark": "鲨鱼"}).inserted_id
In [97]: posts_col.find_one({"_id": shark_id_obj})
Out[97]: {'_id': ObjectId('5a9f9807e138232b02854097'), 'shark': '鲨鱼'}
In [98]: posts_col.find_one({"_id": shark_id})
Out[98]: {'_id': ObjectId('5a9f97fae138232b02854096'), 'shark': '鲨鱼'}
# 注意有个空格哦
In [99]: posts_col.find_one({"shark": " 鲨鱼"})
In [100]: posts_col.find_one({"shark": "鲨鱼"})
Out[100]: {'_id': ObjectId('5a9f97d1e138232b02854094'), 'shark': '鲨鱼'}
从字符串转换为ObjectId
In [121]: from bson.objectid import ObjectId
In [122]: posts_col.find_one({'_id': ObjectId(shark_id_obj)})
Out[122]: {'_id': ObjectId('5a9f9807e138232b02854097'), 'shark': '鲨鱼'}
==在 Python2 中==
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。
关于 _id
In [110]: shark_id.generation_time.isoformat()
Out[110]: '2018-03-07T07:42:50+00:00'
七、 批量插入
为了让查询更有趣,让我们插入更多的文档。除了插入单个文档外,我们还可以通过将列表作为第一个参数传递给insert_many()来执行批量插入操作。这会将每个文档插入列表中,只向服务器发送一条命令:
简单粗暴的上官方示例
>>> 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)}]
>>> result = posts_col.insert_many(new_posts)
>>> result.inserted_ids
[ObjectId('...'), ObjectId('...')]
查询多个文档
我们使用find() 方法来获取多个文档 。
find() 返回一个 Cursor实例,它允许我们遍历所有匹配的文档。
例如,我们可以迭代posts集合中的每个文档:
In [129]: for post in posts_col.find():
...: print(post)
...:
{'_id': ObjectId('5a9f8a6de138232b02854092'), 'author': 'Mike', 'text': 'My first blog post!', 'tags': ['mongodb', 'python', 'pymongo'], 'date': datetime.datetime(2018, 3, 7, 6, 44, 54, 672000)}
{'_id': ObjectId('5a9f8eb2e138232b02854093'), 'author': 'yangge', 'text': '云计算-NoSQL', 'tags': ['mongodb', 'python', 'pymongo'], 'date': datetime.datetime(2018, 3, 7, 15, 1, 0, 355000)}
{'_id': ObjectId('5a9f97d1e138232b02854094'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f97e1e138232b02854095'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f97fae138232b02854096'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f9807e138232b02854097'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f9e48e138232b02854098'), 'author': 'Mike', 'text': 'Another post!', 'tags': ['bulk', 'insert'], 'date': datetime.datetime(2009, 11, 12, 11, 14)}
{'_id': ObjectId('5a9f9e48e138232b02854099'), 'author': 'Eliot', 'title': 'MongoDB is fun', 'text': 'and pretty easy too!', 'date': datetime.datetime(2009, 11, 10, 10, 45)}
就像我们用 find_one()所做的那样,我们可以传递一个文档给 find() 来限制返回的结果。
在这里,我们只得到那些作者是 "鲨鱼" 的文档:
In [133]: for post in posts_col.find({"shark": '鲨鱼'}):
...: print(post)
...:
{'_id': ObjectId('5a9f97d1e138232b02854094'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f97e1e138232b02854095'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f97fae138232b02854096'), 'shark': '鲨鱼'}
{'_id': ObjectId('5a9f9807e138232b02854097'), 'shark': '鲨鱼'}
计数
In [134]: posts_col.count()
Out[134]: 8
In [135]: posts_col.find({"author": "yangge"}).count()
Out[135]: 1
范围查询
MongoDB支持许多不同类型的高级查询。例如,让我们执行查询,将结果限制为比特定日期更早的帖子,还可以按作者对结果进行排序
In [136]: d = datetime.datetime(2018, 11, 12, 12)
In [137]: for post in posts_col.find({"date": {"$lt": d}}).sort("author"):
...: print(post)
...:
{'_id': ObjectId('5a9f9e48e138232b02854099'), 'author': 'Eliot', 'title': 'MongoDB is fun', 'text': 'and pretty easy too!', 'date': datetime.datetime(2009, 11, 10, 10, 45)}
{'_id': ObjectId('5a9f8a6de138232b02854092'), 'author': 'Mike', 'text': 'My first blog post!', 'tags': ['mongodb', 'python', 'pymongo'], 'date': datetime.datetime(2018, 3, 7, 6, 44, 54, 672000)}
{'_id': ObjectId('5a9f9e48e138232b02854098'), 'author': 'Mike', 'text': 'Another post!', 'tags': ['bulk', 'insert'], 'date': datetime.datetime(2009, 11, 12, 11, 14)}
{'_id': ObjectId('5a9f8eb2e138232b02854093'), 'author': 'yangge', 'text': '云计算-NoSQL', 'tags': ['mongodb', 'python', 'pymongo'], 'date': datetime.datetime(2018, 3, 7, 15, 1, 0, 355000)}
排序
db.mycollection
mycol = db.mycollection
mycol.insert_one({'name': '姜文'})
result_dic = db.mycollection.find().sort("age")
for item in result_dic:
print(item)
更新
更新数据库, ObjectId 需要引入
from bson.objectid import ObjectId
db.mycollection.update({'_id': ObjectId('59255118d92fac43dcb1999a')}, {'$set': {'name': '九筒'}})
删除
删除指定数据
db.mycollection.remove({'name':'王二麻33333'})
删除全部数据(慎用)
db.mycollection.remove()
### capped collections (扩展)
Capped collections 就是固定大小的collection。
它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 "RRD" 概念类似。
Capped collections是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能 和标准的collection不同,你必须要显式的创建一个capped collection, 指定一个collection的大小,单位是字节。collection的数据存储空间值提前分配的。
要注意的是指定的存储大小包含了数据库的头信息。
db.createCollection("mycoll", {capped:true, size:100000})
- 在capped collection中,你能添加新的对象。
- 能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
- 数据库不允许进行删除。使用drop()方法删除collection所有的行。
- 注意: 删除之后,你必须显式的重新创建这个collection。
- 在32bit机器中,capped collection最大存储为1e9( 1X109)个字节。
元数据
数据库的信息是存储在集合中。它们使用了系统的命名空间:
dbname.system.*
在MongoDB数据库中名字空间 <dbname>.system.*
是包含多种系统信息的特殊集合(Collection),如下:
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 列出所有名字空间。 |
dbname.system.indexes | 列出所有索引。 |
dbname.system.profile | 包含数据库概要(profile)信息。 |
dbname.system.users | 列出所有可访问数据库的用户。 |
dbname.local.sources | 包含复制对端(slave)的服务器信息和状态。 |
对于修改系统集合中的对象有如下限制。
-
在
{{system.indexes}}
插入数据,可以创建索引。 -
但除此之外该表信息是不可变的(特殊的
drop index
命令将自动更新相关信息)。 -
{{system.users}}
是可修改的。 -
{{system.profile}}
是可删除的。
MongoDB 数据类型
下表为MongoDB中常用的几种数据类型。
数据类型 | 描述 |
---|---|
String | 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。 |
Integer | 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。 |
Boolean | 布尔值。用于存储布尔值(真/假)。 |
Double | 双精度浮点值。用于存储浮点值。 |
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。 |
Array | 用于将数组或列表或多个值存储为一个键。 |
Timestamp | 时间戳。记录文档修改或添加的具体时间。 |
Object | 用于内嵌文档。 |
Null | 用于创建空值。 |
Symbol | 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。 |
Date | 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。 |
Object ID | 对象 ID。用于创建文档的 ID。 |
Binary Data | 二进制数据。用于存储二进制数据。 |
Code | 代码类型。用于在文档中存储 JavaScript 代码。 |
Regular expression | 正则表达式类型。用于存储正则表达式。 |