7-bo‘lim

Hujjatni yangilash

$set va $unset, $inc va $mul, massiv operatorlari $push, $pull va $addToSet, pozitsion operator va upsert.

🕑 18 daqiqa o‘qish 📄 794 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. $set va $unset
  2. Sonlarni o'zgartirish
  3. Ko'p hujjatni yangilash
  4. Massiv operatorlari
  5. $pull bilan o'chirish
  6. Massivning ma'lum elementini yangilash
  7. upsert
  8. findOneAndUpdate
  9. Xulosa

MongoDB da yangilash butun hujjatni qayta yozish emas. Siz qaysi maydonni qanday o'zgartirishni aytasiz, qolgani joyida qoladi.

JavaScript
db.kitob.insertMany([
  { _id: 1, nom: "Otkan kunlar", narx: 85000, ombor: 12,
    teglar: ["roman", "tarixiy"],
    baho: [ { kim: "Malika", ball: 5 }, { kim: "Nodira", ball: 4 } ] },
  { _id: 2, nom: "Navoiy", narx: 95000, ombor: 8,
    teglar: ["roman"],
    baho: [ { kim: "Aziza", ball: 3 } ] },
  { _id: 3, nom: "Ufq", narx: 64000, ombor: 0,
    teglar: ["zamonaviy"],
    baho: [] }
])

$set va $unset #

