MongoDB, out, merge
本篇接續 MongoDB $out 教學,介紹的是 $out 語法的進階版本 $merge,客製化程度更高了,能夠處理衝突時該使用覆蓋還是略過等。
$merge 僅能在 aggregation pipeline 中使用,且必須是最後一個執行的階段,也就是說在前面可以執行任何的資料篩選、操作和轉型,最終輸入到目的地。
語法與參數
1  | { $merge: {  | 
從上述語法上可以得知
into就是匯入的集合on指定唯一的欄位,用來做文件比對是否相符,以進行whenMatched或whenNotMatched動作,可以是一個或多個欄位let可以讓使用者自定義的變數$$xxxwhenMatched當遇到衝突時該..- replace 直接取代
 - keepExisting 略過
 - merge 嘗試合併(預設選項)
 - fail 直接回傳失敗
 - pipeline 若有自定義的 user variable,就必須使用 pipeline 參數來客製整個操作細節
 
whenNotMatched- insert 直接寫入(預設選項)
 - discard 忽略此資料
 - fail 直接報錯
 
注意事項
$merge語法會自動建立db與自動建立collection,唯獨 cluster 狀態下,需要目的的 db 已經存在- 一但成功建立第一筆文件後,
db與collection就能夠立即看到 - 若遇到錯誤,則錯誤之前寫入的資料都還是會存在,
並不會被rollback 
輸出與來源是相同集合
在 4.4 版本之後就支援這個功能啦!
官方的文件上看起來是不建議這樣做,因為文件可能會被多次更新,甚至是造成無限迴圈,且修改行為很有可能造成著名的 Halloween Problen,在效能上也可能無法預期。綜合以上,如果要做這種舉動,可以用更新或者寫到新集合之類的方案來解決。
Halloween Problem
相傳於萬聖節,有間公司想替薪水低於 50000 的員工加薪 10%,於是寫了以下 sql
1  | UPDATE EMP_TABLE SET SALARY=SALARY*1.1 WHERE SALARY<50000  | 
結果發生什麼事?就是所有薪水低於 50000 的員工都會不斷地被執行更新 SALARY 欄位,直到高於 50000 為止。
當然啦~現在資料庫都能夠處理這種低級錯誤了,但還是要了解一下 Update 機制以及 Cursor 機制。
基本語法
最簡單的莫過於單純 merge 啦!
1  | { $merge: <collection> }  | 
重新建立新的 _id
1  | db.source.aggregate([  | 
測試
首先我們來準備一下測試資料
1  | db.src.insertMany([  | 
Case1 直接複製
Syntax:
1  | db.src.aggregate([ { $merge: 'test1col'} ])  | 
1  | > db.src.aggregate([ { $merge: 'test1col'} ])  | 
看起來沒什麼難度
Case2 調整一下欄位吧
移除 _id 與 year 欄位
Syntax:
1  | db.src.aggregate([  | 
可以看到新的集合內 _id 變成預設寫入的 ObjectId 以及 year 不見了
1  | > db.test2col.find()  | 
Case3 嘗試合併欄位
這次要用目的地已經存在該資料,且欄位略有重複,看看會發生什麼事。
首先要在目的資料庫建立一些資料,值用 -1 是方便查看改變,而多了一個 vip 欄位。
1  | db.test3col.insertMany([  | 
Syntax:
1  | db.src.aggregate([  | 
可以看到新的集合內 _id 為 1,2,3 的資料都合併成功,已經存在的欄位覆蓋,多的 vip 則保持不變。
另外 _id 為 4,5 就是單純的 insert
1  | > db.test3col.find()  | 
$merge 的介紹差不多到這就可以了,比較深入的語法若工作有用到可以再私下問,寫太細節應該是沒人會看XD