updateOne, updateMany — $set, $unset, $inc, $push, $pull

~20 daq · MongoDB · CRUD

Muammodan boshlaylik

Foydalanuvchi parolini o'zgartirdi — faqat parol maydonini yangilash kerak. Mahsulot soni kamaydi — soni ni 1 ga kamaytirish kerak. Foydalanuvchi yangi hobby qo'shdi — hobbylar massiviga element qo'shish kerak.

Bularning barchasi uchun MongoDB da update operatorlari bor. $set, $unset, $inc, $push, $pull — har biri o'z vazifasiga ega.


update — asosiy tuzilish

db.<collection>.updateOne(<filter>, <update>, <options>)
                             ↑           ↑
                        Qaysinisi    Nima qilish

Update ikki qismdan iborat:

  1. Filter — qaysi documentni yangilash (8-darsda o'rgandik)
  2. Update — nima o'zgartirilsin (bu darsning asosi)

updateOne vs updateMany

updateOne   → filtrdagi BIRINCHI documentni yangilaydi
updateMany  → filtrdagi BARCHA documentlarni yangilaydi
// Bitta — Jasur degan birinchi foydalanuvchi
db.users.updateOne(
  { ism: "Jasur" },
  { $set: { faol: false } }
)

// Ko'p — barcha Toshkentliklar
db.users.updateMany(
  { shahar: "Toshkent" },
  { $set: { viloyat: "Toshkent shahri" } }
)

SQL bilan:

-- SQL — WHERE bo'lmasa hammasi o'zgaradi (xavfli!)
UPDATE users SET faol = false WHERE ism = 'Jasur' LIMIT 1;
UPDATE users SET viloyat = 'Toshkent shahri' WHERE shahar = 'Toshkent';

Qaytarilgan natija

let natija = db.users.updateOne(
  { ism: "Jasur" },
  { $set: { faol: false } }
)
natija:
{
  acknowledged:  true,
  matchedCount:  1,    ← filtriga mos keldi
  modifiedCount: 1,    ← haqiqatan o'zgartirildi
  upsertedId:    null  ← upsert bo'lmadi (10-darsda)
}
matchedCount vs modifiedCount:

  matchedCount:  1  }  Bir xil bo'lishi mumkin
  modifiedCount: 1  }

  Lekin:
  matchedCount:  1  }  Topildi, lekin qiymat allaqachon bir xil edi
  modifiedCount: 0  }  — o'zgartirilmadi

$set — maydon qiymatini o'rnatish

Eng ko'p ishlatiladigan operator. Mavjud maydonni yangilaydi yoki yangi maydon qo'shadi.

// Mavjud maydonni yangilash
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $set: { yosh: 25 } }         // yosh: 24 → yosh: 25
)

// Yangi maydon qo'shish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $set: { sinf: "11A" } }      // sinf maydoni yo'q edi → qo'shildi
)

// Bir nechta maydon bir vaqtda
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $set: { yosh: 25, sinf: "11A", shahar: "Andijon" } }
)
OLDIN:  { ism: "Jasur", yosh: 24, shahar: "Toshkent" }
                              ↓ $set: { yosh: 25, sinf: "11A" }
KEYIN:  { ism: "Jasur", yosh: 25, shahar: "Toshkent", sinf: "11A" }
                              ↑                            ↑
                         yangilandi                   qo'shildi

Ichki maydonni $set bilan yangilash

// manzil.shahar ni yangilash — butun manzil emas, faqat shahar
db.users.updateOne(
  { ism: "Jasur" },
  { $set: { "manzil.shahar": "Andijon" } }  // ← dot notation
)

// ❌ Bu butun manzil ob'ektini almashtiradi
db.users.updateOne(
  { ism: "Jasur" },
  { $set: { manzil: { shahar: "Andijon" } } }
  // tuman, ko'cha — yo'qoladi!
)

$unset — maydonni o'chirish

// sinf maydonini o'chirish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $unset: { sinf: "" } }   // qiymat ahamiyatsiz — "" yoki 1 yoki true
)
OLDIN:  { ism: "Jasur", yosh: 25, sinf: "11A" }
                                       ↓ $unset: { sinf: "" }
KEYIN:  { ism: "Jasur", yosh: 25 }
                                  sinf yo'q bo'ldi
// Bir nechta maydonni birga o'chirish
db.users.updateOne(
  { ism: "Jasur" },
  { $unset: { vaqtinchalik_kod: "", muddati: "" } }
)

SQL bilan (to'g'ridan-to'g'ri analog yo'q, NULL qo'yamiz):

-- SQL
UPDATE users SET sinf = NULL WHERE ism = 'Jasur';
-- SQL da ustunni o'chira olmaymiz, NULL qilamiz
// MongoDB da maydon butunlay yo'qoladi — NULL ham qolmaydi
db.users.updateOne({ ism: "Jasur" }, { $unset: { sinf: "" } })

$inc — raqamni oshirish yoki kamaytirish