JavaScript
db.kitob.updateOne({ _id: 1 }, { $set: { narx: 92000, nashriyot: "Sharq" } })
db.kitob.findOne({ _id: 1 }, { nom: 1, narx: 1, nashriyot: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 1, nom: 'Otkan kunlar', narx: 92000, nashriyot: 'Sharq' }

$set mavjud maydonni o'zgartiradi, yo'g'ini esa qo'shadi.

JavaScript
db.kitob.updateOne({ _id: 1 }, { $unset: { ombor: "" } })
db.kitob.findOne({ _id: 1 }, { nom: 1, ombor: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 1, nom: 'Otkan kunlar' }

$unset ning qiymati ahamiyatsiz - odatda bo'sh matn yoziladi.

$set ni unutish - butun hujjatni yo'q qiladi

Bu MongoDB dagi eng qimmatga tushadigan xatolardan biri.

Solishtiring:

YozuvNima bo'ladi
updateOne({_id:1}, { $set: { narx: 92000 } })Faqat narx o'zgaradi
replaceOne({_id:1}, { narx: 92000 })Hujjatda faqat narx qoladi

Ikkinchi holatda nom, teglar, baho - hammasi o'chib ketadi. _id saqlanadi, qolgani yo'q.

Eski MongoDB versiyalarida updateOne ga operatorsiz hujjat berish ham xuddi shunday ishlar edi. Hozirgi versiyalar buni xato deb qaytaradi:

Natija
MongoInvalidArgumentError: Update document requires atomic
operators

Bu yaxshi himoya, lekin replaceOne hali ham mavjud va u ataylab shunday ishlaydi. Uni chaqirishdan oldin har doim o'ylab ko'ring.

Sonlarni o'zgartirish #

OperatorNima qiladi
$incQo'shadi (manfiy son - ayiradi)
$mulKo'paytiradi
$minFaqat kichikroq bo'lsa yozadi
$maxFaqat kattaroq bo'lsa yozadi
JavaScript
db.kitob.updateOne({ _id: 2 }, { $inc: { ombor: -3 } })
db.kitob.updateOne({ _id: 2 }, { $mul: { narx: 1.1 } })
db.kitob.findOne({ _id: 2 }, { nom: 1, narx: 1, ombor: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 2, nom: 'Navoiy', narx: 104500.00000000001, ombor: 5 }

Narxdagi .00000000001 tasodif emas: 95000 * 1.1 ikkilik kasrda aniq ifodalanmaydi. Bu 3-bobdagi ogohlantirishning amaldagi ko'rinishi - pul maydoni Double bo'lsa, shunday "quyruq" paydo bo'ladi.

Decimal128 da bunday bo'lmaydi. Bu darslikning qolgan qismida sonlar oddiy Double - chunki misollar qisqaroq chiqadi - lekin haqiqiy loyihada pulni Decimal128 da saqlang.

$inc - poyga holatidan himoya

Ikki foydalanuvchi bir vaqtda kitob sotib olsa, quyidagi kod xato beradi:

Natija
const kitob = db.kitob.findOne({ _id: 2 })
db.kitob.updateOne({ _id: 2 }, { $set: { ombor: kitob.ombor - 1 } })

Ikkalasi ham ombor: 8 ni o'qisa, ikkalasi ham 7 yozadi - natijada ikkita kitob sotilib, qoldiq faqat bittaga kamayadi.

$inc bu muammoni butunlay yo'qotadi:

Natija
db.kitob.updateOne({ _id: 2 }, { $inc: { ombor: -1 } })

Chunki hisoblash serverda, bitta hujjat ustida atomar bajariladi. Hech kim oraga tushib qololmaydi.

Bitta hujjatga tegadigan har qanday yangilash MongoDB da atomar - bu tranzaksiyasiz ham kafolatlangan.

Ko'p hujjatni yangilash #

JavaScript
db.kitob.updateMany({ narx: { $lt: 90000 } }, { $mul: { narx: 1.05 } })
db.kitob.find({}, { nom: 1, narx: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 2,
  modifiedCount: 2,
  upsertedCount: 0
}
[
  { _id: 1, nom: 'Otkan kunlar', narx: 89250 },
  { _id: 2, nom: 'Navoiy', narx: 95000 },
  { _id: 3, nom: 'Ufq', narx: 67200 }
]

updateOne faqat birinchi mos hujjatni o'zgartiradi, updateMany esa hammasini.

Massiv operatorlari #

OperatorNima qiladi
$pushOxiriga qo'shadi (takror bo'lsa ham)
$addToSetQo'shadi, lekin takrorlamaydi
$pullShartga mos elementlarni o'chiradi
$popBirinchi (-1) yoki oxirgi (1) elementni o'chiradi
$eachBir nechta elementni birga qo'shadi
JavaScript
db.kitob.updateOne({ _id: 3 }, { $push: { teglar: "roman" } })
db.kitob.updateOne({ _id: 3 }, { $push: { teglar: "roman" } })
db.kitob.findOne({ _id: 3 }, { nom: 1, teglar: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 3, nom: 'Ufq', teglar: [ 'zamonaviy', 'roman', 'roman' ] }

Ikki marta qo'shildi. $addToSet esa takrorlamaydi:

JavaScript
db.kitob.updateOne({ _id: 3 }, { $addToSet: { teglar: "roman" } })
db.kitob.updateOne({ _id: 3 }, { $addToSet: { teglar: "roman" } })
db.kitob.findOne({ _id: 3 }, { nom: 1, teglar: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 0,
  upsertedCount: 0
}
{ _id: 3, nom: 'Ufq', teglar: [ 'zamonaviy', 'roman' ] }

Bir nechta elementni birga qo'shish:

JavaScript
db.kitob.updateOne(
  { _id: 3 },
  { $addToSet: { teglar: { $each: ["ijtimoiy", "zamonaviy", "yangi"] } } }
)
db.kitob.findOne({ _id: 3 }, { nom: 1, teglar: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 3, nom: 'Ufq', teglar: [ 'zamonaviy', 'ijtimoiy', 'yangi' ] }

zamonaviy allaqachon bor edi - u takrorlanmadi.

$pull bilan o'chirish #

JavaScript
db.kitob.updateOne({ _id: 1 }, { $pull: { teglar: "tarixiy" } })
db.kitob.updateOne({ _id: 1 }, { $pull: { baho: { ball: { $lt: 5 } } } })
db.kitob.findOne({ _id: 1 }, { nom: 1, teglar: 1, baho: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: 1,
  nom: 'Otkan kunlar',
  teglar: [ 'roman' ],
  baho: [ { kim: 'Malika', ball: 5 } ]
}

$pull oddiy qiymatni ham, shartga mos hujjatni ham o'chira oladi.

Massivga ta'sir qiluvchi to'rtta operator $push [a, b] + b [a, b, b] $addToSet [a, b] + b [a, b] $pull [a, b, b] - b [a] $pop [a, b, c] $pop: 1 [a, b] $pull hamma mos elementni o'chiradi, $pop esa faqat bittasini $pull shart qabul qiladi: { $pull: { baho: { ball: { $lt: 3 } } } } $pop faqat -1 (birinchisi) yoki 1 (oxirgisi) qiymatini oladi $addToSet takrorni tekshiradi - shuning uchun u $push dan sekinroq
Massivni o'qib olib, o'zgartirib, qayta yozish deyarli hech qachon kerak emas

Massivning ma'lum elementini yangilash #

Bu yerda pozitsion operator $ ishlatiladi:

JavaScript
db.kitob.updateOne(
  { _id: 2, "baho.kim": "Aziza" },
  { $set: { "baho.$.ball": 5 } }
)
db.kitob.findOne({ _id: 2 }, { nom: 1, baho: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 2, nom: 'Navoiy', baho: [ { kim: 'Aziza', ball: 5 } ] }

$ - "filtr mos kelgan element" degani. Uning shartsiz ishlashi mumkin emas: filtrda shu massivga tegishli shart bo'lishi shart.

Hamma elementni yangilash uchun $[] ishlatiladi:

JavaScript
db.kitob.updateOne({ _id: 1 }, { $inc: { "baho.$[].ball": 1 } })
db.kitob.findOne({ _id: 1 }, { nom: 1, baho: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: 1,
  nom: 'Otkan kunlar',
  baho: [ { kim: 'Malika', ball: 6 }, { kim: 'Nodira', ball: 5 } ]
}

Shartga mos elementlarni yangilash - arrayFilters:

JavaScript
db.kitob.updateOne(
  { _id: 1 },
  { $set: { "baho.$[past].ball": 1 } },
  { arrayFilters: [ { "past.ball": { $lt: 5 } } ] }
)
db.kitob.findOne({ _id: 1 }, { nom: 1, baho: 1 })
Natija
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{
  _id: 1,
  nom: 'Otkan kunlar',
  baho: [ { kim: 'Malika', ball: 5 }, { kim: 'Nodira', ball: 1 } ]
}

Faqat Nodira ning bahosi o'zgardi - Malika niki 5 edi, ya'ni shartga tushmadi.

YozuvQaysi elementlar
$Filtrga birinchi mos kelgani
$[]Hammasi
$[nom]arrayFilters shartiga mos kelganlar

upsert #

upsert: true - "topsang yangila, topmasang qo'sh":

JavaScript
const j = db.kitob.updateOne(
  { nom: "Sarob" },
  { $set: { narx: 59000 }, $setOnInsert: { ombor: 0, teglar: [] } },
  { upsert: true }
)
j.matchedCount
j.upsertedCount
db.kitob.findOne({ nom: "Sarob" }, { _id: 0 })
Natija
0
1
{ nom: 'Sarob', narx: 59000, ombor: 0, teglar: [] }

$setOnInsert faqat yangi hujjat yaratilganda ishlaydi. Mavjud hujjat yangilanayotgan bo'lsa, u e'tiborsiz qoldiriladi.

upsert va takroriy hujjatlar

upsert "topsang yangila" deyishi uchun topa olishi kerak. Agar filtr maydoniga yagona indeks qo'yilmagan bo'lsa, ikki so'rov bir vaqtda kelganda ikkalasi ham "yo'q ekan" degan xulosaga kelib, ikkita hujjat yaratishi mumkin.

Natija - bir xil nom bilan ikkita hujjat, va keyingi updateOne chaqiruvlari ulardan faqat bittasiga tegadi.

Yechim bitta: upsert ishlatiladigan filtr maydoniga unique indeks qo'ying:

Natija
db.kitob.createIndex({ nom: 1 }, { unique: true })

Shunda ikkinchi so'rov E11000 xatosini oladi va dastur uni qayta urinib ko'radi. Indekslar haqida 15-bobda gaplashamiz.

findOneAndUpdate #

Ba'zan yangilangan hujjatning o'zi kerak bo'ladi:

JavaScript
db.kitob.findOneAndUpdate(
  { _id: 3 },
  { $inc: { ombor: 5 } },
  { returnDocument: "after", projection: { nom: 1, ombor: 1 } }
)
Natija
{ _id: 3, nom: 'Ufq', ombor: 5 }
ParametrMa'nosi
returnDocument: "before"Yangilashdan oldingi holat (odatiy)
returnDocument: "after"Yangilashdan keyingi holat
Amaliy topshiriq
  1. $set bilan mavjud maydonni o'zgartiring.
  2. $set bilan yangi maydon qo'shing.
  3. $unset bilan maydonni o'chiring.
  4. replaceOne nega xavfli ekanini tushuntiring.
  5. $inc bilan omborni kamaytiring.
  6. $inc poyga holatidan qanday himoya qilishini yozing.
  7. $push va $addToSet farqini sinab ko'ring.
  8. $pull bilan shartga mos bahoni o'chiring.
  9. Pozitsion $ bilan bitta bahoni yangilang.
  10. upsert uchun nega yagona indeks kerakligini tushuntiring.

Xulosa #

  • Yangilash butun hujjatni emas, ayrim maydonlarni o'zgartiradi.
  • $set mavjudini o'zgartiradi, yo'g'ini qo'shadi; $unset o'chiradi.
  • replaceOne butun hujjatni almashtiradi - qolgan maydonlar yo'qoladi.
  • $inc va $mul serverda hisoblaydi - shuning uchun poyga holati bo'lmaydi.
  • Bitta hujjatga tegadigan har qanday yangilash atomar.
  • $push takrorlaydi, $addToSet takrorlamaydi.
  • $pull shartga mos elementlarni, $pop esa chekkadagi bittasini o'chiradi.
  • Pozitsion $ filtrga mos birinchi elementni, $[] hammasini yangilaydi.
  • arrayFilters bilan shartga mos elementlarni tanlash mumkin.
  • upsert ishlatiladigan filtr maydoniga unique indeks qo'ying - aks holda takroriy hujjat paydo bo'ladi.

Keyingi bo'limda hujjatlarni o'chirish va almashtirishni ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.