2021 iThome 鐵人賽 - DAY7 MongoDB 資料更新(Update)

2021 iThome 鐵人賽 MongoDB披荊斬棘之路


DAY7 MongoDB 資料更新(Update)

更新(update)

資料更新(Update)如同寫入或刪除一樣,都是相同 Pattern,差別是條件比較多。

1
2
db.collection.updateOne(filter, update, options)
db.collection.updateMany(filter, update, options)

兩者基本上是一樣的,相信你們看完前面的 insertOne, insertMany, deleteOne, deleteMany 都有感覺了。

filter 是過慮條件,也就是你要找到欲更新文件的條件。

options 這個是更新的設定選項,在剛開始會用不到太多項目,只有一個要特別注意,就是 upsert,通常我們會設定為 true,這也是 MongoDB 很方便的地方。顧名思義,就是當欲更新的文件如果存在時,進行 update,當找不到這比文件時,就進行 insert

UpdateOne Demo - 1

馬上來個範例,我們先查詢,確保沒有該文件,接著執行 upsert:trueupdateOne 語法:

1
2
3
4
5
6
7
8
9
10
11
12
13
film> db.upsert.sample.find()

film> db.upsert.sample.updateOne({},{$set:{'name':'try upsert'}},{upsert:true})
{
acknowledged: true,
insertedId: ObjectId("6131047cf80a26301f6a5856"),
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1
}
film> db.upsert.sample.find()
[ { _id: ObjectId("6131047cf80a26301f6a5856"), name: 'try upsert' } ]
film>

可以看到 upsert.sample collection 起初是沒有資料的,我使用了 find(),不帶任何參數的查詢,查不到任何資料。
接著再一個 updateOne 的內容裡,執行後直接新增比資料,透過回傳的參數也可以看到 upsertedCount 數量為 1。

  • insertedId:本次執行結果,寫入的 ObjectId 清單
  • matchedCount:本次執行結果,符合查詢條件的文件數量
  • modifiedCount:本次執行結果,符合查詢條件且更新的文件數量
  • upsertedCount:本次執行結果,進行 upsert 的文件數量

接著將這次的 updateOne 語法來拆開來看:

1
2
3
4
5
db.upsert.sample.updateOne(
{},
{$set:{'name':'try upsert'}},
{upsert:true}
)

可以看到三組大括弧 {},這分別對應了上面提到的 filter, updateoptions

  • filter:這邊我們直接不帶條件,想進行全部文件的 update
  • update:我們設定 name 欄位的值為 try upsert,$set 是 MongoDB 的 operator 之一,這個我們之後再講解,先忍著略過它。
  • options:這次 updateOne 中,設定值設定 upserttrue,代表找不到資料就寫入。設定還有 writeConcern, collationhint 等,這個會在後面再講解,現在提就太深入了。

UpdateOne Demo - 2

這次我們準備幾筆符合查詢條件的文件,使用 UpdateOne 看看會發生什麼事情。

資料庫內有三筆名為 Arthas 的資料

1
2
3
4
5
6
7
film> db.upsert.demo2.find()
[
{ _id: ObjectId("61310798630faf5d23c909d3"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d4"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d5"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d6"), name: 'Thrall' }
]

執行 updateOne,將名為 Arthas 的資料欄位改為 ErrorName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
film> db.upsert.demo2.updateOne({'name':'Arthas'}, {$set:{'name':'ErrorName'}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
film> db.upsert.demo2.find()
[
{ _id: ObjectId("61310798630faf5d23c909d3"), name: 'ErrorName' },
{ _id: ObjectId("61310798630faf5d23c909d4"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d5"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d6"), name: 'Thrall' }
]

可以看到執行後,再次查詢,只會有第一筆被修改,因為我們執行的是 updateOne

那為什麼第一筆不是_id9d49d5 結尾的資料呢?因為 MongoDB 採用的是 natural sort,在這個情況下就是 9d3 這一筆。我們會在後面的天數講到 natural sort,這是一個非重要的概念。

replaceOne

replaceOne,找到符合條件的文件,直接整份文件取代。

replaceOne 用法與 Update 高度相近,來看看它的長相:

db.collection.replaceOne(filter, replacement, options)

  • filter: 設定的查找文件條件
  • replacement: 新的文件內容
  • options: 設定值,與 update 的幾乎一樣。

我們來嘗試找到一個文件,{name:'Thrall'},將它改成下面這樣子

1
2
3
4
{
'name':'NewThrall',
'type':'melee'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
film> db.upsert.demo2.find()
[
{ _id: ObjectId("61310798630faf5d23c909d3"), name: 'ErrorName' },
{ _id: ObjectId("61310798630faf5d23c909d4"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d5"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d6"), name: 'Thrall' }
]
film> db.upsert.demo2.replaceOne({'name':'Thrall'}, {'name':'NewThrall', 'type':'melee'})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
film> db.upsert.demo2.find()
[
{ _id: ObjectId("61310798630faf5d23c909d3"), name: 'ErrorName' },
{ _id: ObjectId("61310798630faf5d23c909d4"), name: 'Arthas' },
{ _id: ObjectId("61310798630faf5d23c909d5"), name: 'Arthas' },
{
_id: ObjectId("61310798630faf5d23c909d6"),
name: 'NewThrall',
type: 'melee'
}
]

可以看到新的內容已經取代上去,特別要注意的是 _id 並沒有任何異動,即便它不是客製化的 _id,仍然不會改變,因為這個操作是取代文件內容,不會影響它本身的 key。

附帶一提,replaceOne 如同上面所說,設定值是幾乎一樣的,所以也有 upsert 功能,這邊就不再示範了,結果會與 update 一樣。

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。