Python 第三方模块之 PyMongo - Python 操作 MongoDB

介绍

MongoDB是一种面向文档型的非关系型数据库(NoSQL),由C++编写。非关系数据库中是以键值对存储,结构不固定,易存储,减少时间和空间的开销。文档型数据库通常是以JSON或XML格式存储数据,而Mongodb使用的数据结构是BSON(二进制JSON),和JSON相比,BSON提高了存储和扫描效率,但空间占用会更多一些。

在python中操作MongoDB,我们使用PyMongo

PyMongo的使用

1.安装

1
pip3 install pymongo

2.连接

  • 第1种方式:
1
2
3
4
5
6
7
8
9
# 无密码连接
import pymongo
mongo_client = pymongo.MongoClient('127.0.0.1', 27017)

# 有密码连接
import pymongo
mongo_client = pymongo.MongoClient('127.0.0.1', 27017)
mongo_db = mongo_client.库名 # 或 mongo_client['库名']
mongo_db.authenticate('用户名', '密码')
  • 第2种方式:
1
2
3
4
5
6
7
8
9
10
# 无密码连接
import pymongo
mongo_client = pymongo.MongoClient('mongodb://127.0.0.1:27017')

# 有密码连接
import pymongo
import urllib.parse
mongo_username = urllib.parse.quote_plus('你的用户名')
mongo_password = urllib.parse.quote_plus('密码')
mongo_client = pymongo.MongoClient('mongodb://%s:%s@127.0.0.1:26666' % (mongo_username, mongo_password))

判断是否连接成功:

1
print(mongo_client.server_info()) # 判断是否连接成功

3.获取Database 和 Collection

若没有Database 和Collection,则会自动创建

第一种方式:

1
2
mongo_db = mongo_client['你的database']
mongo_collection = mongo_db['你的collection']

第二种方式:

1
2
mongo_db = mongo_client.你的database
mongo_collection = mongo_db.你的collection

CURD操作

插入

在PyMongo 3.x版本中,官方已经不推荐使用insert()方法了。当然,继续使用也没有什么问题。官方推荐使用insert_one()和insert_many()方法来分别插入单条记录和多条记录,示例如下

插入单条数据 insert_one()

1
2
3
4
5
6
7
8
import datetime
info = {
'name' : 'Zarten',
'text' : 'Inserting a Document',
'tags' : ['a', 'b', 'c'],
'date' : datetime.datetime.now()
}
mongo_collection.insert_one(info)

在MongoDB中,每条数据其实都有一个_id属性来唯一标识。如果没有显式指明该属性,MongoDB会自动产生一个ObjectId类型的_id属性。

  • insert()方法会在执行后返回_id值
  • insert_one()返回的是InsertOneResult对象,我们可以调用其inserted_id属性获取_id

插入多条数据 insert_many()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import datetime
info_1 = {
'name' : 'Zarten_1',
'text' : 'Inserting a Document',
'tags' : ['a', 'b', 'c'],
'date' : datetime.datetime.now()
}

info_2 = {
'name' : 'Zarten_2',
'text' : 'Inserting a Document',
'tags' : [1, 2, 3],
'date' : datetime.datetime.now()
}

insert_list = [info_1, info_2]
mongo_collection.insert_many(insert_list)

该方法返回的类型是InsertManyResult,调用inserted_ids属性可以获取插入数据的_id列表。

插入字符串类型的时间

由上图可以看到插入字符串时间时,mongodb自动转成了 ISOdate类型,若需要时间在mongdb也是字符串类型,只需这样操作即可

1
datetime.datetime.now().isoformat()

删除

删除一条数据 delete_one()

删除一条数据。若删除条件相同匹配到多条数据,默认删除第一条

1
mongo_collection.delete_one({'text' : 'a'})

删除多条数据 delete_many()

删除满足条件的所有数据

1
mongo_collection.delete_many({'text' : 'a'})

更新

更新单条数据 update_one()

只会更新满足条件的第一条数据

update_one(filter,update,upsert=False,bypass_document_validation=False,collation=None,array_filters=None,session=None)

  • 第一个参数 filter:更新的条件
  • 第二个参数 update : 更新的内容,必须用$操作符
  • 第三个参数 upsert : 默认False。若为True,更新条件没找到,则插入更新的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
info = {
'name': '桃子 ',
'text': 'peach',
'tags': [1, 2, 3],
'date': datetime.datetime.now()

}
update_condition = {'name' : 'Zarten_2'} # 更新的条件,也可以为多个条件
# 更新条件多个时,需要同时满足时才会更新
# update_condition = {'name' : 'Pear',
# 'text' : '梨子'}

