mongodb 数组求和问题
mongodb 中求和有两种方法可以进行数组求和,一种是分组求和,一种是 reduce 求和,但是前者在与 $unwind 联合使用的时候,会出现一些意想不到的问题,推荐使用第二种方式
假设
假设分别有如下集合:
student,学生表
1
2
3
4
5
6
7
8
9
10
11
12
13
14[
{
name: 'user1',
age: 18,
},
{
name: 'user2',
age: 19,
},
{
name: 'user3',
age: 18,
}
]achievement,成绩表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22[
{
name: 'user1',
courseName:'语文',
score: '99'
},
{
name: 'user1',
courseName:'数学',
score: '98'
},
{
name: 'user2',
courseName:'语文',
score: '100'
},
{
name: 'user2',
courseName:'数学',
score: '100'
},
]
需求
- 求学生的平均年龄
- 求每个学生的总分数
解决方法
针对第一种需求,直接一个聚合就搞定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17db.aggregate([
{
$group:{
_id:null,
studentsCount: { $sum: 1 }
studentsAge: { $sum: '$age' }
}
}
])
// 结果
[
{
studentsCount: 3,
studentsAge: 55
}
]对第二种需求,就要分步讨论了。
- achievement 集合中,每个学生都有分数录入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20db.aggregate([
{
$lookup:{
from: 'achievement',
localField: 'name',
foreignField: 'name',
as: 'achievements'
}
},
{
$unwind:'$achievements'
},
{
$group:{
_id:null,
name: '$name',
totalScore: { $sum: '$achievements.score' }
}
}
])- 像上面记录那样,
user3
的分数还没有录入
在
user3
还没有分数记录的时候,如果上面的聚合查询,由于使用了$unwind
,这就会导致一个问题,输出的成果里面,我们会发现,user3
丢失了。这是由于
$unwind
的特性导致的。官方的解释如下:1
2
3Deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.
解构数组的每个元素。输入文档的数组字段的值被数组元素替换后生成输出文档。所以,从上面可以知道,因为
$lookup
后,user3
的achievements
为空,导致解析时,就会丢失user3
,而我们想要的是,每一个 user 都有一个totalScore
,如果数组为空,就显示 0。所以,上述的分组求和不能满足我们的需求,我们可以采用
$project
来解决这个问题,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25db.aggregate([
{
$lookup:{
from: 'achievement',
localField: 'name',
foreignField: 'name',
as: 'achievements'
}
},
{
$project:{
name: 1,
totalScore:{
// 使用方法见:https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/
$reduce:{
input: '$achievements',
initialValue: 0,
in: {
$add: ["$$value", "$$this.score"],
},
}
}
}
}
])