// Yoshni 1 ga oshirish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $inc: { yosh: 1 } }     // yosh = yosh + 1
)

// Ko'rishlar sonini 1 ga oshirish (view counter)
db.postlar.updateOne(
  { _id: postId },
  { $inc: { korishlar: 1 } }
)

// Balansdan 5000 ayirish
db.hisoblar.updateOne(
  { foydalanuvchi_id: userId },
  { $inc: { balans: -5000 } }   // manfiy qiymat — kamaytiradi
)

// Bir nechta maydon bir vaqtda
db.o_yinlar.updateOne(
  { o_yinchi_id: id },
  { $inc: { ball: 10, darajalar: 1 } }
)
OLDIN:  { ism: "Jasur", yosh: 24, korishlar: 142 }
                              ↓ $inc: { yosh: 1, korishlar: 1 }
KEYIN:  { ism: "Jasur", yosh: 25, korishlar: 143 }

SQL bilan:

UPDATE postlar SET korishlar = korishlar + 1 WHERE id = ?;
// MongoDB
db.postlar.updateOne({ _id: id }, { $inc: { korishlar: 1 } })

$inc — $set: { korishlar: 143 } dan yaxshiroq, chunki $inc atomik. Ikki server bir vaqtda korishlar ni o'qib, ikkalasi ham 143 yozsa — bir oshirish yo'qoladi. $inc esa MongoDB serverida atomik bajariladi.


$push — massivga element qo'shish

// Hobby qo'shish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $push: { hobbylar: "shaxmat" } }
)
OLDIN:  { hobbylar: ["dasturlash", "futbol"] }
                           ↓ $push: { hobbylar: "shaxmat" }
KEYIN:  { hobbylar: ["dasturlash", "futbol", "shaxmat"] }
                                                  ↑ qo'shildi

$push — bir nechta element

// $each bilan bir nechta qo'shish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $push: { hobbylar: { $each: ["kitob", "musiqa"] } } }
)

$push + $sort + $slice — tartiblab, soni cheklang

// So'nggi 5 ta xabarni saqlash (eski o'chib ketadi)
db.users.updateOne(
  { _id: userId },
  {
    $push: {
      bildirishnomalar: {
        $each:  [{ matn: "Yangi xabar", vaqt: new Date() }],
        $sort:  { vaqt: -1 },   // yangilarni boshiga
        $slice: 5               // faqat 5 tasini saqlaydi
      }
    }
  }
)
Oldin: [B5, B4, B3, B2, B1]  (5 ta bor)
Yangi: B6 qo'shildi
       [B6, B5, B4, B3, B2, B1] → sort + slice(5) →
Keyin: [B6, B5, B4, B3, B2]   (B1 tushib ketdi)

$addToSet — massivga faqat unikal qo'shish

// Agar "dasturlash" allaqachon bor bo'lsa — qo'shilmaydi
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $addToSet: { hobbylar: "dasturlash" } }
)
$push vs $addToSet:

  hobbylar: ["dasturlash", "futbol"]

  $push:      → ["dasturlash", "futbol", "dasturlash"]  ← dublikat!
  $addToSet:  → ["dasturlash", "futbol"]                ← o'zgarmadi

Teglar, kategoriyalar, rollar kabi unikal bo'lishi kerak bo'lgan massivlarda $addToSet ishlatiladi.


$pull — massivdan element o'chirish

// "futbol" hobby ni o'chirish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $pull: { hobbylar: "futbol" } }
)
OLDIN:  { hobbylar: ["dasturlash", "futbol", "shaxmat"] }
                                       ↓ $pull: { hobbylar: "futbol" }
KEYIN:  { hobbylar: ["dasturlash", "shaxmat"] }

$pull — shart bilan o'chirish

// Bahosi 60 dan past bo'lgan baholarni o'chirish
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $pull: { baholar: { $lt: 60 } } }
)

// Ob'ekt massividan shart bilan o'chirish
db.users.updateOne(
  { _id: userId },
  { $pull: { manzillar: { shahar: "Toshkent" } } }
)

$pop — massivdan boshidan yoki oxiridan o'chirish

// Oxirgi elementni o'chirish
db.users.updateOne({ _id: id }, { $pop: { telefon: 1 } })

// Birinchi elementni o'chirish
db.users.updateOne({ _id: id }, { $pop: { telefon: -1 } })

$rename — maydon nomini o'zgartirish

// "ism" ni "name" ga o'zgartirish
db.users.updateMany(
  {},
  { $rename: { "ism": "name" } }
)

Migration paytida foydali — eski nomdan yangisiga o'tish.


updateMany — barcha documentlarni yangilash

// Barcha talabalarning "yil" maydonini qo'shish
db.talabalar.updateMany(
  {},                               // ← barcha documentlar
  { $set: { yil: 2024 } }
)

// Faqat 10-sinf talabalariga sinf_raqam qo'shish
db.talabalar.updateMany(
  { sinf: /^10/ },                  // sinfi "10" bilan boshlanadi
  { $set: { sinf_raqam: 10 } }
)

