之前我们介绍过了 Aggregation pipeline 了,如果不太了解,请往前看 DAY10, DAY11 的文章。
Aggregate 可以经过一堆操作呈现出我们要的结果,那如果我们要的结果是有一种以上的呈现方式怎麽办?例如一个学校想看本校学测的学生们分析资料,一个想看按分数来分群,一个想看按班级来分群,就得准备两次查询语法,再分别记录下来。
当然不用这麽麻烦,这时候就是 facet
出场的时候了,facet 能够在一次查询内执行多个 aggregate 并回传结果,这样做的好处就是来源资料只需要查询一次。两次可能还无法看出效果,如果是十次二十次呢?输入的资料只需做一次,就能省掉额外的消耗。
我们先来看看 Facet 的 pattern:
db.artwork.aggregate( [
{
$facet: {
"output1": [ aggregate1-stage1 , aggregate1-stage2 ],
"output2": [ aggregate2-stage1 , aggregate2-stage2 ]
}
}
])
在使用上有些原生的限制:
知道使用规则後,我们就来准备范例的资料了。
db.facet.insertMany([
{ name: 'movie1', publishYear: 2020, rating: 9, cost: 500 },
{ name: 'movie2', publishYear: 1988, rating: 9, cost: 200 },
{ name: 'movie3', publishYear: 1988, rating: 6, cost: 700 },
{ name: 'movie4', publishYear: 2018, rating: 7, cost: 800 },
{ name: 'movie5', publishYear: 2018, rating: 4, cost: 600 },
{ name: 'movie6', publishYear: 2019, rating: 7, cost: 1200 },
{ name: 'movie7', publishYear: 2020, rating: 7, cost: 700 },
{ name: 'movie8', publishYear: 2019, rating: 7, cost: 600 },
{ name: 'movie9', publishYear: 1988, rating: 5, cost: 400 },
{ name: 'movie10', publishYear: 2018, rating: 7, cost: 800 },
])
在今天之前,我们想达到以下两种统计
按年份统计
有几部电影以及总成本多少?按评分统计
有几部电影以及总成本多少?我们应该是会这样写着:
db.facet.aggregate(
{ '$group':
{_id:'$publishYear', totalCount:{ $sum: 1}, totalCost: {$sum:'$cost'} }
}
)
db.facet.aggregate(
{ '$group':
{_id:'$rating', totalCount:{ $sum: 1}, totalCost: {$sum:'$cost'} }
}
)
分两次查也挺好的。谢谢大家!(被打)
使用 facet 一次查询完也是挺简单的,语法如下:
db.facet.aggregate([
{ $facet:
{
"groupedByPublishYear": [
{ $match: { publishYear : { $gte: 1 } } },
{ $group: {_id:'$publishYear', totalCount:{ $sum: 1}, totalCost: {$sum:'$cost'} } }
],
"groupedByRating": [
{ $match: {} },
{ $group: {_id:'$rating', totalCount:{ $sum: 1}, totalCost: {$sum:'$cost'} } }
]
}
}
])
结果如下:
{
[
{
groupedByPublishYear: [
{ _id: 1988, totalCount: 3, totalCost: 1300 },
{ _id: 2018, totalCount: 3, totalCost: 2200 },
{ _id: 2019, totalCount: 2, totalCost: 1800 },
{ _id: 2020, totalCount: 2, totalCost: 1200 }
],
groupedByRating: [
{ _id: 5, totalCount: 1, totalCost: 400 },
{ _id: 7, totalCount: 5, totalCost: 4100 },
{ _id: 4, totalCount: 1, totalCost: 600 },
{ _id: 9, totalCount: 2, totalCost: 700 },
{ _id: 6, totalCount: 1, totalCost: 700 }
]
}
]
}
其实我在使用上就是当作两个 aggregate 在写,先各别击破後再组合,这样也比较好 debug。
中间的 { $match: { publishYear : { $gte: 1 } } }
与 { $match: {} }
是刻意这样写的,目的只是表现不需要过滤条件时,就这样做即可。
aggregate 使用利器还有一个分桶的运算子,叫做 bucket
(以及 bucketAuto
),功能是帮你统计的栏位进行各别统计,而分桶的方式以及刻度都能够自行定义,便於呈现结果。这个东西算是能够自行订刻度的 group
,来看看它的 Pattern
{
$bucket: {
groupBy: <field>,
boundaries: [ <bound_1>, ... <bound_n>],
default: <literal>,
output: {
<output1>: { <$accumulator expression> },
<output2>: { <$accumulator expression> },
...
}
}
}
1988~2020
,我们上下界线就是 [1988, 2020]
,也可以自己定义范围 [1988, 2000, 2010, 2020]
我们使用出版年来分群,分群为 1988, 2000, 2010, 2020,剩下的就放在名为 others
类别,并在每一份统计数量以及电影名字,马上来看范例:
film> db.facet.aggregate([
... { $bucket:
..... {
....... groupBy: '$publishYear',
....... boundaries: [1988, 2000, 2010, 2020],
....... default: "others",
....... output: {
......... "totalCount": { $sum: 1 },
......... "names" : { $push: "$name" }
......... }
....... }
..... }
... ])
[
{
_id: 1988,
totalCount: 3,
names: [ 'movie2', 'movie3', 'movie9' ]
},
{
_id: 2010,
totalCount: 5,
names: [ 'movie4', 'movie5', 'movie6', 'movie8', 'movie10' ]
},
{
_id: 'others',
totalCount: 2,
names: [ 'movie1', 'movie7' ]
}
]
film>
这边要特别注意的是 movie1 与 movie7,他们的出版年是 2020,触及了设定的 boundary 上限,也就是这个功能的上下界关系是
>=
lower bound<
upper bound本系列文章会同步发表於我个人的部落格 Pie Note
>>: Day-11 其名为超级、於新电视再起的二代霸主超级任天堂
寄送会员通知信 Laravel基於SwiftMailer函式库开发了一套邮件套件, 可以支援多种服务...
缘由: 从UIUX那边总是会收到各种有趣的需求,这次收到的新需求为希望launch页面可以停止个几秒...
这个得上一篇:https://ithelp.ithome.com.tw/articles/10259...
哈罗~ 我们前几天提到, 可以利用网路监听、密码破解来取得使用权限, 今天我们要来介绍可以做远端控制...
安装套件 Visual Studio Code 上有很多方便编写程序的扩充套件,能让我们在使用上更加...