Sejak insertOne() yang pertama, setiap perintah tulis kita dibalas object hasil berisi acknowledged dan kawan-kawannya — kita membacanya sepintas tanpa bertanya dari mana isiinya berasal. Chapter penutup Section CRUD ini membedah dua hal sekaligus: struktur hasil penulisan (write results) yang dikembalikan mongosh, dan write concern — pengaturan yang menentukan seberapa kuat konfirmasi sebuah penulisan sebelum client diberi kabar “selesai”. Keduanya adalah dasar untuk memahami kompromi antara ketahanan data dan kecepatan aplikasi.
Peta materinya:
- Anatomi
InsertOneResult,UpdateResult, danDeleteResult. - Default write concern serta arti
w,j, danwtimeout. - Mode
w: 0fire-and-forget beserta risikonya. - Balasan mentah server lewat
runCommand().
Anatomi Hasil Penulisan #
Setiap method tulis mongosh membungkus balasan server menjadi tipe hasil yang tetap:
| Method | Tipe hasil | Field kunci |
|---|---|---|
insertOne() | InsertOneResult | acknowledged, insertedId |
insertMany() | InsertManyResult | acknowledged, insertedIds |
updateOne(), updateMany(), replaceOne() | UpdateResult | acknowledged, matchedCount, modifiedCount, upsertedCount, insertedId |
deleteOne(), deleteMany() | DeleteResult | acknowledged, deletedCount |
Kita tangkap ketiganya dalam satu rangkaian — sisipkan satu buku, perbarui stoknya, lalu hapus:
use latihan_crud2
db.buku.drop()
db.buku.insertMany([
{ judul: "Laskar Pelangi", penulis: "Andrea Hirata", harga: 95000, stok: 12, kategori: "Fiksi", tag: ["novel", "bestseller"], rating: 4.6 },
{ judul: "Bumi Manusia", penulis: "Pramoedya Ananta Toer", harga: 110000, stok: 8, kategori: "Fiksi", tag: ["novel", "klasik"], rating: 4.8 },
{ judul: "Atomic Habits", penulis: "James Clear", harga: 135000, stok: 20, kategori: "Pengembangan Diri", tag: ["nonfiksi", "bestseller"], rating: 4.7 },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, stok: 0, kategori: "Pengembangan Diri", tag: ["nonfiksi"], rating: 4.5 },
{ judul: "Clean Code", penulis: "Robert C. Martin", harga: 165000, stok: 5, kategori: "Teknologi", tag: ["pemrograman", "bestseller"], rating: 4.4 }
])Output yang Dihasilkan:
switched to db latihan_crud2
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3883b5243d127e3e52586'),
'1': ObjectId('6ad3883b5243d127e3e52587'),
'2': ObjectId('6ad3883b5243d127e3e52588'),
'3': ObjectId('6ad3883b5243d127e3e52589'),
'4': ObjectId('6ad3883b5243d127e3e5258a')
}
}db.buku.insertOne({ judul: "Design Patterns", penulis: "Erich Gamma", harga: 155000 })
db.buku.updateOne({ judul: "Design Patterns" }, { $set: { stok: 6 } })
db.buku.deleteOne({ judul: "Design Patterns" })Output yang Dihasilkan:
{
acknowledged: true,
insertedId: ObjectId('6ad3883b5243d127e3e5258b')
}
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{ acknowledged: true, deletedCount: 1 }acknowledged: true berarti client menunggu dan menerima konfirmasi dari server — tanpa konfirmasi itu, seluruh angka pada hasil tidak akan berarti. Field sisanya menghitung apa yang terjadi: satu document disisipkan (insertedId), satu cocok lalu berubah (matchedCount, modifiedCount), satu terhapus (deletedCount). Driver resmi Node.js dan PyMongo memakai struktur yang setara, jadi kebiasaan membaca hasil ini terbawa ke kode aplikasi.
Tingkat Konfirmasi lewat writeConcern #
Seberapa banyak pihak yang harus menulis sebelum acknowledged: true diberikan? Itulah kerja write concern, dikirim sebagai opsi { writeConcern: { ... } }. Nilainya tidak datang dari angan-angan — server melaporkan default yang berlaku pada dirinya lewat getDefaultRWConcern:
db.adminCommand({ getDefaultRWConcern: 1 })Output yang Dihasilkan:
{
defaultReadConcern: { level: 'local' },
defaultWriteConcern: { w: 'majority', wtimeout: 0 },
defaultWriteConcernSource: 'implicit',
defaultReadConcernSource: 'implicit',
localUpdateWallClockTime: ISODate('2026-10-17T14:37:48.025Z'),
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1792247867, i: 5 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1792247867, i: 5 })
}Aturannya: pada mongod standalone, default implisitnya { w: 1 } — konfirmasi dari satu-satunya anggota yang ada. Pada replica set seperti rs0 milik kita (dibuat dengan MongoDB 5.0 ke atas), default implisitnya cluster-wide write concern { w: "majority" } — persis seperti laporan di atas. Karena rs0 beranggotakan satu node, w: 1 dan w: "majority" menunggu anggota yang sama; keduanya bisa kita contohkan berdampingan:
db.buku.insertOne({ judul: "The Pragmatic Programmer", penulis: "Andrew Hunt", harga: 145000 }, { writeConcern: { w: 1 } })
db.buku.insertOne({ judul: "Extreme Programming Explained", penulis: "Kent Beck", harga: 120000 }, { writeConcern: { w: "majority" } })
db.buku.insertOne({ judul: "Refactoring", penulis: "Martin Fowler", harga: 150000 }, { writeConcern: { w: 1, j: true } })Output yang Dihasilkan:
{
acknowledged: true,
insertedId: ObjectId('6ad3883c5243d127e3e5258c')
}
{
acknowledged: true,
insertedId: ObjectId('6ad3883c5243d127e3e5258d')
}
{
acknowledged: true,
insertedId: ObjectId('6ad3883c5243d127e3e5258e')
}Tiga tingkat jaminan yang berbeda, tiga hasil yang sama-sama acknowledged: true — perbedaannya ada pada apa yang ditunggu sebelum konfirmasi diberikan:
| writeConcern | Menunggu | Jaminan |
|---|---|---|
{ w: 1 } | Konfirmasi dari primary | Tulisan diterima primary, belum tentu terkopikan |
{ w: "majority" } | Konfirmasi dari mayoritas anggota | Tulisan bertahan melewati failover |
{ ..., j: true } | Ditambah penulisan ke journal di disk | Tulisan bertahan melewati restart mongod |
Opsi j: true (jurnal) melengkapi w: konfirmasi baru keluar setelah perubahan tercatat pada journal WiredTiger di disk. Adapun wtimeout membatasi lama penantian — misalnya { w: "majority", wtimeout: 2000 } — dan bila waktu habis, operasi dilaporkan gagal meskipun tulisan mungkin tetap terjadi; nilai 0 berarti menunggu tanpa batas.
w: 0 Fire-and-Forget #
w: 0 membalik keseluruhan logika: client tidak menunggu balasan sama sekali. Perhatikan bentuk hasilnya:
db.buku.insertOne({ judul: "Working Effectively with Legacy Code", penulis: "Michael Feathers", harga: 130000 }, { writeConcern: { w: 0 } })
db.buku.updateOne({ judul: "Refactoring" }, { $set: { stok: 7 } }, { writeConcern: { w: 0 } })
db.buku.findOne({ judul: "Refactoring" }, { judul: 1, stok: 1 })
db.buku.deleteOne({ judul: "Refactoring" }, { writeConcern: { w: 0 } })
db.buku.countDocuments({ judul: "Refactoring" })Output yang Dihasilkan:
{
acknowledged: false,
insertedId: ObjectId('6ad3883c5243d127e3e5258f')
}
{
acknowledged: false,
insertedId: null,
matchedCount: undefined,
modifiedCount: undefined,
upsertedCount: 0
}
{
_id: ObjectId('6ad3883c5243d127e3e5258e'),
judul: 'Refactoring',
stok: 7
}
{ acknowledged: false, deletedCount: undefined }
0Baca baris demi baris: hasil w: 0 berisi acknowledged: false, dan setiap angka yang seharusnya diisi server menjadi undefined — matchedCount, modifiedCount, deletedCount. Field insertedId tetap terisi karena _id dibuat di sisi klien. Buktinya ada di tengah: findOne() menemukan Refactoring dengan stok: 7 dan countDocuments() akhirnya melapor 0 — ketiga perintah fire-and-forget itu benar-benar dieksekusi, hanya saja client tidak pernah menunggu kabar hasilnya.
Balasan Mentah Server #
Tipe-tipe hasil di atas adalah bungkusan mongosh. Bentuk asli balasan server bisa dilihat lewat runCommand() — inilah “WriteResult” di tingkat protokol:
db.runCommand({ update: "buku", updates: [{ q: { judul: "Laskar Pelangi" }, u: { $set: { harga: 93000 } } }] })Output yang Dihasilkan:
{
n: 1,
electionId: ObjectId('7fffffff0000000000000008'),
opTime: { ts: Timestamp({ t: 1792247868, i: 7 }), t: Long('8') },
nModified: 1,
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1792247868, i: 7 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1792247868, i: 7 })
}Dua field intinya langsung tersambung ke yang sudah kita kenal: n: 1 menghitung document yang tersentuh (padanan matchedCount ditambah hasil upsert), dan nModified: 1 menghitung yang benar-benar berubah — kembaran modifiedCount. Field sisanya — electionId, opTime, $clusterTime — adalah metadata replikasi yang dipakai driver untuk menjaga sesi tetap konsisten.
Sebagai penutup, bersihkan database latihan:
db.dropDatabase()Output yang Dihasilkan:
{ ok: 1, dropped: 'latihan_crud2' }Rangkuman #
- Setiap method tulis mengembalikan tipe hasil tetap;
acknowledgedadalah gerbangnya — tanpanya angka lain tidak berarti. - Standalone memakai default implisit
w: 1; replica set baru memakaiw: "majority"— diverifikasi dirs0lewatgetDefaultRWConcern. wmengatur jumlah anggota yang mengonfirmasi,j: truemenambah jaminan journal di disk,wtimeoutmembatasi lama penantian.w: 0tidak menunggu balasan: hasil berisiundefineddan galat tenggelam — hanya untuk data yang boleh raib.- Balasan mentah server (
n,nModified) adalah sumber dari angka-angka yang dibungkusUpdateResult.
Section CRUD tuntas: insert, query, update, dan delete beserta jaminan penulisannya. Perjalanan berikutnya menyusun data dari sisi pembacaan — aggregation pipeline.