mongo_collection.update_one(update_condition, {'$set' : info})

更新多条数据 update_many()

更新满足条件的所有数据

1
2
3
4
5
6
7
8
9
10
11
12
13
info = {
'name': 'Zarten',
'text': 'a',
'tags': [1, 2, 3],
'date': datetime.datetime.now()

}
update_condition = {'text' : 'a'} #更新的条件
#更新条件多个时,需要同时满足时才会更新
# update_condition = {'name' : 'Pear',
# 'text' : '梨子'}

mongo_collection.update_many(update_condition, {'$set' : info})

更新时,若无满足条件,则插入数据**

通过设置upsert为True即可

1
2
3
4
5
6
7
8
9
10
11
12
info = {
'name': 'Banana',
'text': '香蕉',
'tags': [1, 2, 3],
'date': datetime.datetime.now()
}
update_condition = {'text' : 'a'} #更新的条件
#更新条件多个时,需要同时满足时才会更新
# update_condition = {'name' : 'Pear',
# 'text' : '梨子'}

mongo_collection.update_many(update_condition, {'$set' : info}, upsert= True)

查询

查询一条数据 find_one()

匹配第一条满足的条件的结果,这条结果以dict字典形式返回,若没有查询到,则返回None

1
2
3
4
5
find_condition = {
'name' : 'Banana',
'text' : 'peach'
}
find_result = mongo_collection.find_one(find_condition)

可以通过projection参数来指定需要查询的字段,包括是否显示 _id

1
2
3
4
5
find_condition = {
'name' : 'Zarten_3',
}
select_item = mongo_collection.find_one(find_condition, projection= {'_id':False, 'name':True, 'num':True})
print(select_item)

查询范围

范围查询通常用$ 例如:$gte 大于等于 $lt 小于;

符号 含义 示例
$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]}}

例如:查询一段时间内的数据

1
2
3
4
5
6
import datetime
find_condition = {
'date' : {'$gte':datetime.datetime(2018,12,1), '$lt':datetime.datetime(2018,12,3)}
}
select_item = mongo_collection.find_one(find_condition)
print(select_item)

另外,还可以进行正则匹配查询。例如,查询名字以M开头的学生数据,示例如下:

