在数据库查询中,我们经常会遇到这样的需求:
A表 (用户信息) id<主键,唯一>, name <str, 名字>
B表(物品信息) id<主键,唯一>, owner<外键,指向A.id>, price <float, 价格>
如果要查询物品的主人和物品的价格,那么sql的查询语句类似 select A.name, B.price from A,B where B.owner=A.id
很不幸,mongodb在3.2版本之前无法进行这样的join查询,只能做多次查询再把数据组合起来.截至到这篇文章,mongodb不仅仅可以使用lookup实现join查询,连事务也已经支持了.可以说,如果是新项目的话,没有什么理由不选择mongodb来作为数据库, 毕竟灵活性和扩展性不是某些将要被闭源的数据库能比的\left(a\_a\right).
我还是举例说明吧,
主表,是一个简历模型.四个副表分别是 工作履历/交易经历/自有车辆/所获容易. 为了便于理解,都只表示了最基本的部分,这里主副表的关联设计的方式是主表对应副表的字段是一个包含副表的_id的数组,当然你也可以设计成副表中有一个字段(相当于外键)指向主表的_id,但那样的话需要在副表中多做一个外键的索引,否则查询速度会很慢.
"""Resume简历模型(部分)"""
type_dict['_id'] = ObjectId
"""教育经历部分,是Education的ObjectId的list对象"""
type_dict['education_history'] = list
type_dict['vehicle'] = list # 车辆信息, Vehicle.的ObjectId对象
"""工作履历部分,是WorkHistory.的ObjectId的list对象"""
type_dict['work_history'] = list
"""获奖/荣誉证书 Honor._id的list对象"""
type_dict['honor'] = list
"""Education教育经历模型(部分)"""
type_dict['_id'] = ObjectId
"""Vehicle自有车辆模型(部分)"""
type_dict['_id'] = ObjectId
"""WorkHistory工作经历模型(部分)"""
type_dict['_id'] = ObjectId
"""Honor荣誉证书模型(部分)"""
type_dict['_id'] = ObjectId
我们现在不仅仅要查询简历(Resume)的字段信息,也要把四个主表中的信息查出来.(查询某个司机的简历,包含其对应的教育经历,自有车辆,工作履历和所获荣誉)
ses = get_collection() # 获取数据库链接
"""
使用聚合管道进行join查询的时候,要注意以下几个事项:
1. $lookup阶段,尽量使用pipeline以提高查询灵活性.
2. 空值的处理使用$ifNull,注意他接收的是一个二元数组作为参数.$ifNull相当于三元表达式.
3. let在赋值阶段,左值不需要$符号,右值需要$符号.
4. 如果要取let创建的变量,需要前面加$$和文档本身的字段区别一下.
5. 聚合管道中的$in需要一个二元数组作为参数.
6. $cond不能使用不存在的字段名做条件判断,所以如果一个字段名不是确认存在的话,请使用$ifNull替代.
"""
pipeline = [
{"$match": {"_id": resume_id}}, # resume_id是简历id
{"$lookup": {
"from": "honor_info",
"let": {"honor_ids": {"$ifNull": ["$honor", []]}}, # 注意这里的$ifNull的用法,这相当于三元表达式
"pipeline": [
{"$match": {"$expr": {"$in": ["$_id", "$$honor_ids"]}}} # 注意这里的$in的用法
],
"as": "honor_list"
}},
{"$lookup": {
"from": "work_history",
"let": {"work_ids": {"$ifNull": ["$work_history", []]}}, # 注意这里的$ifNull的用法,这相当于三元表达式
"pipeline": [
{"$match": {"$expr": {"$in": ["$_id", "$$work_ids"]}}} # 注意这里的$in的用法
],
"as": "work_list"
}},
{"$lookup": {
"from": "education",
"let": {
"education_ids": {"$ifNull": ["$education_history", []]} # 注意这里的$ifNull的用法,这相当于三元表达式
},
"pipeline": [
{"$match": {"$expr": {"$in": ["$_id", "$$education_ids"]}}} # 注意这里的$in的用法
],
"as": "education_list"
}},
{"$lookup": {
"from": "vehicle_license_info",
"let": {
"vehicle_ids": {"$ifNull": ["$vehicle", []]} # 注意这里的$ifNull的用法,这相当于三元表达式
},
"pipeline": [
{"$match": {"$expr": {"$in": ["$_id", "$$vehicle_ids"]}}} # 注意这里的$in的用法
],
"as": "vehicle_list"
}}
]
resume = ses.aggregate(pipeline=pipeline)
resume = [x for x in resume]
if len(resume) == 0:
ms = "错误的resume_id: {}".format(resume_id)
logger.exception(msg=ms)
mes['message'] = ms
else:
resume = resume[0]
resume.pop("honor", None)
resume.pop("work_history", None)
resume.pop("education_history", None)
resume.pop("vehicle", None)
mes['data'] = resume
aggregate是mongodb的核心功能之一, 虽然用起来相对复杂,但是功能也是异常强大的.大家有空多关注一下.
网友评论