Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. O'qish
- 2.1 find va so'rov obyekti
- 2.2 $regex va boshqa o'qish metodlari
- 3. findOneAndUpdate: o'zgartirish va hujjatni olish
- 3.1 Muammo: qaysi hujjat qaytadi
- 3.2 Sukut: eski hujjat
- 3.3 Yangi hujjat: returnDocument: 'after'
- 4. Update operatorlari Mongoose'da
- 4.1 Operatorlar — drayverdagidek
- 4.2 Operatorsiz obyekt: Mongoose o'zi o'raydi
- 4.3 Upsert va $setOnInsert
- 5. Tuzoq: update validatsiyani chetlab o'tadi
- 5.1 runValidators siz
- 5.2 runValidators: true
- 5.3 pre("save") ham ishlamaydi
- 5.4 Qaysi yo'lni tanlash
- 6. O'chirish
- 7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): returnDocument qanday ishlaydi
- 3-mashq (qiyin): runValidators modeli
- 10. Real ishda
- Xulosa
- Manbalar
Mongoose CRUD va update operatorlari: findOneAndUpdate, returnDocument va runValidators
Qisqacha: Mongoose'da o'qish —
find,findOne,findById; yozish —create,updateOne,findOneAndUpdate,deleteOne. O'zgartirish drayverdagi kabi operatorlar bilan ($set,$inc,$push,$addToSet,$pull), lekin operatorsiz obyektni Mongoose o'zi$setga o'raydi.findOneAndUpdatesukut bo'yicha eski hujjatni qaytaradi; yangisi uchunreturnDocument: 'after'(Mongoose 9.11 da{ new: true }eskirgan). Eng xavfli tuzoq:updateOneschema validatsiyasini ishlatmaydi — kanonda enum'ga zidjudabazaga yozildi;runValidators: truekerak.
Bu darsda
- Mongoose so'rovlari bilan o'qiysiz: filtr,
select, saralash,$in,$regex. findOneAndUpdatenireturnDocument: 'after'bilan ishlatasiz va eskirgannew: trueni taniysiz.- Update operatorlarini Mongoose orqali qo'llaysiz, upsert va
$setOnInsertni bilasiz. runValidatorssiz update validatsiyani chetlab o'tishini vapre("save")ishlamasligini tushunasiz.
Oldin bilishingiz kerak: Mongoose: schema va model, MongoDB native driver, UPDATE va DELETE xavfsiz, Read phenomena va izolyatsiya darajalari.
1. Nega bu kerak?
Oldingi darsda schema yozdik: ustuvorlik faqat past, orta, yuqori bo'la oladi. Kanon buni sinab, ustuvorlik: "juda" bilan create qildi — Mongoose rad etdi. Xotirjam bo'lsa bo'ladimi?
Yo'q. Kanon keyin xuddi shu qiymatni updateOne bilan yozdi — va u bazaga tushdi. Schema bor, qoida bor, lekin bitta metod uni chetlab o'tdi. Bu dars — Mongoose'ning har bir CRUD metodi aslida nima qilishi haqida. Qaysi biri validatsiyani ishga tushiradi, qaysi biri eski hujjatni qaytaradi, qaysi biri save middleware'ini chaqiradi.
O'xshatish: binoning asosiy eshigida qorovul bor — hujjatni tekshiradi. Lekin orqa eshik ham bor va u yerda qorovul yo'q. create va save — asosiy eshik. updateOne — orqa eshik. Qorovulni u yerga ham qo'yish mumkin, faqat buni o'zingiz aytishingiz kerak.
Chiqishlar — kanon tajribasidan (kanon o'lchovi, IlmHamroh, 2026-10): Node 24.21.0, mongoose 9.11.2, MongoDB 8.3.13. Schema — oldingi darsdagi VazifaS; v1 — "Non olish #uy #Bozor" vazifasi, aziz — Azizning hujjati. Kolleksiya nomlari vazifas, foydalanuvchis — oldingi darsdagi tuzoq.
2. O'qish
2.1 find va so'rov obyekti
await Vazifa.insertMany([
{ egasi: aziz._id, matn: "Hisobot yozish #ish",
ustuvorlik: "yuqori" },
{ egasi: aziz._id, matn: "Kitob o'qish #o'qish" },
]);
const yuqorilar = await Vazifa
.find({ ustuvorlik: { $in: ["yuqori", "past"] } })
.select("matn -_id")
.lean();
console.log("find $in:", yuqorilar.map((v) => v.matn));find $in: [ 'Hisobot yozish #ish' ]insertMany — bir nechta hujjatni bitta so'rovda; har biri schema bo'yicha tekshiriladi va default'lar qo'yiladi (ikkinchi vazifada ustuvorlik — orta).
Vazifa.find(…) darhol bazaga bormaydi. U so'rov (Query) obyekti qaytaradi. Uni zanjir bilan to'ldirasiz: .select(), .sort(), .limit(), .lean(). So'rov await qilinganda (yoki .exec() chaqirilganda) ketadi.
.select("matn -_id")— projection satr shaklida:matnni ber,_idni yashir. Obyekt shakli ham bor:.select({ matn: 1, _id: 0 })..lean()— Mongoose hujjati (metodlari, getter'lari bilan) o'rniga oddiy JS obyekt. Tezroq; Mongoose ilg'or darsida o'lchaymiz.
2.2 $regex va boshqa o'qish metodlari
const soni = (await Vazifa
.find({ matn: { $regex: "KITOB", $options: "i" } })
.lean()).length;
console.log("$regex:", soni);$regex: 1i — katta-kichik harf farqsiz. Bu yerda biz hujjatlarni olib, JavaScript'da sanadik. Faqat son kerak bo'lsa — Vazifa.countDocuments(filtr): hujjatlar tarmoqdan o'tmaydi.
| Metod | Qaytaradi | Topilmasa |
|---|---|---|
find(filtr) |
massiv | [] |
findOne(filtr) |
bitta hujjat | null |
findById(id) |
bitta hujjat (_id bo'yicha) |
null |
countDocuments(filtr) |
son | 0 |
exists(filtr) |
{ _id } yoki null |
null |
findById(id) — findOne({ _id: id }) ning qisqasi. Id satr bo'lsa, Mongoose uni ObjectId'ga aylantiradi; aylanmasa — CastError (oldingi dars).
Diqqat:
$regexga foydalanuvchi kiritgan matnni to'g'ridan qo'ymang..*yoki(a+)+kabi naqsh butun kolleksiyani skanerlaydi yoki serverni qiynaydi (ReDoS). Matnni ekranlang yoki to'liq matn qidiruvidan foydalaning (MongoDB indekslari).
3. findOneAndUpdate: o'zgartirish va hujjatni olish
3.1 Muammo: qaysi hujjat qaytadi
Native driver darsida updateOne faqat sonlarni qaytardi (matchedCount, modifiedCount). API'da esa ko'pincha yangilangan hujjat kerak: PATCH /vazifalar/:id javobida yangi holat. Postgres'da UPDATE … RETURNING * edi.
Mongoose'da — findOneAndUpdate(filtr, ozgarish, sozlamalar). U hujjatni topadi, o'zgartiradi va qaytaradi. Qaysi holatini? Kanon ikkalasini ham ko'rsatdi.
3.2 Sukut: eski hujjat
const eski = await Vazifa.findOneAndUpdate(
{ _id: v1._id },
{ $set: { bajarildi: true } },
);
console.log("new'siz — ESKI hujjat qaytadi: bajarildi =",
eski.bajarildi);new'siz — ESKI hujjat qaytadi: bajarildi = falseBazada bajarildi endi true. Lekin qaytgan hujjatda — false. Bu o'zgarishdan oldingi holat. Sukut shunday. Tajribasiz dasturchi buni ko'rib "update ishlamadi" deb o'ylaydi va kodni qayta-qayta tekshiradi.
3.3 Yangi hujjat: returnDocument: 'after'
Yangilangan hujjat uchun sozlama kerak. Uzoq yillar u { new: true } edi — xaritamizda ham, internetdagi ko'p darslarda ham shunday. Kanon aynan shu bilan ishga tushirdi va Mongoose 9.11.2 ogohlantirish berdi:
(node:6752) [MONGOOSE] Warning: mongoose: the `new` option for `findOneAndUpdate()` and `findOneAndReplace()` is deprecated. Use `returnDocument: 'after'` instead.
(Use `node --trace-warnings ...` to show where the warning was created)
findOneAndUpdate {new:true} → versiya 2 | izohlar 1 | teglar uy,bozorTarjima: "findOneAndUpdate() va findOneAndReplace() uchun new sozlamasi eskirgan (deprecated). O'rniga returnDocument: 'after' ishlating." (node:6752) — jarayon raqami, sizda boshqa bo'ladi.
Natija to'g'ri chiqdi (versiya 2, bitta izoh, teglar uy,bozor), lekin eskirgan sozlama keyingi katta versiyada o'chirilishi mumkin. Shuning uchun bu kursda asosiy shakl:
const yangi = await Vazifa.findOneAndUpdate(
{ _id: v1._id },
{
$inc: { versiya: 1 },
$push: { izohlar: { muallif: aziz._id, matn: "Tez" } },
$addToSet: { teglar: "uy" },
},
{ returnDocument: "after" },
);
console.log(yangi.versiya, yangi.izohlar.length,
yangi.teglar.join(","));Ogohlantirish ayni shu almashtiruvni taklif qiladi, va returnDocument — MongoDB drayverining o'z sozlamasi nomi ("before" | "after"). Halol eslatma: kanon tajribasi new: true bilan ishlagan; returnDocument: 'after' li variantni biz bazada alohida ishga tushirmadik — natija Mongoose hujjati bo'yicha bir xil (versiya 2, izohlar 1, teglar uy,bozor). Bazasiz esa tekshirdik (mongoose 9.11.2): sozlama so'rovga {"returnDocument":"after"} bo'lib o'tadi va ogohlantirish chiqmaydi.
Maslahat: Eski loyihada
{ new: true }ni ko'rsangiz — u ishlaydi, faqat ogohlantirish beradi. Yangi koddareturnDocument: "after". Loglaringizda[MONGOOSE] Warningqatorlarini e'tiborsiz qoldirmang — ular keyingi yangilanishda nima buzilishini oldindan aytadi.
findByIdAndUpdate(id, …) — findOneAndUpdate({ _id: id }, …) ning qisqasi. findOneAndDelete — o'chirib, o'chirilgan hujjatni qaytaradi.
Tekshirib ko'ring: API'da
PATCH /vazifalar/:idjavobidafindByIdAndUpdate(id, { $set: body })natijasini qaytaryapsiz,returnDocumentyo'q. Foydalanuvchi matnni o'zgartirdi. U ekranda nimani ko'radi?
Javob
Eski matnni. Bazada yangisi saqlangan, lekin javobda o'zgarishdan oldingi hujjat. Frontend javobni ekranga chiqarsa — foydalanuvchi "saqlanmadi" deb qayta-qayta bosadi. Yechim — { returnDocument: "after" }.
4. Update operatorlari Mongoose'da
4.1 Operatorlar — drayverdagidek
Operatorlar bazaning o'ziniki, Mongoose ularni o'zgarishsiz yuboradi (faqat qiymatlarni schema turiga aylantiradi):
| Operator | Misol | Natija |
|---|---|---|
$set |
{ $set: { bajarildi: true } } |
maydon qiymati |
$inc |
{ $inc: { versiya: 1 } } |
songa qo'shish |
$push |
{ $push: { izohlar: {...} } } |
massiv oxiriga |
$addToSet |
{ $addToSet: { teglar: "uy" } } |
takrorsiz qo'shish |
$pull |
{ $pull: { teglar: "uy" } } |
massivdan olib tashlash |
$unset |
{ $unset: { muddat: 1 } } |
maydonni o'chirish |
$setOnInsert |
upsert'da | faqat yangi hujjat yaratilganda |
$push bilan qo'shilgan izoh ham izoh schema'si (IzohS) bo'yicha turga aylantiriladi va o'z _id sini oladi.
Kanon $pull ni sinadi:
const r = await Vazifa.findByIdAndUpdate(
v1._id, { $pull: { teglar: "uy" } },
{ returnDocument: "after" });
console.log("$pull:", r.teglar.join(","));$pull: bozor(Kanon bu qatorni — va pastdagi ikki findByIdAndUpdate misolini ham — new: true bilan ishga tushirgan va yuqoridagi ogohlantirishni olgan. Biz kodni darsning asosiy shakliga o'tkazdik — ma'nosi bir xil.)
4.2 Operatorsiz obyekt: Mongoose o'zi o'raydi
Native driver darsida updateOne(f, { bajarildi: true }) drayverda xato bergan edi: Update document requires atomic operators. Mongoose'da esa bu ishlaydi. Kanon matnni operatorsiz o'zgartirdi:
await Vazifa.findByIdAndUpdate(
v1._id, { matn: "Boshqa matn #yangi" },
{ returnDocument: "after" });Mongoose { matn: … } ni { $set: { matn: … } } ga aylantirib yuboradi — hujjatning qolgani saqlanadi. Qulay, lekin ikki xavf bor:
- Drayver va Mongoose kodi aralash loyihada bir xil ko'rinadigan qator turlicha ishlaydi.
replaceOne/findOneAndReplaceda bu o'rash yo'q — ular baribir butun hujjatni almashtiradi.
Shuning uchun Mongoose'da ham operatorni aniq yozish tavsiya qilinadi: kod o'qiganga tushunarli bo'ladi.
4.3 Upsert va $setOnInsert
const u = await Vazifa.updateOne(
{ matn: "Yangi upsert" },
{ $setOnInsert: { egasi: aziz._id } },
{ upsert: true },
);
console.log("upsert:", JSON.stringify(u.upsertedCount));upsert: 1$setOnInsert — faqat yangi hujjat yaratilganda qo'llanadi. Hujjat allaqachon bor bo'lsa, egasi o'zgarmaydi. Naqsh: "topilsa — tegma, topilmasa — shu qiymatlar bilan yarat". Filtrdagi matn ham yangi hujjatga kiradi.
Upsert'da ehtiyot bo'ling: filtrga mos noyob indeks bo'lmasa, ikki parallel upsert ikkita hujjat yaratishi mumkin — ikkalasi bir vaqtda "topilmadi" deb o'ylaydi. Bu MongoDB hujjatida yozilgan; kanon parallel upsert'ni sinamadi. Postgres'dagi ON CONFLICT ham noyob cheklovga tayanardi (UPSERT va MERGE).
5. Tuzoq: update validatsiyani chetlab o'tadi
5.1 runValidators siz
Kanonning eng muhim topilmasi shu bo'limda:
const yomon = await Vazifa.updateOne(
{ _id: v1._id }, { $set: { ustuvorlik: "juda" } });
const bazada = (await Vazifa.findById(v1._id).lean()).ustuvorlik;
console.log("runValidators'siz updateOne enum'ni TEKSHIRMADI:",
JSON.stringify(yomon), "→ bazada:", bazada);runValidators'siz updateOne enum'ni TEKSHIRMADI: {"acknowledged":true,"modifiedCount":1,"upsertedId":null,"upsertedCount":0,"matchedCount":1} → bazada: judamodifiedCount: 1 — muvaffaqiyat. Bazada — juda. Schema'da enum bor, create uni rad etgan edi. updateOne esa tekshirmadi.
Nega? Mongoose'da validatsiya hujjat darajasida qurilgan: u to'liq hujjatni xotirada ko'rib, qoidalarni tekshiradi. updateOne esa hujjatni xotiraga yuklamaydi — u faqat "shu filtrga shu operatorlarni qo'lla" degan buyruqni bazaga yuboradi. Tekshiradigan hujjatning o'zi yo'q.
5.2 runValidators: true
await Vazifa.updateOne(
{ _id: v1._id },
{ $set: { ustuvorlik: "juda" } },
{ runValidators: true },
).then(() => "o'tdi", (x) => `${x.name}: ${x.message}`);runValidators: true → ValidationError: Validation failed: ustuvorlik: juda — ustuvorlik emasEndi Mongoose o'zgarishdagi maydonlarni schema qoidalari bilan tekshiradi va xato beradi — o'zbekcha enum xabarimiz bilan. Lekin bu "update validatorlari" to'liq hujjat validatsiyasi emas:
- Faqat o'zgarishda bor maydonlar tekshiriladi (
$set,$unset,$push,$addToSetva boshqalar). - Custom validator ichida
thishujjat emas — so'rov (Query) obyekti. Boshqa maydonga qarab tekshiradigan validator kutilganidek ishlamaydi.
Bu nozik farqlar Mongoose hujjatidagi "Update Validators" bo'limida; kanon ulardan faqat enum holatini sinadi.
Ikkinchi band — biz bazasiz tekshirdik (mongoose 9.11.2): validator ichida this — Query. Amaliy qoida: hamma joyda yoqing. Buning uchun global sozlama bor. Biz uni ham bazasiz sinadik: yoqilgach, runValidators siz updateOne({…}, { $set: { u: "juda" } }) ham ValidationError berdi — so'rov bazaga yetib bormasdan:
mongoose.set("runValidators", true);5.3 pre("save") ham ishlamaydi
Kanon schema'sida pre("save") middleware bor: matn o'zgarsa, teglarni matndan qayta ajratadi (Mongoose ilg'or darsida to'liq). Kanon matnni update bilan o'zgartirdi:
const r = await Vazifa.findByIdAndUpdate(
v1._id, { matn: "Boshqa matn #yangi" },
{ returnDocument: "after" });
console.log("update'da pre('save') ishlamaydi — teglar o'zgarmadi:",
r.teglar.join(","));update'da pre('save') ishlamaydi — teglar o'zgarmadi: uy,bozorMatn endi #yangi tegli, teglar esa eski: uy,bozor. Ma'lumot bir-biriga zid bo'lib qoldi. Sabab o'sha: save middleware hujjat saqlanganda ishlaydi, findByIdAndUpdate esa hujjatni saqlamaydi.
flowchart TD
A["O'zgartirish kerak"] --> B{"Qaysi metod?"}
B -->|"doc.save(), create()"| C["validate + pre save"]
B -->|"updateOne, findOneAndUpdate"| D["to'g'ridan bazaga"]
D --> E{"runValidators?"}
E -->|ha| F["faqat o'zgargan maydonlar tekshiriladi"]
E -->|yo'q| G["tekshiruvsiz"]
C --> H["MongoDB"]
F --> H
G --> HDiagrammaga qarang: chap yo'l — hujjat yo'li (to'liq tekshiruv va save middleware). O'ng yo'l — so'rov yo'li (tez, atomik, lekin tekshiruvsiz yoki qisman tekshiruv bilan).
5.4 Qaysi yo'lni tanlash
doc.save() |
updateOne / findOneAndUpdate |
|
|---|---|---|
| Bazaga murojaat | 2 (o'qish + yozish) | 1 |
| Validatsiya | to'liq | faqat runValidators bilan, qisman |
pre("save") |
ishlaydi | ishlamaydi (query middleware alohida) |
| Bir vaqtdagi o'zgarish | o'qish va yozish orasida boshqasi o'zgartirishi mumkin | atomik ($inc yo'qolmaydi) |
Ikkinchi ustunning atomikligi muhim. doc.versiya += 1; await doc.save() — ikki foydalanuvchi bir vaqtda qilsa, biri yo'qolishi mumkin (lost update). $inc bazada bajariladi — yo'qolmaydi. Shuning uchun hisoblagich va massivga qo'shishda update operatorlari, murakkab biznes qoidali tahrirda — save() (yoki update + runValidators + qoidani alohida tekshirish).
Tekshirib ko'ring: Kanon
pre("save")da teglarni matndan ajratadi. API matnnifindByIdAndUpdatebilan o'zgartiradi. Ikkita yechim ayting.
Javob
- Matnni o'zgartirishda
doc = await Vazifa.findById(id); doc.matn = yangi; await doc.save()—pre("save")ishlaydi (lekin ikki murojaat va atomiklik yo'q). - Update'ning o'zida teglarni ham hisoblab yuborish:
$set: { matn, teglar: teglariniOl(matn) }; yoki shu mantiqni query middleware'ga (pre("findOneAndUpdate")) ham qo'shish (Mongoose ilg'or). Muhimi — qoida bitta funksiyada bo'lsin va ikkala yo'l ham uni chaqirsin.
6. O'chirish
| Metod | Nima qiladi |
|---|---|
deleteOne(filtr) |
birinchi mosini o'chiradi, { deletedCount } |
deleteMany(filtr) |
hammasini; deleteMany({}) — kolleksiyani bo'shatadi |
findOneAndDelete(filtr) |
o'chiradi va o'chirilgan hujjatni qaytaradi |
doc.deleteOne() |
shu hujjatni (document middleware ishlaydi) |
vazifalar ilovasida o'chirish "Savat" orqali — ochirilgan sanasini $set qilish (Soft delete). Mongo'da ham shu naqsh: deleteOne emas, updateOne({ _id }, { $set: { ochirilgan: new Date() } }), o'qishda { ochirilgan: { $exists: false } }.
7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Postgres'da: u yerda UPDATE … RETURNING, validatsiya Zod'da va bazadagi CHECK lar har yo'lni to'sadi. Commit yo'q. Kanon Mongoose'da sinagani (kanon o'lchovi, IlmHamroh, 2026-10):
find+$in,$regex,select,lean.findOneAndUpdate: sukut eski hujjat,new: true— ogohlantirish bilan; shuning uchun bu darsdareturnDocument: 'after'asosiy.runValidatorssiz enum buzildi (juda), bilan —ValidationError.- Update'da
pre("save")ishlamadi;$pull,$setOnInsertupsert.
Solishtiring: Postgres'da CHECK (ustuvorlik IN (…)) qaysi yo'l bilan yozilmasin — psql, pg, Prisma — to'xtatadi. Mongoose'da qoida faqat "asosiy eshik" da turadi.
8. Ko'p uchraydigan xatolar
| Xato | Oqibat | To'g'risi |
|---|---|---|
findOneAndUpdate sozlamasiz |
Eski hujjat qaytadi | { returnDocument: "after" } |
{ new: true } yangi kodda |
Ogohlantirish, kelajakda buziladi | returnDocument: "after" |
updateOne + schema qoidasiga ishonish |
Yaroqsiz qiymat bazaga yoziladi | runValidators: true (yoki global) |
Update'da pre("save") ni kutish |
Hisoblangan maydonlar eskiradi | save() yoki query middleware |
doc.n += 1; doc.save() parallel |
Lost update | $inc |
Foydalanuvchi matni $regex ga |
ReDoS, butun skan | Ekranlash yoki text indeks |
findOneAndReplace ni update deb o'ylash |
Hujjat butunlay almashadi | findOneAndUpdate + $set |
9. Mashqlar
Mashqlar kurs/mashqlar/27/45-mongoose-crud/ da. 2- va 3-mashq yechimlari Node 24 da tekshirilgan (biz ishga tushirdik — Mongoose xulqining kichik modeli, kutubxonasiz).
1-mashq (oson): to'ldiring
findOneAndUpdatesukut bo'yicha qaysi hujjatni qaytaradi?- Yangi hujjatni qaytarish sozlamasi:
{ returnDocument: "" } - Update'da schema qoidalarini yoqadigan sozlama —
- Faqat yangi hujjat yaratilganda qo'llanadigan operator —
- Mongoose 9.11 da eskirgan sozlama:
{ : true }
Yechim
- eski — o'zgarishdan oldingi holat (
bajarildi = false). - after —
returnDocument: "after"; drayver sozlamasining nomi. - runValidators — faqat o'zgargan maydonlar tekshiriladi.
- $setOnInsert — upsert'da yangi hujjat uchun.
- new — ogohlantirish:
Use returnDocument: 'after' instead.
2-mashq (o'rta): returnDocument qanday ishlaydi
Xotiradagi "kolleksiya" uchun topibYangila(kolleksiya, filtr, ozgarish, sozlama) yozing. Filtr — faqat _id bo'yicha tenglik; o'zgarish — faqat $set va $inc. sozlama.returnDocument — "before" (sukut) yoki "after". Topilmasa null, sozlama.upsert bo'lsa — yangi hujjat yaratib, "after" da uni qaytarsin. Kolleksiyadagi hujjat haqiqatan o'zgarsin, qaytgan obyekt esa nusxa bo'lsin.
Ishora: structuredClone — chuqur nusxa.
Yechim
function topibYangila(kol, filtr, oz, sozlama = {}) {
const { returnDocument = "before", upsert = false } = sozlama;
let h = kol.find((x) => x._id === filtr._id);
if (!h && !upsert) return null;
const yangiMi = !h;
if (yangiMi) {
h = { _id: filtr._id };
kol.push(h);
}
const oldin = yangiMi ? null : structuredClone(h);
for (const [m, q] of Object.entries(oz.$set ?? {})) h[m] = q;
for (const [m, q] of Object.entries(oz.$inc ?? {})) {
h[m] = (h[m] ?? 0) + q;
}
return returnDocument === "after" ? structuredClone(h) : oldin;
}
const kol = [{ _id: 1, matn: "Non olish", bajarildi: false,
versiya: 1 }];
const oz = { $set: { bajarildi: true }, $inc: { versiya: 1 } };
const a = topibYangila(kol, { _id: 1 }, oz);
console.log("sukut:", a.bajarildi, a.versiya);
const b = topibYangila(kol, { _id: 1 }, oz,
{ returnDocument: "after" });
console.log("after:", b.bajarildi, b.versiya);
console.log("topilmadi:", topibYangila(kol, { _id: 2 }, oz));
const c = topibYangila(kol, { _id: 2 }, oz,
{ upsert: true, returnDocument: "after" });
console.log("upsert:", JSON.stringify(c), "| jami:", kol.length);Konsolda:
sukut: false 1
after: true 3
topilmadi: null
upsert: {"_id":2,"bajarildi":true,"versiya":1} | jami: 2Ikkinchi chaqiruvda versiya 3: birinchi chaqiruv ham bazani (kolleksiyani) o'zgartirgan edi — faqat eski nusxani qaytargan. Kanondagi "bajarildi = false" holati aynan birinchi qator: o'zgarish bo'ldi, qaytgani eski. Upsert'da $inc yo'q maydonni 0 dan boshlaydi — MongoDB'da ham shunday.
3-mashq (qiyin): runValidators modeli
updateOne(kol, id, ozgarish, sxema, sozlama) yozing. O'zgarish — $set. sozlama.runValidators bo'lsa, $set dagi har maydon sxemadagi enum bo'yicha tekshirilsin; xato bo'lsa hech narsa yozilmasin va ValidationError: Validation failed: <maydon>: <xabar> matnli xato tashlansin. Aks holda — tekshiruvsiz yozilsin. Natija — { matchedCount, modifiedCount }. Kanon misolini takrorlang: ustuvorlik: "juda" avval tekshiruvsiz, keyin runValidators bilan.
Ishora: xabar — {VALUE} — ustuvorlik emas shablonidagi {VALUE} ni replace bilan almashtiring.
Yechim
const sxema = {
ustuvorlik: {
enum: ["past", "orta", "yuqori"],
message: "{VALUE} — ustuvorlik emas",
},
};
function updateOne(kol, id, oz, sxema, sozlama = {}) {
const h = kol.find((x) => x._id === id);
if (!h) return { matchedCount: 0, modifiedCount: 0 };
const set = oz.$set ?? {};
if (sozlama.runValidators) {
const xatolar = [];
for (const [m, q] of Object.entries(set)) {
const r = sxema[m];
if (r?.enum && !r.enum.includes(q)) {
xatolar.push(`${m}: ${r.message.replace("{VALUE}", q)}`);
}
}
if (xatolar.length) {
const x = new Error(`Validation failed: ${xatolar.join(", ")}`);
x.name = "ValidationError";
throw x;
}
}
let ozgardi = false;
for (const [m, q] of Object.entries(set)) {
if (h[m] !== q) { h[m] = q; ozgardi = true; }
}
return { matchedCount: 1, modifiedCount: ozgardi ? 1 : 0 };
}
const kol = [{ _id: 1, matn: "Non olish", ustuvorlik: "orta" }];
const oz = { $set: { ustuvorlik: "juda" } };
console.log(JSON.stringify(updateOne(kol, 1, oz, sxema)),
"→ bazada:", kol[0].ustuvorlik);
kol[0].ustuvorlik = "orta";
try {
updateOne(kol, 1, oz, sxema, { runValidators: true });
} catch (x) {
console.log(`${x.name}: ${x.message}`);
}
console.log("bazada:", kol[0].ustuvorlik);Konsolda:
{"matchedCount":1,"modifiedCount":1} → bazada: juda
ValidationError: Validation failed: ustuvorlik: juda — ustuvorlik emas
bazada: ortaIkkinchi qator kanondagi haqiqiy Mongoose xabari bilan aynan bir xil. Modelning asosiy g'oyasi: update validatori faqat o'zgarishdagi maydonlarni ko'radi — butun hujjatni emas. matn yaroqsiz bo'lsa ham, $set da yo'q ekan, tekshirilmaydi.
10. Real ishda
runValidators: true— ko'p jamoalarda global yoqiladi, chunki "update orqali yaroqsiz ma'lumot" — Mongoose loyihalarining klassik xatosi.returnDocument: 'after'— PATCH endpoint'lari; eski loyihalarni yangilashdanew: trueni almashtirish oddiy, lekin foydali ish.- Hisoblagichlar va massivlar (
likes,views,teglar) — doim$inc/$addToSet; "o'qi-o'zgartir-saqla" — poyga holati. - Intervyu savollari: "
save()vaupdateOne()farqi?", "nega update validatsiyani o'tkazib yuboradi?", "findOneAndUpdatenimani qaytaradi?".
Xulosa
- Mongoose so'rovi — Query obyekti:
awaitqilinganda ketadi;select,sort,limit,leanbilan to'ldiriladi. findOneAndUpdatesukut bo'yicha eski hujjatni qaytaradi; yangisi —returnDocument: 'after';{ new: true }Mongoose 9.11 da eskirgan.- Operatorlar drayverdagidek; operatorsiz obyektni Mongoose
$setga o'raydi (drayver — rad etadi). updateOne/findOneAndUpdateschema validatsiyasini ishlatmaydi: kanondajudabazaga yozildi.runValidators: truefaqat o'zgargan maydonlarni tekshiradi.- Update'da
pre("save")ishlamaydi — hisoblangan maydonlar eskiradi. - Hisoblagich va massivga — atomik operator; murakkab qoidaga —
save()yoki qoidani ikkala yo'lda ham chaqirish.
Keyingi dars: Populate va embed vs reference qarori — Mongo'da bog'lanishning ikki yo'li, populate qancha so'rov yuborishi va qachon nimani tanlash.
Manbalar
- Mongoose 9 hujjati — Queries: https://mongoosejs.com/docs/queries.html
- Mongoose hujjati —
findOneAndUpdate(returnDocument): https://mongoosejs.com/docs/tutorials/findoneandupdate.html - Mongoose hujjati — Validation, Update Validators: https://mongoosejs.com/docs/validation.html#update-validators
- MongoDB hujjati — Update Operators: https://www.mongodb.com/docs/manual/reference/operator/update/
- Kanon o'lchovi (IlmHamroh, 2026-10-25): Node 24.21.0, mongoose 9.11.2, MongoDB 8.3.13 (Docker). Mashq yechimlari — biz ishga tushirdik (Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!