背景
有两张表,简化为下面的结构:
课程 (course): courseNo,courseName,isDelete(用来逻辑删除)
试题(exam_question): courseNos,questionNo,questionDetail,isDelete(用来逻辑删除)
课程和试题是一对多的关系
其实如果用mongo的思想,应该把这两张表合二为一,试题是课程表的一个数组字段
如果没有试题的话,就是一个空数组
我这里是 原来只有课程,后来新加的试题,而且将来可能会改成课程和试题多对多的关系,也可能试题和别的都表关联,所以就独立两张表了
问题也不大,我们有lookup可以表关联
需要实现一个功能: 查询所有的课程,按照试题的数量排序
这里的核心难点在于,课程和试题都需要做一些筛选(isDelete=false)
本来想把思考的过程记录下来,包括错误的思路,后来想了想,没啥意义.
就只记录最终的正确思路了.
思路
如果是用SQL的话,这个很容易实现,直接搞个子查询,然后count一波
本着这个思路,我以课程表为基础,开始聚合(流水线)尝试:
- match,找到未删除的课程
- lookup,课程关联试题,通过字段 课程.courseNo 试题.courseNos,但是这里不能加筛选条件,而是把试题当成了一个字段(array)放到了课程下,起了个别名,叫做questions
- 这里为了筛选,比较曲折
- (×)match,questions.isDelete=false 此路不通.因为match的主体是course,而非questions
- (√)project+filter+cond,这样可以把questions筛选成所有isDelete=false
- project,只拿出我们想要的字段
1~3步,我们拿到了{course,questions:[]},但是我只想要试题的数量,而不是所有的试题(减少网络IO)
所以使用$size,只拿出试题的数量 - sort,skip,limit
其实如果第三步和第四步能够合并到一起的话,看起来会更好一点,即:在$size的时候添加一些$cond;但是目前没找到合适的语法.
将来如果找到更好的实现方式,再做优化.
结论
这里是aggregation的语法:
[
{
'$match': {
'isDelete': false,
}
}, {
'$lookup': {
'from': 'ks_exam_question',
'localField': 'courseNo',
'foreignField': 'courseNos',
'as': 'questions'
}
}, {
'$match': {
'courseNo': {
'$in': [
'3f1f08dba2004576978c58e26a509465', '615e2afdb83b4b7085a4d2550ce633e3'
]
}
}
}, {
'$project': {
'courseNo': '$courseNo',
'courseName': '$courseName',
'questions': {
'$filter': {
'input': '$questions',
'as': 'item',
'cond': {
'$eq': [
'$$item.isDelete', false
]