// Barcha mahsulot narxini 10% oshirish
db.mahsulotlar.updateMany(
  { faol: true },
  { $mul: { narx: 1.10 } }          // $mul — ko'paytirish
)

SQL bilan:

UPDATE talabalar SET yil = 2024;
UPDATE mahsulotlar SET narx = narx * 1.10 WHERE faol = TRUE;

$mul — ko'paytirish

// Narxni 1.1 ga ko'paytirish (10% qimmatlashtirish)
db.mahsulotlar.updateMany(
  {},
  { $mul: { narx: 1.10 } }
)

// Narxni 0.9 ga ko'paytirish (10% chegirma)
db.mahsulotlar.updateMany(
  { chegirma: true },
  { $mul: { narx: 0.90 } }
)

Bir nechta operator birgalikda

Bir update ichida bir nechta operatorni birlashtirish mumkin:

db.users.updateOne(
  { _id: userId },
  {
    $set:   { so_nggi_kirish: new Date(), faol: true },
    $inc:   { kirish_soni: 1 },
    $unset: { vaqtinchalik_token: "" }
  }
)
Bajarilishi:
  1. so'nggi_kirish = hozirgi vaqt
  2. faol = true
  3. kirish_soni = kirish_soni + 1
  4. vaqtinchalik_token o'chdi
  — hammasi bitta atomik operatsiyada!

Ehtiyot bo'lish kerak bo'lgan holat

Operator yo'q = documentni to'liq almashtirish

// ❌ XAVFLI — document to'liq almashadi!
db.talabalar.updateOne(
  { ism: "Jasur" },
  { yosh: 25 }          // ← $set yo'q!
)
OLDIN: { _id: ..., ism: "Jasur", yosh: 24, shahar: "Toshkent", sinf: "11A" }
                           ↓ { yosh: 25 }
KEYIN: { _id: ..., yosh: 25 }
        ism, shahar, sinf — yo'qoldi!

Sabab: operator ($set, $inc va h.k.) yo'q bo'lsa, MongoDB documentni filter bilan topib, to'liq berilgan ob'ekt bilan almashtiradi. _id saqlanadi, qolgan hammasi o'zgaradi.

// ✅ To'g'ri — $set bilan faqat yosh o'zgaradi
db.talabalar.updateOne(
  { ism: "Jasur" },
  { $set: { yosh: 25 } }
)

Bu eng ko'p uchraydigan xato — doim $set va boshqa operatorlarni ishlating.


Tez-tez uchraydigan xatolar

updateMany ga filter bermaslik

// ❌ Juda xavfli — barcha documentlar o'zgaradi
db.users.updateMany({}, { $set: { faol: false } })
//                   ↑ bo'sh filter — hammasi

// ✅ Oldin find bilan tekshiring
db.users.find({ faol: true }).count()    // nechta o'zgarishi
db.users.updateMany({ faol: true }, { $set: { faol: false } })

$push bilan dublikat muammo

// Har marta bosilganda hobby qo'shilib boradi
db.users.updateOne({ _id: id }, { $push: { hobbylar: "futbol" } })
// ["futbol", "futbol", "futbol", ...]

// ✅ $addToSet ishlatish
db.users.updateOne({ _id: id }, { $addToSet: { hobbylar: "futbol" } })

$inc mavjud bo'lmagan maydonni oshirish

// korishlar maydoni yo'q document
db.postlar.updateOne(
  { _id: id },
  { $inc: { korishlar: 1 } }
)
// Xato emas! MongoDB korishlar: 0 dan boshlaydi → 1 bo'ladi
// $inc maydon bo'lmasa, 0 dan boshlab oshiradi

Bu aslida foydali xususiyat — counter larni boshlang'ich qiymat belgilamasdan ishlatish mumkin.


Xulosa

┌─────────────────────────────────────────────────────────────┐
│  $set   { $set: { a: 1 } }     maydon qo'sh/yangilash       │
│  $unset { $unset: { a: "" } }  maydonni o'chirish           │
│  $inc   { $inc: { n: 1 } }     raqamni oshir/kamayti        │
│  $mul   { $mul: { n: 1.1 } }   raqamni ko'paytir            │
│  $rename{ $rename: {a: "b"} }  nom o'zgartir                │
├─────────────────────────────────────────────────────────────┤
│  $push     massivga qo'shish (dublikat mumkin)              │
│  $addToSet massivga faqat unikal qo'shish                   │
│  $pull     massivdan o'chirish (shart bilan ham)            │
│  $pop      massivdan boshi/oxiridan o'chirish               │
└─────────────────────────────────────────────────────────────┘
  • Operatorsiz update → document to'liq almashadi — eng xavfli xato
  • $inc atomik — counter larda doim shu
  • $addToSet vs $push — unikal kerakmi, yo'qmi
  • updateMany({}, ...) — filtrsiz barcha document o'zgaradi, ehtiyot bo'ling

Keyingi dars: 10-dars — upsert, findOneAndUpdate, findOneAndDelete