筆者背景
MongoDB聚合查詢容我以下將MongoDB的Collection說成資料表,以下是我整理MongoDB和SQL的相似之處:
解題過程(文字範例描述)
本職是後端工程師,在公司主要寫golang搭gin框架開發API+資料庫使用MongoDB,
目前有購買ChatGPT Plus(20 USD/每月)的服務至少連續3個月,過去使用免費版的時間也有一年以上,
所以想分享也記錄一些自己在工作中使用ChatGPT想法
資料庫 | MongoDB | SQL |
多表查詢 | Aggregate with Pipelines | Select .. Join on ... |
查詢語法類型 | NoSQL | SQL |
資料表(資料集合) | Collection | Table |
因為MongoDB官方在推薦建立資料表設計也都推薦若常常會一起查詢出的資料,那就放在同一個資料表增加查詢效率,但若關聯差很多的還是會建議拆成多個資料表查詢,一樣也有跟SQL類似的外來鍵join查詢方式。
使用Aggregate(聚合查詢)在多表查詢的部分,
邏輯跟過去以往的關聯式資料庫SQL語法的join不同,主要是以pipeline為核心去進行多表查詢串接。
邏輯跟過去以往的關聯式資料庫SQL語法的join不同,主要是以pipeline為核心去進行多表查詢串接。
舉個例子,假如你需要查詢『指定單一特定群組所有帳號的資訊,回應包含群組名稱及群組底下每個帳號的資訊(包含帳號名稱以及擁有哪些優惠券)』,可以看出會有3個資料表"群組(group)", "帳號(account)","優惠券(coupon)",
這邊加個限制好釐清這些資料表之間的關聯:
1. 一個群組底下能有多個帳號,但一個帳號只會屬於一個群組
2. 每張優惠券只能分配給單一帳號,一個帳號可能會有0~N張優惠券
1. 一個群組底下能有多個帳號,但一個帳號只會屬於一個群組
2. 每張優惠券只能分配給單一帳號,一個帳號可能會有0~N張優惠券
我會以群組(group為主表)進行Aggreagte查詢,然後宣告一個Pipeline:
先用$match過濾出指定group(一個群組),
使用$lookup去查出該群組下每個account,此$lookup{ }本身內部還能再添加一個Pipeline欄位,
在此內部Pipeline 指向一個新的Pipeline在去join對應的coupon,這樣查詢條件就寫完了。
先用$match過濾出指定group(一個群組),
使用$lookup去查出該群組下每個account,此$lookup{ }本身內部還能再添加一個Pipeline欄位,
在此內部Pipeline 指向一個新的Pipeline在去join對應的coupon,這樣查詢條件就寫完了。
原本故事到這邊應該就結束,然而資料本身並不單純,原來群組(group)紀載了允許的優惠券設定,比方說群組使用一個巢狀欄位allowed.couponCategory(資料格式為字串陣列)紀載著該群組能使用的優惠券,所以在原本的查詢上你是直接查出每個帳號所有的coupon就好,現在變成查出的Coupon必須要用外部欄位couponCategory。
這時候我就開始用ChatGPT,貼上我的程式碼將上述的描述清楚跟他說,他回應的內容包含了$let用法,
甚至貼上他認為Pipeline的程式碼,複製貼上執行卻回傳個錯誤。
Invalid $addFields :: caused by :: Use of undefined variable: externalCategory
再次詢問ChatGPT後,從回應中也得知Lookup內部Pipeline無法直接取得外層的Pipeline資料,如果要得到外部資訊只能透過在Lookup內使用$let宣告全域變數externalCategory並設值等同外部欄位allowed.couponCategory,然而道理很正確但問題是回答的程式碼依舊無法正常運作,因為問了太多次甚至回應我只是同義的語法,但依然會導致相同的錯誤。
耗了兩三小時,其中離開座位時想到,會不會即使是Lookup內$let也不能取得那個巢狀欄位,只是設置變數沒取得也不會報錯,後面我向ChatGPT確認我的想法,在Lookup之前我使用最基礎的$Project將那個巢狀欄位allowed欄位內的欄位couponCategory,直接變成最外層的欄位就叫rootCategory
ex:
ex:
Project(bson.M{
"rootCategory": "allowed.couponCategory",
})
接著原本的內部的Pipeline也不需要宣告let,
反而在外部最後一個Project取得結果使用 $$root.rootCategory 去取得,而這方法也不是全域變數。
ex:
結論
Project(bson.M{
"groupName": 1,
"rootCategoy": 1, // 這邊A
"accounts": bson.M{
"$map": bson.M{
"input": "$accounts",
"as": "account",
"in": bson.M{
"_id": "$$account._id",
"coupons": "$$account.coupons",
"rootCategoy": "$$ROOT.rootCategory", //這跟外部的rootCategory是一樣的資料
},
},
},
})
結果可以發現要在內部取得最外層欄位只要用$$root就能取得。
前面被ChatGPT提示的$let根本沒用,但我也從中學習一些語法。
避免洩漏公司機密,所以以上例子實際是我自己重新想過但概念相通的範例,
所以需求跟資料結構都怪怪的,還請多擔待。
結論所以需求跟資料結構都怪怪的,還請多擔待。
明明標題寫得好像是使用ChatGPT直接取得結果,但最後答案仍然是人想出來的,
感覺用起來不是很聰明(指我自己)
感覺用起來不是很聰明(指我自己)

絕大多數開發卡關時我都是先AI(ChatGPT...)產生程式碼,接著確認程式碼沒問題就去跑,
但在一些需求複雜或語法陌生的情況,我就會不斷重複以下三步驟:
1. 將自己對問題的理解和需求丟給AI,嘗試取得答案
2. 嘗試理解答案(Google無法理解的部分,若連搜尋結果無法理解那就問AI)
3. 寫測試驗證答案(不知道怎麼寫,請AI幫你產)
在這種生成式AI橫行的年代,工程師有些怕用AI工具廢了自己多年的肌肉記憶/邏輯思考,
也有人覺得與其問AI不如自己寫答案更快,甚至有人喜歡讓AI來產全部程式碼,
因為覺得學習大量程式文本的模型寫出來的Code品質更高,自己只要思考更高層次的東西即可。
也有人覺得與其問AI不如自己寫答案更快,甚至有人喜歡讓AI來產全部程式碼,
因為覺得學習大量程式文本的模型寫出來的Code品質更高,自己只要思考更高層次的東西即可。
個人想法是把自己跟AI想成是團隊夥伴的關係,我是隊長,而他是隊員。
身為隊長,你必須專注在問題方向/解題路線,讓隊員幫你一起尋找答案,
你迷茫時能詢問隊員的意見,而隊友迷失方向你也能提供新的思路給他,
無論是誰找到答案,你都必須理解答案並確認是否正確。
寫這篇文章編修大概花了我3個小時,這是沒有AI的幫助加上我很久沒寫這種文章的關係,
希望這篇文章能幫助到一些與AI協作感到迷茫的人
