7-bo‘lim
Hujjatni yangilash
$set va $unset, $inc va $mul, massiv operatorlari $push, $pull va $addToSet, pozitsion operator va upsert.
Ushbu bo‘lim mundarijasi
MongoDB da yangilash butun hujjatni qayta yozish emas. Siz qaysi maydonni qanday o'zgartirishni aytasiz, qolgani joyida qoladi.
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 #
db.kitob.updateOne({ _id: 1 }, { $set: { narx: 92000, nashriyot: "Sharq" } })
db.kitob.findOne({ _id: 1 }, { nom: 1, narx: 1, nashriyot: 1 })
{
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.
db.kitob.updateOne({ _id: 1 }, { $unset: { ombor: "" } })
db.kitob.findOne({ _id: 1 }, { nom: 1, ombor: 1 })
{
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 qiladiBu MongoDB dagi eng qimmatga tushadigan xatolardan biri.
Solishtiring:
| Yozuv | Nima 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:
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 #
| Operator | Nima qiladi |
|---|---|
$inc | Qo'shadi (manfiy son - ayiradi) |
$mul | Ko'paytiradi |
$min | Faqat kichikroq bo'lsa yozadi |
$max | Faqat kattaroq bo'lsa yozadi |
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 })
{
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 himoyaIkki foydalanuvchi bir vaqtda kitob sotib olsa, quyidagi kod xato beradi:
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:
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 #
db.kitob.updateMany({ narx: { $lt: 90000 } }, { $mul: { narx: 1.05 } })
db.kitob.find({}, { nom: 1, narx: 1 })
{
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 #
| Operator | Nima qiladi |
|---|---|
$push | Oxiriga qo'shadi (takror bo'lsa ham) |
$addToSet | Qo'shadi, lekin takrorlamaydi |
$pull | Shartga mos elementlarni o'chiradi |
$pop | Birinchi (-1) yoki oxirgi (1) elementni o'chiradi |
$each | Bir nechta elementni birga qo'shadi |
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 })
{
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:
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 })
{
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:
db.kitob.updateOne(
{ _id: 3 },
{ $addToSet: { teglar: { $each: ["ijtimoiy", "zamonaviy", "yangi"] } } }
)
db.kitob.findOne({ _id: 3 }, { nom: 1, teglar: 1 })
{
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 #
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 })
{
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.
Massivning ma'lum elementini yangilash #
Bu yerda pozitsion operator $ ishlatiladi:
db.kitob.updateOne(
{ _id: 2, "baho.kim": "Aziza" },
{ $set: { "baho.$.ball": 5 } }
)
db.kitob.findOne({ _id: 2 }, { nom: 1, baho: 1 })
{
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:
db.kitob.updateOne({ _id: 1 }, { $inc: { "baho.$[].ball": 1 } })
db.kitob.findOne({ _id: 1 }, { nom: 1, baho: 1 })
{
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:
db.kitob.updateOne(
{ _id: 1 },
{ $set: { "baho.$[past].ball": 1 } },
{ arrayFilters: [ { "past.ball": { $lt: 5 } } ] }
)
db.kitob.findOne({ _id: 1 }, { nom: 1, baho: 1 })
{
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.
| Yozuv | Qaysi elementlar |
|---|---|
$ | Filtrga birinchi mos kelgani |
$[] | Hammasi |
$[nom] | arrayFilters shartiga mos kelganlar |
upsert #
upsert: true - "topsang yangila, topmasang qo'sh":
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 })
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 hujjatlarupsert "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:
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:
db.kitob.findOneAndUpdate(
{ _id: 3 },
{ $inc: { ombor: 5 } },
{ returnDocument: "after", projection: { nom: 1, ombor: 1 } }
)
{ _id: 3, nom: 'Ufq', ombor: 5 }
| Parametr | Ma'nosi |
|---|---|
returnDocument: "before" | Yangilashdan oldingi holat (odatiy) |
returnDocument: "after" | Yangilashdan keyingi holat |
$setbilan mavjud maydonni o'zgartiring.$setbilan yangi maydon qo'shing.$unsetbilan maydonni o'chiring.replaceOnenega xavfli ekanini tushuntiring.$incbilan omborni kamaytiring.$incpoyga holatidan qanday himoya qilishini yozing.$pushva$addToSetfarqini sinab ko'ring.$pullbilan shartga mos bahoni o'chiring.- Pozitsion
$bilan bitta bahoni yangilang. upsertuchun nega yagona indeks kerakligini tushuntiring.
Xulosa #
- Yangilash butun hujjatni emas, ayrim maydonlarni o'zgartiradi.
$setmavjudini o'zgartiradi, yo'g'ini qo'shadi;$unseto'chiradi.replaceOnebutun hujjatni almashtiradi - qolgan maydonlar yo'qoladi.$incva$mulserverda hisoblaydi - shuning uchun poyga holati bo'lmaydi.- Bitta hujjatga tegadigan har qanday yangilash atomar.
$pushtakrorlaydi,$addToSettakrorlamaydi.$pullshartga mos elementlarni,$popesa chekkadagi bittasini o'chiradi.- Pozitsion
$filtrga mos birinchi elementni,$[]hammasini yangilaydi. arrayFiltersbilan shartga mos elementlarni tanlash mumkin.upsertishlatiladigan filtr maydonigauniqueindeks qo'ying - aks holda takroriy hujjat paydo bo'ladi.
Keyingi bo'limda hujjatlarni o'chirish va almashtirishni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.