deleteOne, deleteMany — xavfli holatlar

~10 daq · MongoDB · CRUD

Muammodan boshlaylik

O'chirish — CRUD ning eng xavfli operatsiyasi. Qo'shish xato bo'lsa — ortiqcha yozuv qoladi, uni o'chirish mumkin. Yangilash xato bo'lsa — eski qiymatni qaytarish mumkin. Lekin o'chirish xato bo'lsa — ma'lumot yo'qoladi, va ko'pincha qaytarib bo'lmaydi.

Real kompaniyalarda production serverida hech kim db.users.deleteMany({}) kabi buyruqlarni ishlatmaydi. Buning uchun alohida himoya mexanizmlari bor. Bu darsda shu xavflarni ko'rsatamiz.


deleteOne — bitta document o'chirish

// _id bo'yicha o'chirish — eng xavfsiz usul
db.users.deleteOne({ _id: ObjectId("507f1f77bcf86cd799439011") })

// Boshqa filter bilan — birinchi mos kelganini o'chiradi
db.postlar.deleteOne({ status: "spam" })

Qaytarilgan natija

let natija = db.users.deleteOne({ _id: id })

// Topildi va o'chirildi:
{ acknowledged: true, deletedCount: 1 }

// Topilmadi:
{ acknowledged: true, deletedCount: 0 }

deletedCount: 0 — xato emas. Document shunchaki topilmadi. O'chirilganini tekshirish uchun bu qiymatni tekshiring.


deleteMany — bir nechta document o'chirish

// 30 kundan eski sessiyalarni o'chirish
let otmish = new Date()
otmish.setDate(otmish.getDate() - 30)

db.sessiyalar.deleteMany({
  yaratilgan: { $lt: otmish }
})

// Faol bo'lmagan foydalanuvchilarni o'chirish
db.users.deleteMany({
  faol: false,
  oxirgi_kirish: { $lt: otmish }
})
let natija = db.users.deleteMany({ faol: false })
// { acknowledged: true, deletedCount: 47 }

Eng xavfli holat — filtrsiz deleteMany

// ❌ HECH QACHON PRODUCTION DA QILMANG
db.users.deleteMany({})
// Barcha foydalanuvchilar o'chirildi — qaytarib bo'lmaydi

SQL da ham xuddi shunday xavf bor:

-- SQL da ham xuddi shunday
DELETE FROM users;  -- barcha yozuvlar o'chadi (WHERE yo'q)
MongoDB va SQL ikkalasida ham qoidasi bir xil:
  deleteMany({}) == DELETE FROM table;
  Ikkalasi ham halokatli.

⚠️ Qoida: deleteMany dan oldin doim find bilan tekshiring — nechta document o'chirilishini ko'ring. Keyin o'chiring.

// ✅ Oldin tekshirish
let son = db.users.countDocuments({ faol: false })
console.log(`O'chiriladigan: ${son} ta`)  // 47 ta deb chiqdi

// Tasdiqlangandan keyin o'chirish
db.users.deleteMany({ faol: false })

findOneAndDelete — yuqorida ham ko'rdik

O'chirish va o'chirilgan documentni bir vaqtda olish uchun:

let o_chirilgan = await db.vaqtinchalik_fayllar.findOneAndDelete(
  { _id: fileId },
  { projection: { yo_l: 1, hajm: 1 } }
)

if (o_chirilgan) {
  // Diskdan ham o'chirish
  await fs.unlink(o_chirilgan.yo_l)
}

Soft Delete — professional yondashuv

Real loyihalarda deleteOne / deleteMany kamdan-kam ishlatiladi. Buning o'rniga soft delete — "o'chirilgan deb belgilash" usuli qo'llanadi.

Tasavvur qiling: foydalanuvchi akkauntini o'chirdi. Agar haqiqatan o'chirsangiz:

  • Uning buyurtmalari, sharhlari "quvurdan" chiqib ketadi
  • Ma'muriy hisobotlar buziladi
  • Foydalanuvchi qayta kirmoqchi bo'lsa — yo'qolgan

