2021 iThome 鐵人賽 MongoDB披荊斬棘之路
DAY11 MongoDB 深入聚合與常見問題
MongoDB 的運算子前面有提到過,那是屬於查詢用的,本篇還會再提到一些運算子,專門是給 aggregate 使用。$sort
與$limit
在昨天講完了,今天繼續…
$project
指定取出那些欄位,例如我只想知道評價最高的電影名稱,可以只顯示 name
欄位。語法如下:
1 2 3 4
| db.movie.aggregate( {"$sort" : { "rating" : -1 }}, {"$limit" : 1}, {"$project": {"name": 1}})
|
結果:
1 2 3 4 5
| { "_id" : ObjectId("6120c79d2976f517181ffefa"), "name" : "movieE" }
|
嗯…好像不如預期,多了 _id
欄位。其實這個欄位是預設都會查出來的,需要特別關閉它,語法也很簡單。
{"$project": {_id: 0, "name": 1}}
$match
基本上就是 find
指令的條件,應用在 aggregation 就是查詢符合這些條件的資料。
我們來查詢 producer 是 “companyA” 且評價大於 6 的電影。
{"$match": {"producer": "companyA", "rating": { $gte: 6 }}}
結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "_id" : ObjectId("6120c79d2976f517181ffef6"), "name" : "movieA", "language" : "en-gb", "rating" : 8.0, "totalCost" : 30000000.0, "producer" : "companyA" }
{ "_id" : ObjectId("6120c79d2976f517181ffef8"), "name" : "movieC", "language" : "zh-tw", "rating" : 6.0, "totalCost" : 25000000.0, "producer" : "companyA" }
|
$group & $sum
一般我們熟知的 group by 語法,使用指定的欄位進行分群。
$sum
也一併在這個範例使用。
假設我們要計算每間發行商的成本總和…
1 2 3 4 5 6 7 8
| db.movie.aggregate([ { "$group" : { _id: "$producer", "totalCost" : {"$sum":"$totalCost"} } } ])
|
結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "_id" : "companyB", "totalCost" : 10000000.0 }
{ "_id" : "companyA", "totalCost" : 65000000.0 }
{ "_id" : "companyC", "totalCost" : 6000000.0 }
|
欄位名稱目前是只能用預設的 _id,可以使用 project
語法來改變。
$lookup
lookup 就是關聯式資料庫 join
的概念,當然還有很多新增的功能,這個可能之後再單獨開文章去詳細解釋應用。簡單來說就是跨表(collection)查詢資料。
我們先新增 producer
collection,裏面放的是每間公司負責人的名稱。之後我們希望查詢每部電影背後出品公司的資訊,本文目的是解說功能,就不新增太多欄位了。
新增 producer 資料語法
1 2 3 4 5
| db.getCollection('producer').insertMany([ {"companyName": "companyA", "pic": "Thrall"}, {"companyName": "companyB", "pic": "Arthas"}, {"companyName": "companyC", "pic": "Jaina"}, ])
|
look up 語法
1 2 3 4 5 6 7 8 9 10
| db.movie.aggregate([ { "$lookup" : { from: "producer", localField: "producer", foreignField: "companyName", as: "companyDetail" }}, { "$project": {_id:0, language:0, producer:0}} ])
|
結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| { "name" : "movieA", "rating" : 8.0, "totalCost" : 30000000.0, "companyDetail" : [ { "_id" : ObjectId("6120d3e92976f517181ffefe"), "companyName" : "companyA", "pic" : "Thrall" } ] }
{ "name" : "movieB", "rating" : 5.0, "totalCost" : 10000000.0, "companyDetail" : [ { "_id" : ObjectId("6120d3e92976f517181ffefe"), "companyName" : "companyA", "pic" : "Thrall" } ] }
{ "name" : "movieC", "rating" : 6.0, "totalCost" : 25000000.0, "companyDetail" : [ { "_id" : ObjectId("6120d3e92976f517181ffefe"), "companyName" : "companyA", "pic" : "Thrall" } ] }
{ "name" : "movieD", "rating" : 8.0, "totalCost" : 10000000.0, "companyDetail" : [ { "_id" : ObjectId("6120d3e92976f517181ffeff"), "companyName" : "companyB", "pic" : "Arthas" } ] }
{ "name" : "movieE", "rating" : 9.0, "totalCost" : 6000000.0, "companyDetail" : [ { "_id" : ObjectId("6120d3e92976f517181fff00"), "companyName" : "companyC", "pic" : "Jaina" } ] }
|
聚合大致上就介紹到這,否則篇幅可能會佔太多,有問題再發問或私信囉!
常見聚合問題 1
- 每間公司第一部電影是什麼
- 每間公司電影總成本是多少
- 每間公司出了幾部電影
- 列出分別的電影名稱與年份
扣除效能,在商業需求上應該很常遇到這樣的問題
1 2 3 4 5 6 7 8 9 10 11
| db.movie.aggregate([ { $match: { rating: { $gte:1 }} }, { $group: { _id: '$producer', 'totalCount': {$sum:1}, 'totalCost': {$sum: '$totalCost'}, 'totalMovies': {$push: {'name':'$name'} } , 'totalMovies_withLang': {$push: {'name': '$name', 'lang': '$language'} } , } } ])
|
常見聚合問題 2 - 條件統計數量
1 2 3 4
| db.movie.aggregate([ { $match: { rating: { $gte:7 } }}, { $count: 'HigherThan 7 rating movies count' } ])
|
常見聚合問題 3 - 陣列統計
假設每部電影都有一個 tags
陣列欄位 ["Action", "Eastern", "Western", "Historical", "Fantasy", "Drama", "Horror", "Thriller", "Science"]
會隨機包含一到多個元素。
1 2 3 4
| db.movie.aggregate([ { $unwind: { path: '$tags' }}, { $group: { _id: '$tags', 'Counts': {$sum:1} } } ])
|