MongoDB, out, merge
本篇接續 MongoDB $out 教學,介紹的是 $out 語法的進階版本 $merge,客製化程度更高了,能夠處理衝突時該使用覆蓋還是略過等。
$merge 僅能在 aggregation pipeline 中使用,且必須是最後一個執行的階段,也就是說在前面可以執行任何的資料篩選、操作和轉型,最終輸入到目的地。
語法與參數
| 1 | { $merge: { | 
從上述語法上可以得知
- into就是匯入的集合
- on指定唯一的欄位,用來做文件比對是否相符,以進行- whenMatched或- whenNotMatched動作,可以是一個或多個欄位
- let可以讓使用者自定義的變數- $$xxx
- whenMatched當遇到衝突時該..- 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