Soft delete — documentni o'chirish o'rniga deleted: true va deletedAt: new Date() qo'yadi.

// ❌ Haqiqatan o'chirish — xavfli
db.users.deleteOne({ _id: userId })

// ✅ Soft delete — professional usul
db.users.updateOne(
  { _id: userId },
  {
    $set: {
      deleted: true,
      deletedAt: new Date()
    }
  }
)
// Soft delete bilan barcha so'rovlarda filter qo'shish kerak
db.users.find({ deleted: { $ne: true } })    // o'chirilmaganlar

// Yoki index bilan
db.users.find({ deleted: false })

Soft delete ning afzalliklari:

  • Ma'lumot yo'qolmaydi — audit, hisobot, tiklanish uchun qoladi
  • O'chirilgan narsani tiklash oson
  • Tegishli ma'lumotlar (buyurtmalar, sharhlar) buzilmaydi

Kamchiliklari:

  • Har bir so'rovga deleted: false filter qo'shishni unutmaslik kerak
  • Collection hajmi oshadi
  • Index lar murakkablashadi

💡 Maslahat: Mongoose da mongoose-delete plugin'i soft delete ni avtomatik boshqaradi — 57-darsda ko'ramiz.


dropCollection — collection ni butunlay o'chirish

// Collection ni butunlay o'chirish (barcha document + index lar)
db.vaqtinchalik.drop()

⚠️ drop() — o'chirilgan collection qaytarib bo'lmaydi. Indexlar ham yo'qoladi.

SQL bilan:

-- SQL
DROP TABLE vaqtinchalik;    -- jadval butunlay o'chadi
TRUNCATE TABLE vaqtinchalik; -- faqat ma'lumotlar o'chadi, jadval qoladi

MongoDB da:

db.vaqtinchalik.drop()           // collection butunlay o'chadi
db.vaqtinchalik.deleteMany({})   // ma'lumotlar o'chadi, collection qoladi

Tez-tez uchraydigan xatolar

deletedCount tekshirilmaydi

// ❌ O'chirilganmi yo'qmi bilmaymiz
await db.users.deleteOne({ _id: userId })

// ✅ Tekshirish
let natija = await db.users.deleteOne({ _id: userId })
if (natija.deletedCount === 0) {
  throw new Error("Foydalanuvchi topilmadi")
}

String _id bilan o'chirish

// ❌ String sifatida berish — topilmaydi
db.users.deleteOne({ _id: "507f1f77bcf86cd799439011" })
// deletedCount: 0 — o'chirmadi

// ✅ ObjectId ga o'tkazish
const { ObjectId } = require("mongodb")
db.users.deleteOne({ _id: new ObjectId("507f1f77bcf86cd799439011") })

deleteMany — filter maydoni yo'q documentlar

// ❌ Agar ba'zi documentlarda "faol" maydoni umuman yo'q bo'lsa
db.users.deleteMany({ faol: false })
// Faqat faol: false bo'lganlar o'chadi
// faol maydoni yo'q documentlar qolib ketadi

// ✅ $ne — false yoki yo'q bo'lgan hammasi
db.users.deleteMany({ faol: { $ne: true } })
// faol: false ham, faol yo'q ham o'chadi

Xulosa

  • deleteOne — faqat birinchi mos kelganini o'chiradi
  • deleteMany — barcha moslarini o'chiradi — doim filter tekshiring
  • deleteMany({}) — halokatli, production da hech qachon ishlatmang
  • Soft delete — real loyihalarda delete o'rniga deleted: true qo'yish professional yondashuv
  • O'chirishdan oldin countDocuments bilan tekshiring

Keyingi dars: 12-dars — bulkWrite — bir nechta operatsiyani birga bajarish