python 之pymongo增删查改和管道操作

pymongo的基本操作

前言

前面有了 mongodb命令操作学习 以及 pymongo单例连接池的学习,
接下来就学习pymongo 的增删查改操作,具体源码地址:https://gitee.com/allen-huang/python/blob/master/python-code/do-mongodb/test_curd.py

新增

1、新增一条记录

  • 格式:
集合.insert_one(<字典结构>)
  • 测试用例代码
def test_insert_one(self):
    """
    单条插入数据
    @return:
    """
    insert_data = {
        "uid": 1,
        "name": "0001",
        "real_name": "黄飞鸿",
        "email": "000@sina.cn",
        "age": 30,
        "sex": 1,
    }
    res = MongoPool().test.user.insert_one(insert_data)
    print(res.inserted_id)
    pass

2、新增多条记录

  • 格式:
集合.insert_one(<列表结构>)
  • 测试用例代码:
def test_insert_many(self):
    """
    批量插入数据
    @return:
    """
    user_list = [
        {"uid": 2, "name": "0002", "real_name": "张三", "email": "001@sina.cn", "age": 29, "sex": 1},
        {"uid": 3, "name": "0003", "real_name": "魏延", "email": "002@sina.cn", "age": 28, "sex": 1},
        {"uid": 4, "name": "0004", "real_name": "刘备", "email": "003@sina.cn", "age": 38, "sex": 1},
        {"uid": 5, "name": "0005", "real_name": "关羽", "email": "004@sina.cn", "age": 32, "sex": 1},
        {"uid": 6, "name": "0006", "real_name": "张飞", "email": "005@sina.cn", "age": 33, "sex": 1},
        {"uid": 7, "name": "0007", "real_name": "赵云", "email": "006@sina.cn", "age": 21, "sex": 1},
        {"uid": 8, "name": "0008", "real_name": "马超", "email": "007@sina.cn", "age": 22, "sex": 1},
        {"uid": 9, "name": "0009", "real_name": "黄忠", "email": "008@sina.cn", "age": 24, "sex": 1},
        {"uid": 10, "name": "0010", "real_name": "孙尚香", "email": "009@sina.cn", "age": 28, "sex": 2},
        {"uid": 11, "name": "0011", "real_name": "黄月英", "email": "0010@sina.cn", "age": 27, "sex": 2},
        {"uid": 12, "name": "0012", "real_name": "诸葛亮", "email": "0011@sina.cn", "age": 29, "sex": 1},
    ]
    res = MongoPool().test.user.insert_many(user_list)
    print(res.inserted_ids)
    pass

3、自定义_id 的新增

def test_insert_custom_ids(self):
    """
    批量插入数据,自定义_id
    @return: 
    """
    ids_list = [
        {"_id": 1, "name": "曹操"},
        {"_id": 2, "name": "曹丕"},
        {"_id": 3, "name": "典韦"},
        {"_id": 4, "name": "许诸"}
    ]
    res = MongoPool().test.user_ids.insert_many(ids_list)
    print(res.inserted_ids)
    pass

更新

1、更新一条记录

  • 格式:
    第三个参数 upsert=True 表示存在更新,不存在就新增
集合.update_one(
	filter=<更新条件>,
	update=<更新的数据属于字典结构>,
	upsert=True|False
)
  • 不带 upsert测试用例代码:
def test_update_one(self):
	"""
	更新一条记录
	@return: 
	"""
	# 更新条件
	condition = {"uid": 5}
	# 更新的数据
	update_data = {"email": "0004@163.com"}
	res = MongoPool().test.user.update_one(filter=condition, update={"$set": update_data})
	print(res.modified_count)
	pass
  • 带上 upsert测试用例代码:
def test_update_upsert(self):
    """
    更新一条记录,存在更新,不存在新增
    @return:
    """
    # 更新的数据
    update_data = {
        "uid": 13,
        "name": "0013",
        "real_name": "铁桥三",
        "email": "013sina.cn",
        "age": 27,
        "sex": 1,
    }
    # 更新条件
    condition = {"uid": 13}
    # 更新的选项,存在更新,不存在新增
    upsert_option = True
    res = MongoPool().test.user.update_one(filter=condition, update={"$set": update_data}, upsert=upsert_option)
    print(res)
    pass

