Thao tác CRUD trong MongoDB
Xem thửTình huống mở đầu
Sáng nay cửa hàng bán 3 màn hình, tồn kho phải giảm từ 5 còn 2. Bạn gõ updateOne({ name: "monitor" }, { stock: 2 }) và shell từ chối bằng một lỗi nhắc tới "atomic operators". Bạn đổi cách viết thành $set, lệnh chạy xong, nhưng bạn không chắc nó đã sửa đúng một document hay không. Đồng nghiệp bên cạnh còn kể chuyện hôm trước một bạn gõ deleteMany({}) định xóa một sản phẩm thì kho trống trơn.
Hãy đoán trước: bốn nghiệp vụ nhập, tra, sửa, xuất của kho MongoDB là những lệnh nào, và mỗi lệnh báo lại cho bạn điều gì?
Ôn nhanh bài trước: db.dropDatabase() xóa database nào? Đáp án: database mà db đang trỏ tới. Bài này làm việc nhỏ hơn: xóa và sửa từng document bên trong một database.
Mục tiêu bài học
Sau bài này bạn có thể:
- Gọi
insertOne,insertManyvà đọcinsertedId,insertedIds. - Dùng
findvàfindOneđể đọc lại dữ liệu vừa ghi. - Sửa dữ liệu bằng
updateOnevàupdateManyvới$sethoặc$inc, và phân biệtmatchedCountvớimodifiedCount. - Biết bẫy thiếu
$set, khác biệt vớireplaceOne, và cách dùngupsert. - Xóa bằng
deleteOnevàdeleteMany, đọcdeletedCount, và tránh xóa nhầm khi bộ lọc rỗng.
Bức tranh lớn
Xuyên suốt bài, kho products có bốn nghiệp vụ: nhập hàng (create), tra hàng (read), sửa hàng (update), xuất hàng ra khỏi kho (delete). Dự án xuyên suốt là quản lý kho products của tòa nhà shop với ba sản phẩm mẫu: keyboard, mouse và monitor.
Hình dưới trả lời một câu hỏi: bốn nghiệp vụ gồm những lệnh nào?
Đang tải sơ đồ…
Cách đọc hình: đọc từ trên xuống theo vòng đời của một thùng hàng. Ô xanh lá là nhập, ô cyan là tra, ô tím là sửa, ô đỏ là xóa. Trong mỗi ô là tên các lệnh của nghiệp vụ đó.
Chỗ ẩn dụ không còn đúng: thùng hàng thật chỉ ở một nơi, còn document xóa xong là mất hẳn và không có thùng rác. Bài này chỉ giới thiệu bước đầu; các phương thức như insertMany, updateOne, find có chương riêng để đào sâu.
Khái niệm cốt lõi
Bài này có năm khái niệm.
1. Create: insertOne và insertMany
insertOne(document) ghi một document và trả { acknowledged, insertedId }. insertMany([...]) ghi nhiều document và trả insertedIds. _id trùng bị từ chối với lỗi E11000.
2. Read: find và findOne
Hai lệnh đọc bạn đã học ở bài Truy vấn. find trả cursor, findOne trả một document hoặc null.
3. Update: toán tử cập nhật
updateOne(bộ lọc, cập nhật) sửa document đầu tiên khớp, updateMany sửa mọi document khớp. Tham số cập nhật phải dùng toán tử như $set (đặt giá trị trường), $inc (cộng thêm), $unset (bỏ trường). Kết quả có matchedCount (số document khớp) và modifiedCount (số document thật sự đổi).
4. replaceOne và upsert
replaceOne(bộ lọc, document mới) thay cả document, giữ _id, các trường khác biến mất. Tùy chọn { upsert: true } nghĩa là nếu không có document khớp thì tạo mới, và kết quả có upsertedId.
5. Delete: deleteOne và deleteMany
deleteOne(bộ lọc) xóa document đầu tiên khớp, deleteMany(bộ lọc) xóa mọi document khớp. Kết quả có deletedCount. Bộ lọc rỗng {} với deleteMany xóa mọi document trong collection nhưng collection vẫn còn.
Ví dụ đầu tiên
Nhập ba sản phẩm vào kho bằng cả hai lệnh:
db.products.insertOne({ _id: 1, name: "keyboard", price: 450000, stock: 12 })
db.products.insertMany([
{ _id: 2, name: "mouse", price: 220000, stock: 40 },
{ _id: 3, name: "monitor", price: 3200000, stock: 5 }
])
{ "acknowledged" : true, "insertedId" : 1 }
{ "acknowledged" : true, "insertedIds" : [ 2, 3 ] }
Dự đoán trước khi xem kết quả: insertOne trả insertedId hay insertedIds, và insertMany thì sao?
Kết quả ở trên [Đã chạy: MongoDB 4.4.19, mongo shell 4.4.19]. Tra lại bằng find và findOne:
db.products.find({}, { name: 1, stock: 1 })
db.products.findOne({ _id: 2 })
{ "_id" : 1, "name" : "keyboard", "stock" : 12 }
{ "_id" : 2, "name" : "mouse", "stock" : 40 }
{ "_id" : 3, "name" : "monitor", "stock" : 5 }
{ "_id" : 2, "name" : "mouse", "price" : 220000, "stock" : 40 }
Bây giờ sửa giá keyboard, rồi chạy lại đúng lệnh đó, rồi sửa một document không tồn tại:
db.products.updateOne({ _id: 1 }, { $set: { price: 430000 } })
db.products.updateOne({ _id: 1 }, { $set: { price: 430000 } })
db.products.updateOne({ _id: 99 }, { $set: { price: 1 } })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
updateOne({ _id: 1 }, { $set: { price: 430000 } }) | Tìm document _id 1 và đặt price thành 430000 | $set chỉ đổi trường được nêu, các trường khác giữ nguyên |
matchedCount: 1, modifiedCount: 1 | Một document khớp và một document thật sự đổi | Lần đầu giá đổi từ 450000 sang 430000 |
matchedCount: 1, modifiedCount: 0 | Lần chạy lặp vẫn khớp nhưng không đổi | Giá đã là 430000 nên không có gì để ghi |
matchedCount: 0, modifiedCount: 0 | Không có document _id 99 | Cập nhật không tạo mới trừ khi có upsert |
Xem từng bước
Animation dưới đây cho thấy một document đi qua tạo, sửa hai lần, sửa sai cú pháp rồi xóa. Hãy trả lời câu dự đoán trước khi bấm bước đầu tiên.
Đang tải Animation…
Bảng trace bằng chữ [Đã chạy: ví dụ set1 ở trên và noset ở mục Thử phá nó đi]:
| Bước | Lệnh | matchedCount | modifiedCount | Document còn lại |
|---|---|---|---|---|
| 1 | updateOne với $set lần đầu | 1 | 1 | price 430000 |
| 2 | Cùng lệnh lần hai | 1 | 0 | price 430000 |
| 3 | updateOne thiếu $set | Không chạy, báo lỗi | Không chạy | price 430000 |
| 4 | updateOne với _id 99 | 0 | 0 | Không đổi |
Bạn vừa làm được: đọc hai con số của một lệnh cập nhật và nói chính xác lệnh đã làm gì với dữ liệu.
Concept Check
Dùng kho products ba document ở trên.
Câu 1. updateOne({ _id: 1 }, { $set: { price: 450000 } }) khi keyboard đã có price: 450000. Kết quả?
- A.
matchedCount: 0, modifiedCount: 0, vì giá đã đúng nên bỏ qua. - B.
matchedCount: 1, modifiedCount: 0, vì khớp nhưng không có gì đổi. - C.
matchedCount: 1, modifiedCount: 1, vì lệnh luôn ghi lại document. - D. Lỗi trùng giá trị, vì MongoDB cấm ghi lại cùng một giá trị.
Câu 2. updateOne({ _id: 1 }, { price: 430000 }) trong mongo shell 4.4.19 trả về gì?
- A. Lỗi yêu cầu toán tử cập nhật, document giữ nguyên.
- B. Thay cả document keyboard bằng
{ price: 430000 }. - C. Thêm trường
pricemới ở cuối document keyboard. - D. Thành công với
matchedCount: 1, modifiedCount: 1.
Câu 3. Sau replaceOne({ _id: 2 }, { name: "mouse wireless", price: 260000 }), document mouse còn trường stock không?
- A. Còn, vì replace giữ mọi trường không được nhắc tới.
- B. Còn, nhưng bị đặt lại về giá trị mặc định là 0.
- C. Không, vì document mới thay thế toàn bộ trừ
_id. - D. Không có document nào, vì replace đã xóa nó.
Đáp án và giải thích:
- Câu 1: B. Document khớp nhưng giá trị không đổi. A sai vì nó vẫn khớp. C sai vì chỉ đếm cái thực sự đổi. D bịa ra quy tắc.
- Câu 2: A. Tham số cập nhật phải có toán tử. B là hành vi của
update()cũ, không phảiupdateOne. C và D sai vì lệnh bị từ chối trước khi ghi. - Câu 3: C.
replaceOnethay cả document, chỉ giữ_id. A là hành vi của$set. B bịa ra giá trị mặc định. D sai vì document vẫn còn.
Làm cùng mình
Nhiệm vụ: ghi lại nghiệp vụ một ngày. Nhập thêm cable (_id 4) và headset (_id 5). Bán 3 monitor (giảm stock). Tăng giá mouse lên 230000. Rồi trả headset về nhà cung cấp (xóa).
Bước 1. Vẽ ra giấy
Vẽ kho sau mỗi nghiệp vụ, ghi stock của monitor và price của mouse. Dự đoán: kho cuối ngày có mấy sản phẩm?
Bước 2. Điền chỗ trống
// TODO 1: nhập cable (_id 4, price 35000, stock 200) và headset (_id 5, price 780000, stock 9) bằng insertMany
// TODO 2: giảm stock của monitor (_id 3) đi 3 bằng $inc
// TODO 3: đặt price của mouse (name "mouse") thành 230000 bằng $set
// TODO 4: xóa headset bằng deleteOne theo name
// TODO 5: in lại name, price, stock của mọi sản phẩm
Gợi ý: $inc nhận số âm để giảm. In kết quả của từng lệnh để thấy matchedCount, modifiedCount, deletedCount.
Bước 3. So kết quả
Chạy xong bạn sẽ thấy [Đã chạy] ba kết quả đếm, rồi danh sách sản phẩm. So với dự đoán ở Bước 1; đáp án ở mục Lời giải và đáp án.
Bước 4. Hỏi thêm
Vẽ lại: nếu bước 4 viết deleteMany({}) thay vì deleteOne({ name: "headset" }), kho còn gì và collection còn không? Ghi một câu.
Thử phá nó đi
Năm lỗi hay gặp khi ghi dữ liệu [Đã chạy: MongoDB 4.4.19]:
| Code lỗi | Kết quả | Nguyên nhân | Cách sửa |
|---|---|---|---|
insertOne với _id đã có | E11000 duplicate key error | _id phải duy nhất | Đổi _id hoặc bỏ để tự sinh |
updateOne(filter, { price: 1 }) | must contain atomic operators | Thiếu toán tử như $set | Viết { $set: { price: 1 } } |
replaceOne(filter, { $set: ... }) | must not contain atomic operators | Dùng toán tử trong document thay thế | Dùng updateOne nếu muốn sửa một phần |
updateOne(..., { $set: { _id: 5 } }) | would modify the immutable field '_id' | _id không đổi được | Xóa và ghi lại document mới |
deleteMany({}) | Mọi document bị xóa | Bộ lọc rỗng khớp tất cả | Kiểm tra bộ lọc, đếm trước bằng countDocuments |
Hình dưới giải thích điểm dễ nhầm nhất: tham số thứ hai của updateOne và của replaceOne khác nhau.
Đang tải sơ đồ…
Cách đọc hình: ô xanh dương là tham số thứ hai của lệnh sửa. Ô cam hỏi các khóa có bắt đầu bằng dấu đô la không. Nhánh "yes" đi tới updateOne và updateMany (sửa từng trường), nhánh "no" đi tới replaceOne (thay cả document). Hai ô đỏ cuối là lỗi khi dùng nhầm.
Chạy thật các lỗi. Thiếu $set (raw shell, kèm dấu vết gọi hàm):
db.products.updateOne({ _id: 1 }, { price: 430000 })
uncaught exception: Error: the update operation document must contain atomic operators :
DBCollection.prototype.updateOne@src/mongo/shell/crud_api.js:565:19
@(shell):1:1
Dùng $set trong replaceOne:
db.products.replaceOne({ _id: 2 }, { $set: { price: 260000 } })
uncaught exception: Error: the replace operation document must not contain atomic operators :
DBCollection.prototype.replaceOne@src/mongo/shell/crud_api.js:480:15
@(shell):1:1
Đổi _id:
db.products.updateOne({ _id: 1 }, { $set: { _id: 5 } })
WriteError({
"index" : 0,
"code" : 66,
"errmsg" : "Performing an update on the path '_id' would modify the immutable field '_id'",
"op" : {
"q" : {
"_id" : 1
},
"u" : {
"$set" : {
"_id" : 5
}
},
"multi" : false,
"upsert" : false
}
})
Trùng _id khi nhập:
db.products.insertOne({ _id: 1, name: "another keyboard" })
WriteError({
"index" : 0,
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: shop.products index: _id_ dup key: { _id: 1.0 }",
"op" : {
"_id" : 1,
"name" : "another keyboard"
}
})
replaceOne thay cả document: trường stock biến mất.
db.products.replaceOne({ _id: 2 }, { name: "mouse wireless", price: 260000 })
db.products.findOne({ _id: 2 })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "_id" : 2, "name" : "mouse wireless", "price" : 260000 }
updateMany sửa nhiều document cùng lúc ($gt nghĩa là lớn hơn, chương sau sẽ dạy kỹ): keyboard 12 và mouse 40 đều có stock lớn hơn 10 nên cùng giảm 1, monitor 5 giữ nguyên.
db.products.updateMany({ stock: { $gt: 10 } }, { $inc: { stock: -1 } })
db.products.find({}, { name: 1, stock: 1 })
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }
{ "_id" : 1, "name" : "keyboard", "stock" : 11 }
{ "_id" : 2, "name" : "mouse", "stock" : 39 }
{ "_id" : 3, "name" : "monitor", "stock" : 5 }
upsert tạo mới khi không khớp, và lần chạy lặp không tạo thêm:
db.products.updateOne({ _id: 50 }, { $set: { name: "headset" } }, { upsert: true })
db.products.updateOne({ _id: 50 }, { $set: { name: "headset" } }, { upsert: true })
db.products.findOne({ _id: 50 })
{
"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : 50
}
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
{ "_id" : 50, "name" : "headset" }
Xóa, kể cả xóa sạch bằng bộ lọc rỗng. Collection vẫn còn nhưng không có document:
db.products.deleteOne({ _id: 3 })
db.products.deleteMany({ name: "nothing" })
db.products.deleteMany({})
print("documents left:", db.products.countDocuments({}))
print("collection still exists:", db.getCollectionNames().indexOf("products") >= 0)
{ "acknowledged" : true, "deletedCount" : 1 }
{ "acknowledged" : true, "deletedCount" : 0 }
{ "acknowledged" : true, "deletedCount" : 2 }
documents left: 0
collection still exists: true
Tự tay làm
Dùng chung kho products ba sản phẩm.
Cơ bản
Bài 1. Đoán kết quả. Đầu vào: thêm { _id: 4, name: "cable", price: 35000, stock: 200 }, rồi hai lần updateMany({ stock: { $gt: 100 } }, { $set: { hot: true } }), rồi deleteMany({ hot: true }). Ghi ra giấy ba kết quả đếm. Tiêu chí đạt: đoán đúng cả ba. Gợi ý: lần lặp vẫn khớp nhưng giá trị không đổi.
Vận dụng
Bài 2. Bỏ một trường. Đầu vào: keyboard (_id 1). Dùng $unset để bỏ trường stock, đọc lại document, rồi chạy lại lệnh lần hai. Tiêu chí đạt: nêu được modifiedCount của lần hai và lý do. Gợi ý: $unset: { stock: 1 }.
Thử thách (tùy chọn)
Bài 3. Nhập kho một lệnh. Đầu vào: cable (_id 4). Viết một lệnh updateOne với upsert: true sao cho nếu cable chưa có thì tạo mới với stock 10 và đặt tên, giá lúc tạo; nếu đã có thì chỉ cộng thêm 10 vào stock. Tiêu chí đạt: chạy hai lần cho kết quả upsertedId ở lần đầu và stock bằng 20 sau lần hai. Gợi ý: $inc cộng stock, $setOnInsert đặt trường chỉ khi tạo mới.
Tự kiểm tra
Trả lời bằng lời của bạn, không nhìn lại bài.
- (Mục tiêu 1, nhớ lại)
insertOnevàinsertManytrả vềinsertedIdhayinsertedIds? - (Mục tiêu 3, dự đoán)
updateManykhớp 3 document nhưng 1 document đã có sẵn giá trị mới. Hai con số đếm là bao nhiêu? - (Mục tiêu 4, tìm lỗi)
replaceOne({ _id: 2 }, { $set: { price: 1 } })báo lỗi gì, vì sao? - (Mục tiêu 4, vận dụng) Khi nào dùng
upsert: true? - (Mục tiêu 5, giải thích) Vì sao
deleteMany({})nguy hiểm và làm sao giảm rủi ro?
Điều cần nhớ
- Create:
insertOnetrảinsertedId,insertManytrảinsertedIds;_idtrùng gây E11000. - Read:
findtrả cursor,findOnetrả một document hoặcnull. - Update: cần toán tử như
$set,$inc,$unset;matchedCountđếm khớp,modifiedCountđếm thật sự đổi. replaceOnethay cả document trừ_id;upsert: truetạo mới khi không khớp và trảupsertedId.- Delete:
deleteOnehoặcdeleteManytrảdeletedCount; bộ lọc rỗng xóa sạch document nhưng giữ collection.
Cheat sheet:
| Muốn | Viết | Kết quả |
|---|---|---|
| Nhập một | insertOne({ ... }) | acknowledged, insertedId |
| Nhập nhiều | insertMany([...]) | acknowledged, insertedIds |
| Tra nhiều | find(filter, projection) | Cursor |
| Tra một | findOne(filter) | Document hoặc null |
| Sửa một | updateOne(filter, { $set: { ... } }) | matchedCount, modifiedCount |
| Sửa nhiều | updateMany(filter, { $inc: { ... } }) | matchedCount, modifiedCount |
| Thay cả document | replaceOne(filter, { ... }) | matchedCount, modifiedCount |
| Tạo nếu chưa có | updateOne(filter, update, { upsert: true }) | Thêm upsertedId |
| Xóa một | deleteOne(filter) | deletedCount |
| Xóa nhiều | deleteMany(filter) | deletedCount |
Hình tóm tắt của bài là sơ đồ ở mục Bức tranh lớn.
Câu hỏi để dành cho ngày mai: bạn đã thấy insertMany dừng ở lỗi đầu tiên. Nếu cần nhập hàng nghìn document và chịu được lỗi lẻ, có những cách nào nhanh và chắc hơn?
Đào sâu
Phần này dành cho bạn tò mò.
- Cơ chế bên trong. Mỗi lệnh ghi trả một kết quả có
acknowledged: true, cho biết server đã nhận. [Suy luận] Theo tài liệu, đây là mức xác nhận mặc định (write concern) và có thể chỉnh để chờ ghi lên nhiều máy; bài này dùng mặc định. Chủ đề write concern nằm ngoài phạm vi bài. - So sánh với lựa chọn khác.
replaceOnegiống ghi đè cả dòng, cònupdateOnevới$setgiốngUPDATE ... SET một_cộttrong SQL. Trong SQL,UPDATEkhông có WHERE sửa mọi dòng; trong MongoDB,updateMany({}, ...)vàdeleteMany({})có cùng rủi ro. - Thông báo lỗi thay đổi theo phiên bản. Thông báo
must contain atomic operatorsmà bài in ra là của mongo shell 4.4.19 [Có thể khác nhau]; các driver và bản mới có thể dùng câu chữ khác, ví dụ nhắcUpdate document requires atomic operators. [Suy luận] API cũupdate()(chương Phương thức) có hành vi khác: tham số không có toán tử sẽ thay cả document. - Hiểu lầm nâng cao.
matchedCountlớn hơnmodifiedCountkhông phải là lỗi: nó cho biết có document khớp nhưng đã có sẵn giá trị đó. - Giới hạn của bài. Bài chưa nói về
insertManyvớiordered,bulkWrite,findOneAndUpdate, hay giao dịch nhiều document; các chủ đề đó có chương riêng. - Đọc thêm. MongoDB Manual 4.4: https://www.mongodb.com/docs/v4.4/crud/ và https://www.mongodb.com/docs/v4.4/tutorial/update-documents/ và https://www.mongodb.com/docs/v4.4/tutorial/remove-documents/
Bài tiếp theo
Chương sau đi sâu từng phương thức: insertOne, insertMany, updateOne, find, deleteOne và nhiều phương thức khác, mỗi phương thức một bài. Mini-dự án nối tiếp: nhập lô linh kiện thứ hai vào kho shop và theo dõi lỗi lẻ.
Lời giải và đáp án
Làm cùng mình, Bước 2.
var r1 = db.products.insertMany([{ _id: 4, name: "cable", price: 35000, stock: 200 }, { _id: 5, name: "headset", price: 780000, stock: 9 }])
printjson(db.products.updateOne({ _id: 3 }, { $inc: { stock: -3 } }))
printjson(db.products.updateOne({ name: "mouse" }, { $set: { price: 230000 } }))
printjson(db.products.deleteOne({ name: "headset" }))
db.products.find({}, { name: 1, price: 1, stock: 1 })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "acknowledged" : true, "deletedCount" : 1 }
{ "_id" : 1, "name" : "keyboard", "price" : 450000, "stock" : 12 }
{ "_id" : 2, "name" : "mouse", "price" : 230000, "stock" : 40 }
{ "_id" : 3, "name" : "monitor", "price" : 3200000, "stock" : 2 }
{ "_id" : 4, "name" : "cable", "price" : 35000, "stock" : 200 }
Monitor giảm từ 5 còn 2, mouse có giá 230000, headset bị xóa, kho cuối ngày còn 4 sản phẩm: keyboard, mouse, monitor, cable. Nếu bước 4 viết deleteMany({}) thì kho còn 0 document nhưng collection products vẫn còn.
Bài 1.
print(tojson(db.products.updateMany({ stock: { $gt: 100 } }, { $set: { hot: true } })))
print(tojson(db.products.updateMany({ stock: { $gt: 100 } }, { $set: { hot: true } })))
print(tojson(db.products.deleteMany({ hot: true })))
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
{ "acknowledged" : true, "deletedCount" : 1 }
Lần một matchedCount và modifiedCount đều 1 (chỉ cable khớp stock > 100). Lần hai matchedCount 1 nhưng modifiedCount 0 vì hot: true đã có. deleteMany xóa 1 document.
Bài 2.
print(tojson(db.products.updateOne({ _id: 1 }, { $unset: { stock: 1 } })))
print(tojson(db.products.findOne({ _id: 1 })))
print(tojson(db.products.updateOne({ _id: 1 }, { $unset: { stock: 1 } })))
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "_id" : 1, "name" : "keyboard", "price" : 450000 }
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
Lần hai modifiedCount bằng 0 vì trường stock đã không còn, không có gì để bỏ.
Bài 3.
var op = { $inc: { stock: 10 }, $setOnInsert: { name: "cable", price: 35000 } }
print(tojson(db.products.updateOne({ _id: 4 }, op, { upsert: true })))
print(tojson(db.products.updateOne({ _id: 4 }, op, { upsert: true })))
print(tojson(db.products.findOne({ _id: 4 })))
{
"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : 4
}
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
{ "_id" : 4, "name" : "cable", "price" : 35000, "stock" : 20 }
Lần đầu có upsertedId 4 (tạo mới với stock 10, tên và giá từ $setOnInsert). Lần hai chỉ cộng thêm 10 nên stock bằng 20 và $setOnInsert không chạy.
Tự kiểm tra.
insertOnetrảinsertedId,insertManytrảinsertedIds.matchedCount3 vàmodifiedCount2.- Lỗi
must not contain atomic operators, vìreplaceOnethay cả document nên không nhận toán tử như$set; muốn sửa một phần dùngupdateOne. - Khi muốn cập nhật nếu có và tạo mới nếu chưa có, ví dụ nhập kho một mã hàng chưa chắc đã tồn tại.
- Bộ lọc rỗng khớp mọi document nên xóa sạch collection. Giảm rủi ro bằng cách dùng bộ lọc cụ thể và chạy
countDocumentsvới cùng bộ lọc để xem số document sẽ bị xóa trước.