Diskon akhir tahun tiba dan manajemen toko buku memutuskan seluruh novel fiksi turun harga Rp10.000. Mengubah document satu per satu lewat updateOne() tentu mungkin, tetapi memaksa kita menulis perintah sebanyak jumlah buku yang terkena kebijakan. Method updateMany(filter, update) menyelesaikannya dengan satu perintah: semua document yang cocok dengan filter diubah sekaligus.

Semua yang Cocok Ikut Berubah #

updateMany() memakai bentuk argument yang sama persis dengan updateOne() — filter lalu dokumen update — bedanya hanya pada cakupan: updateOne() berhenti setelah document pertama yang cocok, sedangkan updateMany() meneruskan ke seluruhnya. Kita siapkan dulu data contoh yang sama seperti Chapter sebelumnya:

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('6ad3882f7d5975d32134a543'),
    '1': ObjectId('6ad3882f7d5975d32134a544'),
    '2': ObjectId('6ad3882f7d5975d32134a545'),
    '3': ObjectId('6ad3882f7d5975d32134a546'),
    '4': ObjectId('6ad3882f7d5975d32134a547')
  }
}

Sekarang jalankan diskon massal itu — operator $inc dengan nilai negatif menurunkan harga sebesar Rp10.000:

js
db.buku.updateMany({ kategori: "Fiksi" }, { $inc: { harga: -10000 } })
db.buku.find({ kategori: "Fiksi" }, { judul: 1, harga: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 2,
  modifiedCount: 2,
  upsertedCount: 0
}
[
  {
    _id: ObjectId('6ad3882f7d5975d32134a543'),
    judul: 'Laskar Pelangi',
    harga: 85000
  },
  {
    _id: ObjectId('6ad3882f7d5975d32134a544'),
    judul: 'Bumi Manusia',
    harga: 100000
  }
]

Mari kita bedah hasilnya:

  1. matchedCount: 2 — dua document berkategori Fiksi cocok dengan filter: Laskar Pelangi dan Bumi Manusia.
  2. modifiedCount: 2 — keduanya berubah; $inc: { harga: -10000 } menurunkan 95000 menjadi 85000 dan 110000 menjadi 100000.
  3. find() dengan projection memastikan hasilnya; document kategori lain tidak tersentuh.

Bila filter tidak menemukan satu pun document, hasilnya sama seperti pada updateOne() — bukan galat:

js
db.buku.updateMany({ kategori: "Sejarah" }, { $inc: { harga: -5000 } })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 0,
  modifiedCount: 0,
  upsertedCount: 0
}

matchedCount: 0 menandakan tidak ada buku kategori Sejarah — perintah sukses tanpa efek. Sama seperti updateOne(), argument ketiga bisa menerima { upsert: true }: bila tidak ada kecocokan, updateMany() justru menyisipkan satu document baru dari gabungan filter dan update. Perilaku lengkapnya kita bahas di Chapter Upsert.

updateOne Hanya Menyentuh yang Pertama #

Perbedaan kedua method ini paling mudah dilihat lewat filter longgar yang mencocokkan banyak document. Kategori Pengembangan Diri berisi dua buku; bandingkan apa yang terjadi bila kita menambah stok lewat updateOne():

js
db.buku.updateOne({ kategori: "Pengembangan Diri" }, { $inc: { stok: 5 } })
db.buku.find({ kategori: "Pengembangan Diri" }, { judul: 1, stok: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
[
  {
    _id: ObjectId('6ad3882f7d5975d32134a545'),
    judul: 'Atomic Habits',
    stok: 25
  },
  {
    _id: ObjectId('6ad3882f7d5975d32134a546'),
    judul: 'Filosofi Teras',
    stok: 0
  }
]

Filter cocok dengan dua document, tetapi matchedCount melapor 1: hanya Atomic Habits (document pertama yang ditemukan) yang naik menjadi 25, sementara Filosofi Teras tetap 0. Jalankan perintah yang sama dengan updateMany():

js
db.buku.updateMany({ kategori: "Pengembangan Diri" }, { $inc: { stok: 5 } })
db.buku.find({ kategori: "Pengembangan Diri" }, { judul: 1, stok: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 2,
  modifiedCount: 2,
  upsertedCount: 0
}
[
  {
    _id: ObjectId('6ad3882f7d5975d32134a545'),
    judul: 'Atomic Habits',
    stok: 30
  },
  {
    _id: ObjectId('6ad3882f7d5975d32134a546'),
    judul: 'Filosofi Teras',
    stok: 5
  }
]

Ringkasan pilihan antara keduanya:

AspekupdateOne()updateMany()
Document yang diubahPaling banyak satu: yang pertama cocokSemua yang cocok filter
matchedCount maksimal1Sebanyak document yang cocok
Cocok untukSatu entitas spesifik (satu judul, satu _id)Kebijakan massal (diskon kategori, penyesuaian stok)

Dari sisi jaringan, satu updateMany() juga mengungguli loop updateOne() di sisi aplikasi: server mengerjakan seluruh kecocokan dalam satu perintah dengan satu balasan, alih-alih puluhan permintaan bolak-balik. Urutan kerja yang sehat untuk setiap perubahan massal pun sederhana: verifikasi cakupan lewat find() lebih dulu, lalu jalankan updateMany() dengan filter yang sama.

Filter Kosong Menyentuh Seluruh Collection #

Filter kosong {} berarti “semua document” — pola yang berguna untuk menambah field baru ke seluruh collection, misalnya menandai mata uang harga:

js
db.buku.updateMany({}, { $set: { mataUang: "IDR" } })
db.buku.findOne({}, { judul: 1, harga: 1, mataUang: 1 })

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 5,
  modifiedCount: 5,
  upsertedCount: 0
}
{
  _id: ObjectId('6ad3882f7d5975d32134a543'),
  judul: 'Laskar Pelangi',
  harga: 85000,
  mataUang: 'IDR'
}

Lima document kini memiliki field mataUang — $set membuat field baru bila field tersebut belum ada. Kekuatan ini sekaligus menjadi risikonya.

Terakhir, bersihkan database latihan:

js
db.dropDatabase()

Output yang Dihasilkan:

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

Rangkuman #

  • updateMany(filter, update) mengubah semua document yang cocok filter dalam satu perintah; argument-nya identik dengan updateOne().
  • matchedCount pada updateMany() bisa lebih dari satu — itulah indikator utama cakupan perubahan.
  • updateOne() dengan filter longgar hanya mengubah document pertama yang cocok.
  • Filter kosong {} menjangkau seluruh collection: ampuh untuk menambah field massal, berisiko bila tidak disengaja.
  • Tanpa kecocokan, hasil tetap sukses dengan matchedCount: 0 — bukan galat.

Dengan pola update sudah di tangan, Chapter berikutnya membedah kotak peralatannya secara utuh: seluruh update operators dari $set sampai $pull.