1
results = collection.find({'name': {'$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]}} | 年龄模5余0 |
| $text | 文本查询 | {'$text': {'$search': 'Mike'}} | text类型的属性中包含Mike字符串 |
| $where | 高级条件查询 | {'$where': 'obj.fans_count == obj.follows_count'} | 自身粉丝数等于关注数 |

关于这些操作的更详细用法,可以在MongoDB官方文档找到:https://docs.mongodb.com/manual/reference/operator/query/。

查询多条数据 find()

返回满足条件的所有结果,返回类型为 Cursor,通过迭代获取每个查询结果,每个结果类型为dict字典

1
2
3
4
5
6
7
find_condition = {
'name' : 'Banana',
'text' : '香蕉'
}
find_result_cursor = mongo_collection.find(find_condition)
for find_result in find_result_cursor:
print(find_result)

通过 _id 来查询

查询条件中_id 类型是ObjectId类型,也就是插入时返回的对象。

若 _id 提供的是str类型的,我们需要转成ObjectId类型

1
2
3
4
5
6
7
from bson.objectid import ObjectId
query_id_str = '5c00f60b20b531196c02d657'
find_condition = {
'_id' : ObjectId(query_id_str),
}
find_result = mongo_collection.find_one(find_condition)
print(find_result)

查询一条数据同时删除 find_one_and_delete()

find_one_and_delete(filter,projection=None,sort=None,session=None,**kwargs) 详细说明

  • filter:查询条件
  • projection:选择返回和不返回的字段
  • sort:list类型,当查询匹配到多条数据时,根据某个条件排序,函数返回时返回第一条数据

只能返回一条数据

此函数的特别之处在于,它会返回被删除的信息,以字典dict形式返回

查询并删除,匹配单条数据

1
2
3
4
5
find_condition = {
'name' : 'Banana',
}
deleted_item = mongo_collection.find_one_and_delete(find_condition)
print(deleted_item)

查询并删除,匹配多条数据,有选择的返回某条数据

通过sort参数

1
2
3
4
5
find_condition = {
'name' : 'Zarten_2',
}
deleted_item = mongo_collection.find_one_and_delete(find_condition, sort= [('num', pymongo.DESCENDING)])
print(deleted_item)

删除后的数据:

计数

注意:此函数在3.7版本添加,以下的版本无法使用

1
2
3
4
5
find_condition = {
'name' : 'Zarten_1'
}
select_count = mongo_collection.count_documents(find_condition)
print(select_count)

索引

创建索引 create_index()

插入数据时,已经有一个_id索引了,我们还可以自定义创建索引

参数 unique设置为True时,创建一个唯一索引,索引字段插入相同值时会自动报错。默认为False,为False时可以插入相同值

1
mongo_collection.create_index('name', unique= True)

获取索引信息

list_indexes() 和 index_information()

1
2
3
4
5
6
# list_indexs = mongo_collection.list_indexes()
# for index in list_indexs:
# print(index)

index_info = mongo_collection.index_information()
print(index_info)

索引的名称会自动 作处理,生成别名 name_1

删除索引 drop_index() 和 drop_indexes()

需要使用索引的别名,没有则抛出错误

1
2
del_index = mongo_collection.drop_index('name_1')
print(del_index)

排序

排序时,直接调用sort()方法,并在其中传入排序的字段及升降序标志即可。示例如下:

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

# 运行结果如下:['Harden', 'Jordan', 'Kevin', 'Mark', 'Mike']

这里我们调用pymongo.ASCENDING指定升序。如果要降序排列,可以传入 pymongo.DESCENDING。

偏移 limit、skip

在某些情况下,我们可能想只取某几个元素,这时可以利用skip()方法偏移几个位置,比如偏移2,就忽略前两个元素,得到第三个及以后的元素:

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

# 运行结果如下:['Kevin', 'Mark', 'Mike']

另外,还可以用limit()方法指定要取的结果个数,示例如下:

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

# 运行结果如下:['Kevin', 'Mark']

如果不使用limit()方法,原本会返回三个结果,加了限制后,会截取两个结果返回。

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

1
2
from bson.objectid import ObjectId 
collection.find({'_id': {'$gt': ObjectId('593278c815c2602678bb2b8d')}})

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

删除

删除集合 drop()

1
mongo_collection.drop()

Pymongo常用操作

  • 根据_id查询数据插入时间排序
1
col.find().sort('_id',-1) #根据插入时间降序
  • 根据_id查询某个日期插入的数据

比如查询今天插入的所有数据

1
2
3
4
5
6
7
8
import datetime
from bson.objectid import ObjectId

today_zero = datetime.datetime.strptime(datetime.datetime.now().strftime("%Y-%m-%d"), "%Y-%m-%d")
dummy_id = ObjectId.from_datetime(today_zero)
results = col.find({"_id": {"$gte": dummy_id}}).limit(10)
for result in results:
print(result)

比如查询15天前的那天日期的所有插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import datetime
from bson.objectid import ObjectId

start_day_time = datetime.datetime.today() - datetime.timedelta(15)
end_day_time = datetime.datetime.today() - datetime.timedelta(14)

start_day_zero = datetime.datetime.strptime(start_day_time.strftime("%Y-%m-%d"), "%Y-%m-%d")
end_day_zero = datetime.datetime.strptime(end_day_time.strftime("%Y-%m-%d"), "%Y-%m-%d")

start_dummy_id = ObjectId.from_datetime(start_day_zero)
end_dummy_id = ObjectId.from_datetime(end_day_zero)

results_count = col.find({"_id": {"$gte": start_dummy_id,"$lte":end_dummy_id}}).count()
print(results_count)

比如昨天插入数据

1
2
3
4
5
6
7
8
9
10
11
12
start_day_time = datetime.datetime.today() - datetime.timedelta(1)
end_day_time = datetime.datetime.today() - datetime.timedelta(0)

start_day_zero = datetime.datetime.strptime(start_day_time.strftime("%Y-%m-%d"), "%Y-%m-%d")
end_day_zero = datetime.datetime.strptime(end_day_time.strftime("%Y-%m-%d"), "%Y-%m-%d")

start_dummy_id = ObjectId.from_datetime(start_day_zero)
end_dummy_id = ObjectId.from_datetime(end_day_zero)

results_count = col.find({"_id": {"$gte": start_dummy_id,"$lte":end_dummy_id}}).count()

print(results_count)

Python 第三方模块之 PyMongo - Python 操作 MongoDB
https://flepeng.github.io/021-Python-31-Python-第三方模块-41-数据库相关-Python-第三方模块之-PyMongo-Python-操作-MongoDB/
作者
Lepeng
发布于
2021年4月27日
许可协议