2、更新多条记录

  • 格式:

格式和更新一条数据差不多

集合.update_many(
	filter=<更新条件>,
	update=<更新的数据属于字典结构>,
	upsert=True|False
)
  • 测试用例1:
def test_update_many(self):
    """
    更新多条记录,更新年龄大于30岁的人,武力值更新为95
    @return:
    """
    # 更新的数据
    update_data = {"$set": {"force_value": 95}}
    # 更新条件
    condition = {"age": {"$gt": 30}}
    # 更新选项,存在更新,不存在就新增
    upsert_option = True
    res = MongoPool().test.user.update_many(filter=condition, update=update_data, upsert=upsert_option)
    print(res)
    pass
  • 测试用例2:
def test_update_many_init(self):
    """
    更新多条记录,筛选出name包含000的人员,武力值更新为90,初始化登录次数
    @return:
    """
    # 更新的数据,$setOnInsert类似新增数据后进行初始化操作
    update_data = {"$set": {"force_value": 90}, "$setOnInsert": {"login_nums": 1}}
    # 更新条件
    condition = {"name": {"$regex": "^000"}}
    # 更新选项,存在更新,不存在就新增
    upsert_option = True
    res = MongoPool().test.user.update_many(filter=condition, update=update_data, upsert=upsert_option)
    print(res)
    pass

删除

删除一条记录

  • 格式:
集合.delete_one(<删除条件>)
  • 测试用例
def test_delete_one(self):
    """
    删除单条数据
    @return:
    """
    res = MongoPool().test.user.delete_one({'uid': 1})
    print(res.deleted_count)

删除多条记录

  • 格式:
集合.delete_many(<删除条件>)
  • 测试用例
def test_delete_many(self):
    """
    批量删除uid<=2的记录
    @return:
    """
    condition = {"uid": {"$lte": 2}}
    res = MongoPool().test.user.delete_many(condition)
    print(res)

查询

查询涉及到条件,运算符,正则表达式查询,排序,投影(查询部分字段)
find_one([查询条件]) 查询一条记录
find([查询条件]) 查询多条记录

条件查询

  • 单个条件
def test_find_one(self):
    """
    查询一条数据
    @return:
    """
    coll = MongoPool().test.user
    data = coll.find_one({"uid": 1})
    print(data)
    pass
  • 多个条件

是 and 条件的简写

def test_find_more_filter(self):
    """
    查询sex=1,年龄大于30岁的数据
    @return: 
    """
    coll = MongoPool().test.user
    data_list = coll.find({"sex": 1, "age": {"$gte": 30}})
    for data in data_list:
        print(data)
  • and条件

格式:集合对象.find({“$and” : [{条件1},{条件2}…]})
多个条件是放在一个列表中去的

def test_find_and(self):
    """
    and条件,查询sex=1并且年龄大于30岁的数据
    @return:
    """
    coll = MongoPool().test.user
    # 多个and条件用列表表示
    condition = [
        {"sex": 1},
        {"age": {"$gte": 30}}
    ]
    list_data = coll.find({"$and": condition})
    for data in list_data:
        pprint(data)
  • or条件

格式:集合对象.find({“$and” : [{条件1},{条件2}…]})
多个条件是放在一个列表中去的

def test_find_or(self):
    """
    或条件,查询sex=2或者年龄大于35岁的数据
    @return:
    """
    coll = MongoPool().test.user
    # 多个or条件用列表表示
    condition = [
        {"sex": 2},
        {"age": {"$gte": 35}}
    ]
    list_data = coll.find({"$or": condition})
    for data in list_data:
        pprint(data)

根据运算符查询

  • 大于
def test_find_gt(self):
    """
    查询age大于30岁的数据
    @return:
    """
    coll = MongoPool().test.user
    data_list = coll.find({"age": {"$gt": 30}})
    for data in data_list:
        print(data["uid"])
  • 大于等于
def test_find_gte(self):
    """
    查询 age 大于等于35岁的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"age": {"$gte": 35}})
    for data in data_list:
        print(data["real_name"])
  • 小于
def test_find_lt(self):
    """
    查询 age 小于30岁的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"age": {"$lt": 30}})
    for data in data_list:
        print(data)
  • 小于等于
