當你好不容易寫好一連串的aggregate,想要將整理好的資料寫回資料庫內,這時候就可以使用$merge
將資料寫入,使用上必須特別注意,一定要放在pipeline最後一個位置,不然會直接噴錯誤訊息,「MongoError: $merge can only be the final stage in the pipeline」。
讓我們延續昨天的範例,原本有學生的成績資料(隔壁小王成績太高,有被我調低一點XD)
{ _id: 1, name: " 小美", math: 20, english: 12 },
{ _id: 2, name: " 小王", math: 81, english: 66 },
{ _id: 3, name: " 小明", math: 78, english: 30 },
{ _id: 4, name: " 小帥", math: 80, english: 60 },
{ _id: 5, name: " 小真", math: 78, english: 12 }
老師決定幫學生進行加分,加分方式是數學成績乘上1.2,英文的分數乘上1.5,並且要將調整完的分數,寫回去資料庫內,這時會使用到以下指令。
student.aggregate([
{
$addFields: {
math: { $floor: { $multiply: ["$math", 1.2] } },
english: { $floor: { $multiply: ["$english", 1.5] } }
}
},
{
$merge: {
into: "student", // 要將結果寫入哪一個collection內
on: "_id", // 寫入的資料,要用哪一個欄位與原始資料做對應
whenMatched: "replace", // 如果有原始資料,則取代它
whenNotMatched: "insert" // 如果沒有原始資料,則新增一筆資料
}
},
]);
此時aggregate跑完會回傳空陣列,如果想看資料是否被正確寫入,可以使用find把學生的資料撈出來檢查一下。
student.find() // 找出student collection所有資料
// 回傳的資料會是
{ _id: 1, name: " 小美", math: 24, english: 18 },
{ _id: 2, name: " 小王", math: 97, english: 99 },
{ _id: 3, name: " 小明", math: 93, english: 45 },
{ _id: 4, name: " 小帥", math: 96, english: 90 },
{ _id: 5, name: " 小真", math: 93, english: 18 }
$merge
其實有很多細節設定可以拿出來講
into
設定要寫入的collection是原本不存在的,MongoDB會自動幫你建立一個新的collectionon
其實可以設定多個欄位,來取匹配原本資料庫的文件,看要取代掉哪一筆資料。例如:on: ["_id", "name]
代表寫入的資料_id
和name
這兩個欄位都要和原本資料_id
和name
欄位值都相同,才會取代這筆資料,否則會新增一筆全新資料。
⚠️ 必須特別注意的是,選取的欄位一定要有 unique index,不然終端機會噴出錯誤訊息「MongoError: Cannot find index to verify that join fields will be unique」
ps.良心建議,直接寫on: "_id"
會比較間單,因為MongoDB本身就會幫你建立好_id
的unique index,完全不需要額外設定。(本人為了測時這個設定,被unique index搞了一整個上午)
whenMatched
除了replace(取代),還有其他的設定可以選擇,例如:keepExisting可以保留原本的資料,不會被取代。whenNotMatched
除了insert(插入),還有其他的設定可以選擇,例如:discard不會插入新的資料進入collection。如果想要在更深入了解$merge
的細節設定,可以參考官方文件。
本篇文章同步放在我的部落格,大家有空可以進來逛逛