bulkWrite — bir nechta operatsiyani birga bajarish

~15 daq · MongoDB · CRUD

Muammodan boshlaylik

Tasavvur qiling: internet-do'kon. Bir foydalanuvchi 5 ta mahsulot sotib oldi. Siz quyidagilarni qilishingiz kerak:

  1. Har bir mahsulotning zaxirasini (soni) kamaytirish
  2. Buyurtmani buyurtmalar collection'iga qo'shish
  3. Foydalanuvchining xaridlar_soni ni oshirish

Buni loop bilan qilsangiz — 7 ta alohida so'rov. Har biri tarmoqdan o'tadi, server javob kutadi. 1000 ta buyurtma bir vaqtda kelsa — 7000 ta so'rov. Server cho'kadi.

bulkWrite — bularning barchasini bitta so'rovda yuboradi.


bulkWrite nima?

bulkWrite — turli xil operatsiyalarni (insert, update, delete) bir "paket" ga yig'ib, bitta so'rovda serverga yuborish.

Supermarket kassasiga o'xshang: 10 ta narsa olsangiz, har birini alohida to'lamasiz — hammani birga kassaga qo'yasiz, bir marta to'laysiz. bulkWrite ham shunday.

db.mahsulotlar.bulkWrite([
  { insertOne: { document: { nom: "Olma", narx: 5000 } } },
  { updateOne: { filter: { nom: "Nok" }, update: { $inc: { soni: -1 } } } },
  { deleteOne: { filter: { muddati: { $lt: new Date() } } } }
])

Operatsiya turlari

bulkWrite quyidagi operatsiyalarni qabul qiladi:

db.collection.bulkWrite([

  // 1. insertOne — bitta qo'shish
  { insertOne: { document: { nom: "Olma", narx: 5000 } } },

  // 2. updateOne — bitta yangilash
  { updateOne: {
      filter: { _id: id1 },
      update: { $set: { faol: true } },
      upsert: false         // ixtiyoriy
  }},

  // 3. updateMany — ko'p yangilash
  { updateMany: {
      filter: { kategoriya: "elektronika" },
      update: { $inc: { korishlar: 1 } }
  }},

  // 4. deleteOne — bitta o'chirish
  { deleteOne: { filter: { _id: id2 } } },

  // 5. deleteMany — ko'p o'chirish
  { deleteMany: { filter: { eskirgan: true } } },

  // 6. replaceOne — to'liq almashtirish
  { replaceOne: {
      filter: { _id: id3 },
      replacement: { nom: "Yangi nom", narx: 3000 },
      upsert: false
  }},

])

Amaliy misol — buyurtma qilish

async function buyurtmaQil(foydalanuvchiId, mahsulotlar) {
  // mahsulotlar: [{ id, miqdor, narx }, ...]

  const operatsiyalar = []

  // 1. Har bir mahsulot zaxirasini kamaytirish
  for (let mahsulot of mahsulotlar) {
    operatsiyalar.push({
      updateOne: {
        filter: {
          _id: mahsulot.id,
          soni: { $gte: mahsulot.miqdor }  // yetarli zaxira bormi
        },
        update: {
          $inc: { soni: -mahsulot.miqdor }   // kamaytiramiz
        }
      }
    })
  }

  // 2. Foydalanuvchi xaridlar sonini oshirish
  operatsiyalar.push({
    updateOne: {
      filter: { _id: foydalanuvchiId },
      update: { $inc: { xaridlar_soni: 1 } }
    }
  })

  let natija = await db.mahsulotlar.bulkWrite(operatsiyalar)
  return natija
}
Natija — 7 ta so'rov o'rniga 1 ta so'rov
  Tarmoq yuklamasi: 7x kamaydi
  Server javob kutish vaqti: 7x kamaydi

Qaytarilgan natija

let natija = await db.mahsulotlar.bulkWrite([...])

// natija:
{
  ok:              1,
  insertedCount:   2,      // qo'shilganlar
  upsertedCount:   1,      // upsert orqali yaratilganlar
  matchedCount:    5,      // filtrlarga mos keldi
  modifiedCount:   5,      // haqiqatan o'zgartirildi
  deletedCount:    3,      // o'chirildi

  insertedIds: { 0: ObjectId("..."), 1: ObjectId("...") },
  upsertedIds: { 2: ObjectId("...") }
}

ordered vs unordered

