Pada Chapter updateOne() kita sudah melihat galat Update document requires atomic operators: MongoDB menolak dokumen update polos dan menuntut perubahan dinyatakan lewat operator. Operator-operator inilah kosakata kerja seluruh operasi update — $set menulis, $inc menghitung, $push menambah anggota array, dan seterusnya. Chapter ini memetakan semuanya sekaligus menguji yang paling penting di collection buku toko kita.

Operator Field #

Tujuh operator ini bekerja pada field tunggal — membuat, menghapus, atau mengubah nilainya:

OperatorTugasContoh
$setMengisi atau mengganti nilai field{ $set: { harga: 89000 } }
$unsetMenghapus field dari document{ $unset: { stok: "" } }
$incMenambah (atau mengurangi) nilai numerik{ $inc: { stok: -3 } }
$mulMengalikan nilai numerik{ $mul: { harga: 1.1 } }
$minMenulis hanya bila nilai baru lebih kecil{ $min: { harga: 120000 } }
$maxMenulis hanya bila nilai baru lebih besar{ $max: { rating: 5.0 } }
$renameMengganti nama field{ $rename: { kategori: "genre" } }

Seperti biasa, kita mulai dari data contoh yang bersih:

js
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:

text
switched to db latihan_crud2
true
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad3883195780b821157a833'),
    '1': ObjectId('6ad3883195780b821157a834'),
    '2': ObjectId('6ad3883195780b821157a835'),
    '3': ObjectId('6ad3883195780b821157a836'),
    '4': ObjectId('6ad3883195780b821157a837')
  }
}

$set dan $unset adalah pasangan yang paling sering dipakai — satu menulis, satu menghapus. Nilai pada $unset diabaikan; string kosong "" hanyalah konvensi:

js
db.buku.updateOne({ judul: "Laskar Pelangi" }, { $set: { harga: 89000 }, $unset: { stok: "" } })
db.buku.findOne({ judul: "Laskar Pelangi" })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a833'),
  judul: 'Laskar Pelangi',
  penulis: 'Andrea Hirata',
  harga: 89000,
  kategori: 'Fiksi',
  tag: [ 'novel', 'bestseller' ],
  rating: 4.6
}

Hasilnya: harga berubah menjadi 89000 dan field stok tidak lagi ada. $inc bekerja pada nilai numerik — dan bila field belum ada, $inc membuatnya mulai dari nol:

js
db.buku.updateOne({ judul: "Laskar Pelangi" }, { $inc: { terjual: 10 } })
db.buku.findOne({ judul: "Laskar Pelangi" }, { judul: 1, harga: 1, terjual: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a833'),
  judul: 'Laskar Pelangi',
  harga: 89000,
  terjual: 10
}

$mul mengalikan nilai yang ada dengan angka pengali. Cobalah menaikkan harga 10%:

js
db.buku.updateOne({ judul: "Clean Code" }, { $mul: { harga: 1.1 } })
db.buku.findOne({ judul: "Clean Code" }, { judul: 1, harga: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a837'),
  judul: 'Clean Code',
  harga: 181500.00000000003
}

Dua operator perbandingan melengkapi kelompok ini. $min hanya menulis bila nilai baru lebih kecil dari nilai lama; $max kebalikannya. Pola ini pas untuk menjaga “rekor” — harga promo tidak menimpa harga yang sudah lebih murah:

js
db.buku.updateOne({ judul: "Atomic Habits" }, { $min: { harga: 120000 }, $max: { rating: 4.9 } })
db.buku.findOne({ judul: "Atomic Habits" }, { judul: 1, harga: 1, rating: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a835'),
  judul: 'Atomic Habits',
  harga: 120000,
  rating: 4.9
}

Harga 120000 lebih kecil dari 135000 sehingga ditulis; rating 4.9 lebih besar dari 4.7 sehingga ikut naik. Terakhir, $rename memindahkan nilai ke nama field baru — field _id dan array yang dijadikan sumber index tidak boleh di-rename:

js
db.buku.updateOne({ judul: "Filosofi Teras" }, { $rename: { kategori: "genre" } })
db.buku.findOne({ judul: "Filosofi Teras" }, { judul: 1, genre: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a836'),
  judul: 'Filosofi Teras',
  genre: 'Pengembangan Diri'
}

Operator Array #

Lima operator ini mengelola isi array — pada data kita, array tag:

OperatorTugas
$pushMenambah elemen ke akhir array — duplikat diperbolehkan
$addToSetMenambah elemen hanya bila belum ada di array
$popMembuang elemen pertama (-1) atau terakhir (1)
$pullMembuang semua elemen yang cocok dengan nilai atau kondisi
$pullAllMembuang semua elemen yang tercantum pada daftar

$push menempel satu nilai ke akhir array; dengan modifier $each, ia menempel banyak nilai sekaligus:

js
db.buku.updateOne({ judul: "Bumi Manusia" }, { $push: { tag: "sastra" } })
db.buku.updateOne({ judul: "Bumi Manusia" }, { $push: { tag: { $each: ["kampus", "buku-wajib"] } } })
db.buku.findOne({ judul: "Bumi Manusia" }, { judul: 1, tag: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a834'),
  judul: 'Bumi Manusia',
  tag: [ 'novel', 'klasik', 'sastra', 'kampus', 'buku-wajib' ]
}

$push juga menerima $sort dan $slice sebagai modifier — kombinasi yang umum untuk menjaga “N nilai teratas”. Di bawah ini seluruh isi tag diurutkan alfabetis lalu dipangkas menjadi tiga:

js
db.buku.updateOne({ judul: "Bumi Manusia" }, { $push: { tag: { $each: ["sekolah", "budaya"], $sort: 1, $slice: 3 } } })
db.buku.findOne({ judul: "Bumi Manusia" }, { judul: 1, tag: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a834'),
  judul: 'Bumi Manusia',
  tag: [ 'budaya', 'buku-wajib', 'kampus' ]
}

Sekarang perbedaan yang paling sering menjebak: $push tidak memeriksa duplikat, sedangkan $addToSet memeriksa:

js
db.buku.updateOne({ judul: "Atomic Habits" }, { $push: { tag: "bestseller" } })
db.buku.updateOne({ judul: "Atomic Habits" }, { $addToSet: { tag: "bestseller" } })
db.buku.updateOne({ judul: "Atomic Habits" }, { $addToSet: { tag: { $each: ["bestseller", "manajemen-diri"] } } })
db.buku.findOne({ judul: "Atomic Habits" }, { judul: 1, tag: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 0,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a835'),
  judul: 'Atomic Habits',
  tag: [ 'nonfiksi', 'bestseller', 'bestseller', 'manajemen-diri' ]
}

Bacalah tiga hasil di atas secara berurutan: $push menambah bestseller kedua tanpa protes (modifiedCount: 1), $addToSet dengan nilai yang sudah ada tidak mengubah apa pun (modifiedCount: 0), dan $addToSet dengan $each hanya memasukkan manajemen-diri yang memang baru.

Untuk membuang elemen, $pop memilih berdasarkan posisi — -1 untuk pertama, 1 untuk terakhir:

js
db.buku.updateOne({ judul: "Bumi Manusia" }, { $pop: { tag: 1 } })
db.buku.updateOne({ judul: "Bumi Manusia" }, { $pop: { tag: -1 } })
db.buku.findOne({ judul: "Bumi Manusia" }, { judul: 1, tag: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a834'),
  judul: 'Bumi Manusia',
  tag: [ 'buku-wajib' ]
}

$pop: 1 membuang kampus (posisi terakhir), lalu $pop: -1 membuang budaya (posisi pertama). Sementara itu, $pull dan $pullAll membuang berdasarkan isi: $pull menerima nilai eksak maupun kondisi query, dan menghapus semua kemunculannya; $pullAll hanya menerima daftar nilai eksak:

js
db.buku.updateOne({ judul: "Atomic Habits" }, { $pull: { tag: "bestseller" } })
db.buku.updateOne({ judul: "Atomic Habits" }, { $pullAll: { tag: ["nonfiksi", "manajemen-diri"] } })
db.buku.findOne({ judul: "Atomic Habits" }, { judul: 1, tag: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883195780b821157a835'),
  judul: 'Atomic Habits',
  tag: []
}

Satu $pull menghapus kedua salinan bestseller sekaligus — perilaku “semua yang cocok” ini sejajar dengan semangat updateMany().

Operator Khusus: $setOnInsert, $, dan $[] #

Tiga operator penutup mengisi ceruk tertentu. $setOnInsert hanya bekerja ketika opsi upsert: true benar-benar menyisipkan document baru — sempurna untuk field yang cukup diisi sekali seumur hidup data:

js
db.buku.updateOne(
  { judul: "Buku Hitung Stok" },
  { $set: { kategori: "Referensi" }, $setOnInsert: { dibuatPada: "2026-10-17" } },
  { upsert: true }
)
db.buku.updateOne(
  { judul: "Buku Hitung Stok" },
  { $set: { kategori: "Referensi" }, $setOnInsert: { dibuatPada: "TIDAK BERLAKU" } },
  { upsert: true }
)
db.buku.findOne({ judul: "Buku Hitung Stok" })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: ObjectId('6ad388329d08d4e3fde1a3af'),
  matchedCount: 0,
  modifiedCount: 0,
  upsertedCount: 1
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 0,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad388329d08d4e3fde1a3af'),
  judul: 'Buku Hitung Stok',
  dibuatPada: '2026-10-17',
  kategori: 'Referensi'
}

Panggilan kedua menemukan document yang sudah ada, sehingga $setOnInsert: { dibuatPada: "TIDAK BERLAKU" } diabaikan sama sekali — nilai 2026-10-17 dari penyisipan pertama tetap bertahan.

Dua operator terakhir menunjuk posisi elemen array. Operator positional $ merujuk elemen pertama yang cocok dengan filter — pada contoh berikut, ulasan milik Siti:

js
db.buku.insertOne({ judul: "Rumah Kaca", penulis: "Pramoedya Ananta Toer", harga: 98000, stok: 4, ulasan: [ { pengguna: "Budi", bintang: 5 }, { pengguna: "Siti", bintang: 4 } ] })
db.buku.updateOne({ judul: "Rumah Kaca", "ulasan.pengguna": "Siti" }, { $set: { "ulasan.$.teks": "Petualangan yang memikat" } })
db.buku.findOne({ judul: "Rumah Kaca" }, { judul: 1, ulasan: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: ObjectId('6ad3883295780b821157a838')
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883295780b821157a838'),
  judul: 'Rumah Kaca',
  ulasan: [
    { pengguna: 'Budi', bintang: 5 },
    { pengguna: 'Siti', bintang: 4, teks: 'Petualangan yang memikat' }
  ]
}

Filter "ulasan.pengguna": "Siti" menandai elemen kedua; ulasan.$ lalu merujuk tepat ke elemen itu. Adapun $[] menunjuk semua elemen array sekaligus:

js
db.buku.updateOne({ judul: "Rumah Kaca" }, { $set: { "ulasan.$[].diverifikasi": true } })
db.buku.findOne({ judul: "Rumah Kaca" }, { judul: 1, ulasan: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3883295780b821157a838'),
  judul: 'Rumah Kaca',
  ulasan: [
    { pengguna: 'Budi', bintang: 5, diverifikasi: true },
    {
      pengguna: 'Siti',
      bintang: 4,
      teks: 'Petualangan yang memikat',
      diverifikasi: true
    }
  ]
}

Untuk seleksi elemen yang lebih bebas, MongoDB menyediakan arrayFilters sebagai opsi tambahan — di luar cakupan Section ini, tetapi pola $ dan $[] di atas sudah menutup kebutuhan paling umum. Terakhir, bersihkan database latihan:

js
db.dropDatabase()

Output yang Dihasilkan:

text
{ ok: 1, dropped: 'latihan_crud2' }

Rangkuman #

  • Operator field: $set menulis, $unset menghapus, $inc menambah (mulai dari 0 bila field baru), $mul mengalikan, $min/$max menjaga batas, $rename memindahkan nama.
  • $mul dengan pecahan menghasilkan double — waspadai untuk nilai uang.
  • $push membiarkan duplikat; $addToSet menolak nilai yang sudah ada — pilih sesuai makna array.
  • $pop membuang berdasarkan posisi; $pull (nilai/kondisi, semua kemunculan) dan $pullAll (daftar nilai) membuang berdasarkan isi.
  • $setOnInsert hanya berlaku saat upsert menyisipkan; $ menunjuk elemen yang cocok filter, $[] menunjuk semua elemen.

Kosakata update lengkap. Chapter berikutnya menempuh jalur berbeda untuk kasus “ganti total”: replaceOne().