原本學SQL的時候,心想:原來這就是資料庫啊!太燒腦了吧(以前怎麼傻傻的以為Excel就夠用了)
結果後來老師說,還有NoSQL喔!那我們來下載MongoDB吧!
簡直是晴天霹靂,我的Notion筆記已經長到可以用滑鼠滾輪往下滾20下還沒結束
MongoDB...我還去查了正確發音:
真的是令我一頭霧水,特別是跟上週學的關聯式資料庫差異很大。
一開始搞不清楚哪時候該用什麼符號,整個人卡在這些神秘符號裡:
-什麼時候該用 {} 大括號?一下子大括號裡又有大括號,到底要包幾個?
-什麼時候用 [] 中括號?
-單引號''好多!
-還有$符號,到底...
-還有Add Stage?!?!pipeLine?!?!
-還有讀寫資料(甚至MP3、圖片、影片也可以)
感覺就像打開了一本魔法書,裡面全是咒語,超難懂。(應該說眼睛看懂了,腦子跟不上🥲)
接著我發現MongoDB跟Python也不太一樣,雖然文件的概念有點像Python裡的字典,但MongoDB的查詢語法還多了很多特殊符號跟操作指令。像Python的dict和list好不容易熟悉了,結果看到{ }跟[ ]在MongoDB又變成其他用法(救命)MongoDB還有$gt、$regex、$set...這種符號來下條件,燒腦了。
然後Compass的介面也需要再熟悉,偶爾也要用到shell,於是就這樣在Compass、shell、VS CODE還有我的Notion筆記之間華麗切換✨
$
符號整理MongoDB 主要用 $
符號在兩大地方:
$
運算符符號 | 用途說明 |
---|---|
$eq |
等於。例如:{field: {$eq: value}} |
$ne |
不等於 |
$gt |
大於 |
$gte |
大於等於 |
$lt |
小於 |
$lte |
小於等於 |
$in |
欄位值在指定陣列中 |
$nin |
欄位值不在指定陣列中 |
$exists |
欄位是否存在,值為 true 或 false |
$type |
欄位資料型態 |
$regex |
正規表達式查詢 |
$and |
多條件且 |
$or |
多條件或 |
$not |
非 |
$nor |
多條件皆不成立 |
$all |
陣列欄位包含所有指定值 |
$elemMatch |
陣列欄位中符合條件的元素 |
$size |
陣列欄位長度 |
符號 | 用途說明 |
---|---|
$set |
設定欄位值 |
$unset |
移除欄位 |
$inc |
欄位加減數值 |
$mul |
欄位乘法 |
$rename |
欄位重新命名 |
$min |
將欄位值更新為指定值(只有當新值小於目前值時) |
$max |
將欄位值更新為指定值(只有當新值大於目前值時) |
$push |
陣列欄位新增元素 |
$pop |
陣列欄位刪除第一或最後一個元素 |
$pull |
從陣列中刪除符合條件的元素 |
$addToSet |
將元素新增到陣列(若不存在才新增) |
$
符號符號 | 用途說明 |
---|---|
$match |
過濾文件(類似查詢) |
$project |
選擇輸出欄位、重塑文件 |
$group |
分組(類似 SQL 的 GROUP BY) |
$sort |
排序 |
$limit |
限制輸出筆數 |
$skip |
跳過指定筆數 |
$unwind |
將陣列欄位拆成多筆文件 |
$lookup |
連接另一個 collection(左外連接) |
$addFields |
新增欄位 |
$replaceRoot |
替換文件根層 |
符號 | 用途說明 |
---|---|
$sum |
求和 |
$avg |
平均值 |
$min |
最小值 |
$max |
最大值 |
$first |
分組中第一個值 |
$last |
分組中最後一個值 |
$push |
將欄位值放入陣列 |
$addToSet |
將欄位值放入陣列,避免重複 |
$cond |
條件運算式,類似三元運算子 if-then-else |
$ifNull |
欄位為 null 時使用替代值 |
$concat |
字串連接 |
$substr |
字串截取 |
$toInt / $toString |
型態轉換 |
$literal |
字面值,不當作運算符處理 |
$dateToString |
日期格式化 |
MongoDB 的 pipeline 就是一串「資料處理步驟」,資料會像流水線一樣,從第一個步驟開始,經過一個接一個的處理,最後吐出你想要的結果。
每個步驟都叫做一個 stage(階段),stage 用 $ 開頭像 $match、$group、$sort 等等。每個階段都會對「文件(document)」做操作,比如:
篩選($match)
分組($group)
排序($sort)
拆解陣列($unwind)
然後下一個階段會拿前面處理完的結果繼續處理。(所以stage如果用不到就要刪掉,不然會影響到結果!)
譬如,我想買咖啡,流程是:
挑豆子(篩選出好豆) → $match
把豆子磨粉 → $project
分批包裝(分組統計) → $group
排隊出貨 → $sort
資料按照一個個stage過關,最後變成我們想要的「乾淨結果」。
一次串接多個操作,不用一個步驟一個步驟慢慢跑,可以做統計、轉換、展開陣列等比較複雜的分析!
-查詢、過濾、更新常用 $ 運算符
-聚合管線階段跟表達式都用 $ 開頭
-陣列操作符特別多,推陣列、拉陣列用 $push、$pull 等
為了避免搞混,我試了這幾招:
把 Python 當作「程式語言」,MongoDB 查詢語法當成「資料庫的查詢語言」。
兩者用處不同,並不是完全使用 Python 思維寫 MongoDB 查詢。
把 MongoDB 文件想像成「Python 裡的 dict,但更像一份資料清單」。
把 $gt 等符號想成資料庫的關鍵字,而不是 Python 裡的函式。
把理論放一邊,直接用 pymongo 嘗試增刪查改。
遇到不懂的指令,查官方文件、看範例,邊改邊跑。
這種思維切換一開始一定會不順,給自己時間慢慢適應。
想到小時候連注音符號都認不得時,卻會背弟子規,之後就慢慢能理解意涵了
操作 | Python 語法 (dict/list) | MongoDB 查詢語法(JSON風格) | 說明 |
---|---|---|---|
定義一筆資料 | person = {"name": "厄卡倫", "age": 35, "hobbies": ["寫程式", "看動畫"]} |
{ "name": "厄卡倫", "age": 35, "hobbies": ["寫程式", "看動畫"] } |
Python dict 和 MongoDB document 基本相同結構 |
找出年齡大於30的人 | (Python自己寫過濾邏輯) | db.collection.find({ "age": { "$gt": 30 } }) |
$gt = greater than,大於符號 |
找出名字是寺仁的人 | (Python自己寫過濾邏輯) | db.collection.find({ "name": "寺仁" }) |
直接比對欄位值 |
新增一筆資料 | people.append({"name": "小桃", "age": 25}) |
db.collection.insertOne({ "name": "小桃", "age": 25 }) |
MongoDB用 insertOne() 插入文件 |
更新符合條件的資料 | (Python手動找資料改值) | db.collection.updateOne({ "name": "小桃" }, { "$set": { "age": 26 } }) |
$set 更新欄位值 |
刪除資料 | (Python用 list.remove()) | db.collection.deleteOne({ "name": "小桃" }) |
刪除符合條件的第一筆文件 |
使用陣列 | hobbies = ["寫程式", "看動畫"] |
文件裡的陣列同樣用[] ,如 "hobbies": ["寫程式", "看動畫"] |
MongoDB 文件可直接存陣列 |
查詢陣列中包含指定值的文件 | (Python自己判斷list元素) | db.collection.find({ "hobbies": "寫程式" }) |
找出陣列欄位中含指定元素 |
多條件查詢 | (Python自己寫複雜判斷) | db.collection.find({ "age": { "$gt": 30 }, "name": "厄卡倫" }) |
多個欄位條件同時符合 |