Bu — bulkWrite ning muhim parametri. Xato bo'lganda nima qilishini belgilaydi.

// ordered: true (default) — xato bo'lsa keyingilar bajarilmaydi
db.users.bulkWrite([
  { updateOne: { filter: { _id: id1 }, update: { $set: { a: 1 } } } },
  { updateOne: { filter: { _id: "xato_id" }, update: { $set: { b: 1 } } } },  // xato
  { updateOne: { filter: { _id: id3 }, update: { $set: { c: 1 } } } },  // bu bajarilmaydi
], { ordered: true })
// ordered: false — xato bo'lsa ham keyingilar davom etadi
db.users.bulkWrite([
  { updateOne: { filter: { _id: id1 }, update: { $set: { a: 1 } } } },
  { updateOne: { filter: { _id: "xato_id" }, update: { $set: { b: 1 } } } },  // xato
  { updateOne: { filter: { _id: id3 }, update: { $set: { c: 1 } } } },  // bu bajariladi
], { ordered: false })
ordered: true  → tartib muhim, biri xato bo'lsa to'xtaydi
ordered: false → biri xato bo'lsa, qolganlar davom etadi (tezroq)

Qachon qaysinisini tanlash?

ordered: true  — operatsiyalar bog'liq bo'lganda
                 (A qo'shilmasa, B ni yangilamaslik kerak)

ordered: false — operatsiyalar mustaqil bo'lganda
                 (1000 ta foydalanuvchi notifikatsiyasini yuborish —
                  biri xato bo'lsa, qolganlari to'xtamasin)

Performance — qachon bulkWrite kerak?

1-10 ta operatsiya  → oddiy update/insert yetarli
10-100 ta           → bulkWrite sezilarli tezlashtiradi
100+ ta             → bulkWrite majburiy
1000+ ta            → bulkWrite + batch larga bo'lish
// ✅ Katta ma'lumot uchun batch ga bo'lish
const BATCH_SIZE = 500

for (let i = 0; i < barcha.length; i += BATCH_SIZE) {
  const batch = barcha.slice(i, i + BATCH_SIZE)
  await db.mahsulotlar.bulkWrite(
    batch.map(m => ({
      updateOne: {
        filter: { _id: m.id },
        update: { $set: { yangilangan: new Date() } }
      }
    })),
    { ordered: false }   // tezroq
  )
}

Tez-tez uchraydigan xatolar

Bo'sh massiv yuborish

// ❌ operatsiyalar bo'sh bo'lib qolishi mumkin
let ops = []
// ... (ba'zi shartlarda hech narsa qo'shilmadi)
await db.users.bulkWrite(ops)  // MongoError: bulkWrite must contain at least 1 operation

// ✅ Tekshirish
if (ops.length > 0) {
  await db.users.bulkWrite(ops)
}

ordered: true da bir xato hammani to'xtatadi

// ❌ Birinchi xato — qolgan 999 ta bajarilmaydi
await db.users.bulkWrite(mingTaOperatsiya, { ordered: true })

// ✅ Mustaqil operatsiyalar uchun ordered: false
await db.users.bulkWrite(mingTaOperatsiya, { ordered: false })

Xato ushlanmaydi

// ❌ Xatoliklar e'tiborga olinmaydi
await db.users.bulkWrite(ops)

// ✅ Xatolarni ushlab, qaerda xato bo'lganini bilish
try {
  let natija = await db.users.bulkWrite(ops, { ordered: false })
  if (natija.hasWriteErrors()) {
    console.error("Ba'zi operatsiyalar bajarilmadi:", natija.getWriteErrors())
  }
} catch (err) {
  console.error("Kritik xato:", err.message)
}

Xulosa

  • bulkWrite — bir nechta operatsiyani bitta so'rovda yuboradi
  • 6 ta operatsiya turi: insertOne, updateOne, updateMany, deleteOne, deleteMany, replaceOne
  • ordered: true — xato bo'lsa to'xtaydi (bog'liq operatsiyalar uchun)
  • ordered: false — xato bo'lsa davom etadi (mustaqil operatsiyalar uchun, tezroq)
  • 10+ operatsiyada bulkWrite — sezilarli performance oshiradi
  • Bo'sh massiv yuborishdan oldin tekshiring

Keyingi dars: 13-dars — Embedding vs Referencing — qachon qaysinisi