def test_find_lte(self):
    """
    查询 age 小于等于27岁的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"age": {"$lte": 27}})
    for data in data_list:
        print(data)
  • 不等于
def test_find_ne(self):
    """
    查询sex!=1的数据
    @return:
    """
    coll = MongoPool().test.user
    data_list = coll.find({"sex": {"$ne": 1}})
    for data in data_list:
        print(data)

根据范围查找

  • 数字范围start<=num<=end
def test_find_between(self):
    """
    查询 age 在 20到25 之间的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"age": {"$gte": 20, "$lte": 25}})
    for data in data_list:
        print(data)
  • 数组范围
def test_find_range(self):
    """
    范围查找,指定 uid来查找
    @return:
    """
    data_list = MongoPool().test.user.find({"uid": {"$in": [1, 2, 4, 5]}})
    for data in data_list:
        print(data)

根据正则表达式查询

  • 类似 mysql 的 like 查找
def test_find_like(self):
    """
    查询 name 包含 000的数据,类似mysql like
    这和mongodb的shell命令行不同,mongodb 的 shell 命令行是/000/
    """
    data_list = MongoPool().test.user.find({"name": {"$regex": "000"}})
    for data in data_list:
        print(data)
  • 以某个字符串开头
def test_find_pos(self):
    """
    查找邮箱以某个字符串001开头的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"email": {"$regex": "^001"}})
    for data in data_list:
        print(data)
  • 以某个字符串结尾
def test_find_rpos(self):
    """
    查找邮箱以某个字符串163.com结尾的数据
    @return:
    """
    data_list = MongoPool().test.user.find({"email": {"$regex": "163.com$"}})
    for data in data_list:
        print(data)

投影

投影是查询若干个字段,不是所有字段的数据
注意:除了_id之外,其他字段的投影操作只能全部是1或0,不能共存,否则会报错

def test_find_projection(self):
    """
    投影查找,只返回uid,real_name,age,sex
    注意:除了_id之外,其他字段的投影操作只能全部是1或0,不能共存,否则会报错
    @return:
    """
    # 筛选条件
    condition = {"age": {"$lte": 25}}
    # 字段_id 不显示,uid,real_name,age,sex显示
    data_list = MongoPool().test.user.find(condition, {"_id": 0, "uid": 1, "real_name": 1, "age": 1, "sex": 1})
    for data in data_list:
        print(data)

排序

在MongoDB中对结果进行排序,可使用sort()方法。
该方法接受一个包含字段列表及其排序顺序的参数,要指定排序顺序,使用1和-1,1用于升序,-1用于降序。

  • 格式:

注意:对于复合排序是,哪个字段在前,谁就优先

集合.find([查询条件]).sort({field1:1},{field2:-1})
  • 单个排序
def test_find_sort(self):
    """
    查找 sex=1,按年龄降序排序
    @return:
    """
    coll = MongoPool().test.user
    data_list = coll.find({"sex": 1}).sort({"age": -1})
    for data in data_list:
        print(data)
  • 复合排序
def test_find_comp_sort(self):
    """
    复合排序,先按性别升序,再按武力值降序
    注意:排序字段的顺序,决定了排序的优先级
    @return:
    """
    coll = MongoPool().test.user
    data_list = coll.find({"age": {"$lt": 35}}).sort({"age": 1, "force_value": -1})
    for data in data_list:
        print(data)

统计数量

老版本:采用 集合.find({条件}).count()
新版本:采用 集合.count_documents({条件})

def test_find_count(self):
    """
    查找总数,条件,年龄小于35岁的人数
    @return:
    """
    coll = MongoPool().test.user
    count_num = coll.count_documents({"age": {"$lt": 35}})
    print(count_num)

分页查询

mongodb 分页是使用 skip 和 limit来进行分页
skip(N),表示跳过前N条后的结果
limit(N),表示只显示前 N条的结果
skip(N)和 limit(N)不分先后,它始终以 skip 先执行,这和管道的操作不一样

def test_find_page(self):
    """
    分页查找
    @return:
    """
    coll = MongoPool().test.user
    page_size = 10
    page_num = 1
    skip_num = (page_num - 1) * page_size
    data_list = coll.find().sort({"age": -1}).skip(skip_num).limit(page_size)
    for data in data_list:
        print(data)

管道聚合

以下的操作还是以上面的 user 集合为例:
image.png

聚合:

聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的的管道进行分组,过滤等功能,然后经过一系列的处理,输出相应的结果。

管道

在存在多个管道时,比如第一个管道的产生结果会 作为 第二个管道的输入值,后面的以此类推,下面是这个管道的原理图(网上找的)

语法格式:db.集合名词.aggregate([{管道1:{表达式1}},{管道2:{表达式2}}…])
在 aggregate中的参数,因有多个管道,所以它是以数组形式存在

常用管道说明

管道类型
$group: 将集合中的文档分组,可用于统计结果
$match: 过滤数据,只输出符合条件的结果
$project: 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
$sort: 将输入文档排序后输出
$limit: 限制聚合管道返回的文档数
$skip: 跳过指定数量的文档,并返回余下的文档
$unwind: 将数组类型的字段进行拆分
表达式
作用:处理输入文档并输出
常用表达式
$sum: 计算某个字段总和,$sum:"$字段名"$sum:1表示计数
$avg: 计算某个字段的平均值,$avg:"$字段名"
$min: 获得某个字段最小值,$min:"$字段名"
$max: 获得某个字段最大值,$max:"$字段名"
$push: 在结果文档中添加值到一个数组
$addToSet 将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入
$first: 根据资源文档的排序获取第一个文档数据
$last: 根据资源文档的排序获取最后一个文档的审计局
$group

作用:将集合中的文档分组,可用于统计结果。其中_id 表示分组的标识,其后面使用某个字段的格式为$字段
$group 注意点:
1、分组需要放在_id后面
2、对应的字典中有几个键,结果就有几个键
3、取不同字段的值需要使用$age,$ sex…这样子
4、取字典嵌套的字典中的值时,$_id.email

例子1,按性别统计人数

按男女性别进行分组,并统计出人数

在Mongodb Compass中执行结果:
image.png
python代码:

def test_aggr_group_sex(self):
    """
    $group分组示例1,分组查找,按性别分组,统计每个性别的人数
    @return:
    """
    coll = MongoPool().test.user
    groups = {
        "$group": {
            "_id": "$sex",
            "user_count": {"$sum": 1}
        }
    }
    # 这是一个游标对象,可以遍历出来
    res = coll.aggregate([groups])
    for data in res:
        pprint(data)

image.png
上面的例子类似于 sql语句:

select sex,count(*) from user group by sex
例子2,按性别统计武力值总和:

按性别统计武力值的总和
按性别统计 “_id”: “$sex”
计算武力值的和:“字段和的别名”: {“$sum”: “$force_value”}

在Compass的结果
image.png
python 代码

def test_aggr_group_total(self):
    """
    $group分组示例2,分组查找,统计每个性别的总武力值
    @return:
    """
    coll = MongoPool().test.user

    # $sum 后面跟上要求和的字段
    groups = {
        "$group": {
            "_id": "$sex",
            "total_force_value": {"$sum": "$force_value"}
        }
    }
    res = coll.aggregate([groups])
    for data in res:
        print(data)

翻译成 SQL 语句

select sex,sum(force_value) from user group by sex
例子3,按年龄统计平均武力值

统计每个年龄段,平均武力值
按年龄统计 “_id”: “$age”
计算平均值:“字段平均值的别名”: {“$avg”: “$force_value”}

在Compass 中的结果
image.png
python 代码

def test_aggr_group_avg(self):
    """
    $group分组示例3,分组查找,统计每个年龄的平均武力值
    @return:
    """
    coll = MongoPool().test.user
    groups = {
        "$group": {
            "_id": "$age",
            "avg_force_value": {"$avg": "$force_value"}
        }
    }
    res = coll.aggregate([groups])
    for data in res:
        print(data)

image.png
翻译成 SQL 语句

select age,avg(force_value) from user group by age
例子4,统计每个性别的最大武力值

统计每个性别的最大武力值

def test_aggr_group_max(self):
    """
    分组查找,统计每个性别的最大武力值
    @return:
    """
    coll = MongoPool().test.user
    groups = {
        "$group": {
            "_id": "$sex",
            "max_force_value": {"$max": "$force_value"}
        }
    }
    res = coll.aggregate([groups])
    for data in res:
        print(data)

image.png

例子5,统计不重复的姓名,武力值列表

统计每个年龄段的 不重复的姓名,武力值列表

Compass 的结果
image.png
python 代码

def test_aggr_group_addtoset(self):
    """
    分组查找,统计每个年龄的用户名字,武力值列表
    @return:
    """
    coll = MongoPool().test.user
    groups = {
        "$group": {
            "_id": "$age",
            "real_names": {"$addToSet": "$real_name"},
            "force_values": {"$addToSet": "$force_value"}
        }
    }
    res = coll.aggregate([groups])
    for data in res:
        print(data)

image.png

$match

相当于是 mysql 中的 where 条件,用于筛选出符合条件的文档。

例子1,筛选出>27,<=35岁的人员信息

Compass 的结果:
image.png
单独按姓名查询
image.png
Python的代码

def test_aggr_match1(self):
    """
    筛选出年龄大于27岁的人,小于等于35岁的人员信息
    @return:
    """
    coll = MongoPool().test.user
    matches = {
        "$match": {
            "age": {"$gt": 27, "$lte": 35}
        }
    }
    res = coll.aggregate([matches])
    for data in res:
        pprint(data)

image.png

例子2,筛选出性别为男,根据年龄段分组的武力值信息

Compass的结果
image.png
python代码:

def test_aggr_group_match(self):
    """
    筛选出性别为男,根据年龄段分组的武力值信息组成数组,可以重复的
    @return:
    """
    coll = MongoPool().test.user
    matches = {
        "$match": {
            "sex": 1
        }
    }
    groups = {
        "$group": {
            "_id": "$age",
            "force_values": {"$push": "$force_value"}
        }
    }
    # 这里是先将match的结果作为输入,再进行group操作
    res = coll.aggregate([matches, groups])
    for data in res:
        print(data)

image.png
翻译成 SQL 语句:

select age,group_concat(force_values) from user where sex=1 group by age
$project

这个就是查询的投影,查询出需要的字段

例子1,查询出年龄大于30岁的用户编号,真实姓名,武力值

compass的结果
image.png
python代码

def test_aggr_project(self):
    """
    投影查找,只返回uid,real_name,force_value
    注意:除了_id之外,其他字段的投影操作只能全部是1或0,不能共存,否则会报错
    @return:
    """
    coll = MongoPool().test.user
    # 筛选条件
    matches = {
        "$match": {
            "age": {"$gt": 30}
        }
    }
    # 需要查询的字段
    project = {
        "$project": {
            "_id": 0,
            "uid": 1,
            "real_name": 1,
            "force_value": 1
        }
    }
    res = coll.aggregate([matches, project])
    for data in res:
        print(data)

image.png
翻译成 SQL 语句

select uid,real_name,force_value from user where age > 30
sort

对管道内的结果进行排序操作

例子1:对性别为男的人,进行武力值分组,并武力值进行倒序排

compass的结果
image.png
python代码

def test_aggr_sort(self):
    """
    排序查找,按武力值降序排序
    @return:
    """
    coll = MongoPool().test.user
    matches = {
        "$match": {"sex": 1}
    }
    groups = {
        "$group": {
            "_id": "$age",
            "force_values": {"$sum": "$force_value"},
            "force_value_avg": {"$avg": "$force_value"}
        }
    }
    # 根据前面的 match,group 的结果,再进行排序操作
    sort = {
        "$sort": {"force_value_avg": -1}
    }
    res = coll.aggregate([matches, groups, sort])
    for data in res:
        print(data)

image.png

SQL语句和聚合对应表

SQL语句Mongodb 的聚合操作
SELECT$project
WHERE$match
GROUP BY$group
Having$match
ORDER BY$sort
SUM(字段名)$sum:“$字段名”
COUNT(*)$sum:1
AVG(字段名)$avg:“$字段名”
MAX(字段名)$max:“$字段名”
MIN(字段名)$min:“$字段名”
  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值