Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. 16 MB chegara
- 3. Anti-naqsh: cheksiz massiv
- 3.1 O'lchov
- 4. Subset naqshi: faqat kerakli qism
- 4.1 G'oya
- 4.2 Natija
- 5. Computed naqshi: hisobni oldindan saqlash
- 6. Bucket naqshi: vaqt qatorlari
- 6.1 Muammo
- 6.2 O'lchov
- 7. Extended reference: nusxa bilan ishora
- 8. Outlier va attribute naqshlari
- 9. Schema versioning
- 10. Naqshlar jadvali
- 11. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 12. Ko'p uchraydigan xatolar
- 13. Mashqlar
- 1-mashq (oson): qaysi naqsh?
- 2-mashq (o'rta): soatlik bucket
- 3-mashq (qiyin): $push + $each + $sort + $slice
- 14. Real ishda
- Xulosa
- Manbalar
MongoDB sxema dizayn naqshlari: subset, computed, bucket, extended reference va anti-naqshlar
Qisqacha: Hujjat bazasida modellashning sinalgan yechimlari bor — naqshlar (patterns). Eng xavfli anti-naqsh — cheksiz massiv: kanonda 20 000 izohli vazifa 1.85 MB ga yetdi,
findOne4 ms dan 96 ms ga sekinlashdi. Uni subset (hujjatda faqat oxirgi 5 izoh, hammasi alohida kolleksiyada — 2.4 ms) va computed (izohlarSonini$incbilan saqlash) hal qiladi. Bucket 1 440 ta daqiqalik hodisani 24 ta soatlik hujjatga yig'adi (102 KB → 49 KB). Extended reference o'qishni tezlatadi, lekin nusxalarni yangilash kerak (1 000 nusxa — 44.9 ms). Schema versioning eski va yangi shaklni bir kolleksiyada yashatadi.
Bu darsda
- 16 MB chegarani va cheksiz massiv anti-naqshini o'lchov bilan tushunasiz.
- Subset va computed naqshlarini
$push+$sliceva$incbilan qo'llaysiz. - Bucket naqshi bilan vaqt qatorlarini ixcham saqlaysiz.
- Extended reference, outlier va attribute naqshlarining narxini bilasiz.
- Schema versioning bilan sxemani to'xtovsiz o'zgartirasiz.
Oldin bilishingiz kerak: MongoDB indekslari, Populate va embed vs reference, Denormalizatsiya va hisoblangan maydonlar, Modellash naqshlari.
1. Nega bu kerak?
Populate va embed vs reference darsida qaror qoidasini o'rgandik: chegaralangan — embed, cheksiz — reference. Lekin hayot oq-qora emas. Vazifaning izohlari odatda 2–3 ta. Lekin bitta vazifa "Kurs tugadi, fikringizni yozing!" bo'lsa, unga 20 000 izoh tushishi mumkin. Hammasini reference qilsak — oddiy vazifalar uchun ortiqcha so'rov. Hammasini embed qilsak — mashhur vazifa hujjati shishib ketadi.
Postgres'da bunday savollar kam edi: normalizatsiya, keyin kerak bo'lsa denormalizatsiya (Denormalizatsiya va hisoblangan maydonlar). Mongo'da esa shu oraliq yechimlarning nomli to'plami bor — sxema dizayn naqshlari. Ular MongoDB jamoasining "Building with Patterns" maqolalar turkumidan keng tarqalgan.
O'xshatish: oshxonadagi muzlatgich. Hamma mahsulotni muzlatgichga tiqsangiz (cheksiz embed) — eshik yopilmaydi, kerakli narsani topish qiyin. Hammasini omborda saqlasangiz (reference) — har ovqatga omborga borasiz. Yechim: kundalik kerakli narsalar muzlatgichda, qolgani omborda (subset). Va "nechta tuxum qoldi" deb har safar sanamaslik uchun eshikka yozib qo'yasiz (computed).
Hamma o'lchovlar — kanon o'lchovi (IlmHamroh, 2026-10, alohida naqsh bazasi): MongoDB 8.3.13 (Docker, WSL2), mongodb 7.7.0, Node 24.21.0, i5-12500H. Bitta o'lchov — tendensiyaga qarang, aniq millisekundga emas.
2. 16 MB chegara
MongoDB tuzilishi darsida aytdik: bitta BSON hujjat — ko'pi bilan 16 MB. Kanon chegarani buzib ko'rdi — bitta hujjatga 17 MB matn:
await db.collection("katta_hujjat").insertOne({
matn: "x".repeat(17 * 1024 * 1024),
});RangeError: The value of "offset" is out of range. It must be >= 0 && <= 17825792. Received 17825794Kutilmagan joy: xatoni server emas, drayver berdi. RangeError — Node/drayverning o'z xatosi: hujjatni BSON'ga aylantirish (serializatsiya) paytida bufer chegarasidan chiqib ketdi. Tarjima: "offset qiymati ruxsat etilgan oraliqdan tashqarida: 0 dan 17 825 792 gacha bo'lishi kerak, 17 825 794 berildi". 17 825 792 bayt = 17 MB — satrning o'zi; { matn: … } ning qo'shimcha baytlari sig'madi. Ya'ni so'rov serverga yetib bormadi ham.
Xabar 16 MB haqida gapirmaydi va chalg'itadi. Shuning uchun real ilovada kattaligi oldindan bilinmaydigan maydonni (yuklangan fayl, uzun log) hujjat ichida saqlamang: fayllar — obyekt omboriga (S3 kabi) yoki GridFS'ga (hujjat bo'yicha), hujjatda faqat havola.
3. Anti-naqsh: cheksiz massiv
3.1 O'lchov
Kanon bitta vazifaga izohlarni ichiga $push bilan qo'shdi — 20 000 ta, va oraliq nuqtalarda hujjat hajmini, bitta $push vaqtini va findOne vaqtini (5 marta, mediana) o'lchadi:
await V.updateOne({ _id: 1 }, { $push: { izohlar: {
muallif: i % 50,
matn: `Izoh raqami ${i} — biroz uzunroq matn bilan`,
vaqt: new Date(1.7e12 + i * 60000),
} } });
// hajm: { $bsonSize: "$$ROOT" } — hujjatning BSON hajmiHar qator — bitta oraliq nuqta. izohlar o'sgan sari hajm_kb va ayniqsa findOne_ms qanday o'zgarishini kuzating:
{"izohlar":100,"hajm_kb":9,"push_ms":9.86,"findOne_ms":4.13}
{"izohlar":1000,"hajm_kb":90,"push_ms":8.08,"findOne_ms":6.96}
{"izohlar":5000,"hajm_kb":457,"push_ms":8.69,"findOne_ms":14.48}
{"izohlar":10000,"hajm_kb":916,"push_ms":15.54,"findOne_ms":72.72}
{"izohlar":20000,"hajm_kb":1853,"push_ms":22.98,"findOne_ms":95.71}
16 MB ga taxminan: 176838 izohda yetadi- findOne (mediana 5)
- bitta $push
| izohlar soni | findOne (mediana 5) | bitta $push |
|---|---|---|
| 100 | 4,13 | |
| 1 000 | 6,96 | |
| 5 000 | 14,48 | |
| 10 000 | 72,72 | |
| 20 000 | 95,71 | |
| 100 | 9,86 | |
| 1 000 | 8,08 | |
| 5 000 | 8,69 | |
| 10 000 | 15,54 | |
| 20 000 | 22,98 |
Manba: IlmHamroh kanon o'lchovi, 2026-10-25: MongoDB 8.3.13 (Docker, WSL2), mongodb 7.7.0, Node 24.21.0, i5-12500H; bitta tajriba
Grafikdan uch xulosa:
- O'qish chiziqli emas, keskin o'sdi. 100 izohda 4 ms, 20 000 da 96 ms. Har
findOnebutun hujjatni — 1.85 MB ni — tarmoqdan o'tkazib, JavaScript obyektiga aylantiradi. Vazifani ro'yxatda ko'rsatish uchun 20 000 izoh kerak emas edi, lekin ular baribir keldi. - Yozish ham sekinlashdi (10 → 23 ms): katta hujjatni yangilash qimmatroq.
- 16 MB hali uzoq (~177 000 izoh) — lekin muammo chegaraga yetmasdan ancha oldin boshlanadi.
Bu anti-naqsh hech qanday xato bermaydi. U asta-sekin, oylar davomida ilovani sekinlashtiradi. Mashhur vazifani ochganlar shikoyat qiladi, oddiy vazifalar esa tez ishlaydi — muammoni topish qiyin.
4. Subset naqshi: faqat kerakli qism
4.1 G'oya
Subset — hujjatda massivning faqat tez-tez kerak bo'ladigan qismi (oxirgi 5 izoh), hammasi esa alohida kolleksiyada. Ro'yxat sahifasi — bitta findOne. "Hamma izohlar" sahifasi — alohida kolleksiyadan, sahifalab.
Kanon har izohni ikki joyga yozdi — izohlar kolleksiyasiga va vazifaning oxirgiIzohlar iga:
await I.insertOne(izoh); // to'liq ro'yxat — alohida kolleksiya
await S.updateOne({ _id: 1 }, {
$push: { oxirgiIzohlar: {
$each: [{ matn: izoh.matn, vaqt: izoh.vaqt }],
$sort: { vaqt: -1 },
$slice: 5,
} },
$inc: { izohlarSoni: 1 },
});$push ning modifikatorlari:
$each— qo'shiladigan elementlar massivi (modifikator ishlatish uchun shart).$sort: { vaqt: -1 }— qo'shgandan keyin massivni vaqt bo'yicha kamayish tartibida saralash.$slice: 5— faqat birinchi 5 tasini qoldirish.
Natija: massiv hech qachon 5 tadan oshmaydi. Hujjat hajmi doim kichik.
4.2 Natija
2 000 izohdan keyin vazifa hujjati:
{"izohlarSoni":2000,"oxirgi":["Izoh raqami 2000","Izoh raqami 1999","Izoh raqami 1998","Izoh raqami 1997","Izoh raqami 1996"]}
subset findOne: 2.40 ms | 2-sahifa izohlar (keyset vaqt bo'yicha): 2.58 msfindOne— 2.40 ms: 20 000 izohli embed variantdagi 96 ms o'rniga.- 2-sahifa (keyingi 5 izoh) —
izohlarkolleksiyasidan{ vazifa: 1, vaqt: { $lt: … } }+sort+limit— 2.58 ms. Bu Keyset pagination ning o'zi;{ vazifa: 1, vaqt: -1 }indeksi bilan (ESR: tenglik, keyin saralash).
Tekshirib ko'ring: Subset naqshida foydalanuvchi bitta izohni o'chirdi. Qaysi joylarni yangilash kerak?
Javob
Uchta joyni: izohlar kolleksiyasidagi hujjatni, izohlarSoni ni ($inc: -1) va — agar izoh oxirgi 5 tasi orasida bo'lsa — oxirgiIzohlar massivini ($pull, keyin bo'shagan joyga izohlar dan navbatdagi izohni qo'shish). Subset'ning narxi aynan shu: ma'lumot ikki joyda.
Narxi: har izoh — ikki yozuv. Va ikki yozuv orasida jarayon yiqilsa, oxirgiIzohlar va izohlar farq qilib qolishi mumkin — kerak bo'lsa tranzaksiya (Replica set, tranzaksiyalar va change streams).
5. Computed naqshi: hisobni oldindan saqlash
Yuqoridagi $inc: { izohlarSoni: 1 } — computed naqshi. "Bu vazifada nechta izoh bor?" savoliga har o'qishda countDocuments qilish o'rniga, sonni har yozishda yangilab borish. Postgres'dagi counter cache (Denormalizatsiya va hisoblangan maydonlar).
Mongoose ilg'or darsidagi virtual izohlarSoni dan farqi: virtual har safar hisoblanadi va bazada yo'q; computed maydon bazada bor — u bo'yicha filtr, saralash va indeks mumkin ("eng ko'p muhokama qilingan vazifalar").
$inc atomik — ikki foydalanuvchi bir vaqtda izoh qo'shsa ham son to'g'ri. Xavf — izoh o'chirilganda $inc: -1 ni unutish. Vaqti-vaqti bilan haqiqiy son bilan solishtiradigan tekshiruv skripti foydali.
6. Bucket naqshi: vaqt qatorlari
6.1 Muammo
Har foydalanuvchi harakati (vazifani ko'rdi, bajardi) — hodisa. Har daqiqada bitta bo'lsa, bir kunda 1 440 ta hujjat. Har hujjatda _id, foydalanuvchi, vaqt takrorlanadi va har biri indeksga kiradi.
Bucket — hodisalarni vaqt oralig'i bo'yicha bitta hujjatga yig'ish: bir soat — bitta hujjat, ichida o'sha soatning hodisalari massivi.
6.2 O'lchov
const soat = new Date(h.vaqt);
soat.setUTCMinutes(0, 0, 0); // soat boshi — bucket kaliti
await B.updateOne(
{ foydalanuvchi: 7, soat },
{
$push: { hodisalar: { d: h.vaqt.getUTCMinutes(), amal: h.amal } },
$inc: { soni: 1, bajarish: h.amal === "bajarish" ? 1 : 0 },
},
{ upsert: true },
);Har hodisa: shu soatning hujjati bo'lmasa — upsert yaratadi, bo'lsa — massivga qo'shadi. Hodisa ichida to'liq vaqt emas, faqat daqiqa (d) — soat hujjat kalitida bor. $inc bilan bucket ichidagi computed sonlar.
{"alohida":{"hujjat":1440,"size_kb":102,"indeks_kb":4},"bucket":{"hujjat":24,"size_kb":49,"indeks_kb":4}}
kunlik 'bajarish' soni: alohida 480 | bucket (computed $inc) 480- 1 440 hujjat → 24 hujjat; 102 KB → 49 KB (takrorlanadigan maydonlar va
_idlar yo'qoldi). - Natija bir xil: kunlik "bajarish" — 480. Bucket'da bu son tayyor — 24 ta
bajarishmaydonini qo'shish yetadi, 1 440 hujjatni sanash shart emas. - Indeks hajmi ikkalasida 4 KB — bu kichik to'plamda farq ko'rinmadi; millionlab hodisada hujjatlar soni 60 baravar kam bo'lgani uchun indeks ham kichrayadi (hisob, sinalmagan).
MongoDB 5.0 dan beri bu naqsh bazaning o'zida bor — time series kolleksiyalar (bucket'lashni server o'zi qiladi; hujjat bo'yicha, kanon sinamadi).
7. Extended reference: nusxa bilan ishora
Extended reference — reference yoniga eng ko'p kerak bo'ladigan maydonlarning nusxasi. Vazifada egasi: ObjectId o'rniga egasi: { _id, ism }. Vazifalar ro'yxati egasi ismi bilan — populate siz, $lookup siz.
Narxi — ism o'zgarsa, hamma nusxa yangilanishi kerak. Kanon 1 000 vazifali foydalanuvchining ismini o'zgartirdi:
await E.updateMany(
{ "egasi._id": 1 },
{ $set: { "egasi.ism": "Aziz K." } },
);ism o'zgardi: 1000 nusxa yangilandi, 44.9 ms (indekssiz)1 000 nusxa — 44.9 ms. "egasi._id" — ichma-ich maydon yo'li (nuqta bilan); indeks qo'yilsa tezroq bo'lardi. Ism kamdan-kam o'zgaradi, ro'yxat esa har daqiqada o'qiladi — savdo foydali. Populate darsidagi qaror: tez o'zgaradigan maydonni (balans, qoldiq) nusxalamang.
Nusxa yana bir savol tug'diradi: updateMany yarim yo'lda to'xtasa? Ba'zi vazifalarda eski ism qoladi. Ko'pincha bu qabul qilinadi (ism — muhim emas). Muhim ma'lumot bo'lsa — tranzaksiya yoki change streams orqali sinxronlash (keyingi dars).
8. Outlier va attribute naqshlari
Bu ikkisini kanon o'lchamadi — hujjat bo'yicha, qisqa.
Outlier — "istisno" hujjatlar uchun alohida yo'l. 99% vazifada 50 tadan kam izoh, 1% da minglab. Hammasi uchun subset murakkablik keltirmaslik uchun: oddiy hujjatda izohlar ichida, chegara oshganda hujjatga ortiqcha: true bayrog'i qo'yilib, qolganlari alohida kolleksiyaga yoziladi. Ilova bayroqni ko'rganda ikkinchi so'rov yuboradi.
{ _id: 1, matn: "Kurs tugadi!", izohlar: [/* birinchi 50 */],
ortiqcha: true } // qolganlari: izohlar_ortiqcha kolleksiyasiAttribute — turli hujjatlarda turli nomli, lekin bir xil ma'noli maydonlar. Masalan, mahsulot xususiyatlari: telefonda xotira, ekran, ko'ylakda olcham, rang. Har biriga indeks qo'yish mumkin emas. Yechim — kalit-qiymat juftlari massivi va bitta indeks:
{ nom: "Telefon", xususiyatlar: [
{ k: "xotira", v: "128 GB" },
{ k: "ekran", v: "6.1" },
] }
// bitta multikey indeks: { "xususiyatlar.k": 1, "xususiyatlar.v": 1 }So'rov: { xususiyatlar: { $elemMatch: { k: "xotira", v: "128 GB" } } } (native driver darsidagi $elemMatch). Postgres'da shu muammo uchun JSONB va GIN indeks ishlatgandik (JSONB).
9. Schema versioning
Sxema o'zgaradi. Postgres'da bu migratsiya: ALTER TABLE, hamma qatorlar yangi shaklga (Migratsiya tushunchasi). Mongo'da millionlab hujjatni bir vaqtda o'zgartirish shart emas — eski va yangi shakl birga yashashi mumkin. Schema versioning — har hujjatga versiya raqami (v) va o'qishda moslashtirish.
Kanon: foydalanuvchi sozlamalari eski shaklda (mavzu yuqori darajada) va yangi shaklda (v: 2, sozlama.mavzu ichida):
await SV.insertMany([
{ foydalanuvchi: 1, mavzu: "qorongi" },
{ foydalanuvchi: 2, v: 2,
sozlama: { mavzu: "yorug", til: "uz-Latn" } },
]);
await SV.aggregate([{ $project: {
_id: 0, foydalanuvchi: 1,
v: { $ifNull: ["$v", 1] },
mavzu: { $ifNull: ["$sozlama.mavzu", "$mavzu"] },
} }]).toArray();[{"foydalanuvchi":1,"v":1,"mavzu":"qorongi"},{"foydalanuvchi":2,"v":2,"mavzu":"yorug"}]vyo'q hujjat — 1-versiya ($ifNull: ["$v", 1]).mavzu— yangi joydan, bo'lmasa eski joydan.
Tipik oqim: (1) ilova ikkala shaklni o'qiy oladigan qilinadi, (2) yangi yozuvlar v: 2 bilan, (3) fon skripti eski hujjatlarni asta-sekin yangilaydi, (4) hamma hujjat v: 2 bo'lgach, eski shaklni o'qish kodi olib tashlanadi. Bu Zero-downtime: expand-contract g'oyasining hujjat bazasidagi shakli.
Tekshirib ko'ring: 3-versiyada
mavzuyana boshqa joyga —korinish.mavzuga ko'chdi. O'qishdagi$ifNullqanday o'zgaradi?
Javob
$ifNull bir nechta argument oladi va birinchi null bo'lmaganini qaytaradi: { $ifNull: ["$korinish.mavzu", "$sozlama.mavzu", "$mavzu"] } — eng yangi joydan eskisiga qarab. Versiya ifodasi { $ifNull: ["$v", 1] } o'zgarmaydi: 3-versiyali hujjatlar v: 3 bilan yoziladi.
10. Naqshlar jadvali
| Naqsh | Muammo | Yechim | Narxi |
|---|---|---|---|
| Subset | Katta massiv o'qishni sekinlatadi | Hujjatda oxirgi N ta, hammasi alohida | Ikki yozuv |
| Computed | Har o'qishda sanash | Sonni $inc bilan saqlash |
O'chirishda kamaytirishni unutmaslik |
| Bucket | Juda ko'p mayda hujjat | Vaqt oralig'i — bitta hujjat | So'rovlar murakkablashadi |
| Extended reference | Har o'qishda $lookup |
Kerakli maydonlar nusxasi | Nusxalarni yangilash |
| Outlier | Kam hujjat hammani murakkablashtiradi | Istisnolarga alohida yo'l | Ilovada ikki yo'l |
| Attribute | Ko'p turli maydon, ko'p indeks | { k, v } massivi |
So'rov $elemMatch bilan |
| Schema versioning | Sxema o'zgarishi | v maydoni, o'qishda moslash |
Kodda bir necha shakl |
11. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Postgres'da: izohlar alohida jadvalda (sahifalash bilan), sonlar — so'rovda yoki materialized view'da, sxema o'zgarishi — migratsiya bilan. Kanon Mongo'da:
- 17 MB hujjat — drayverning
RangeErrori (server emas). - Cheksiz massiv: 20 000 izoh — 1.85 MB,
findOne4 → 96 ms. - Subset + computed:
findOne2.40 ms, keyingi sahifa 2.58 ms. - Bucket: 1 440 → 24 hujjat, 102 → 49 KB, son bir xil (480).
- Extended reference: 1 000 nusxa — 44.9 ms; schema versioning —
$ifNull.
12. Ko'p uchraydigan xatolar
| Xato | Oqibat | To'g'risi |
|---|---|---|
| Cheksiz o'sadigan massivni embed qilish | O'qish va yozish asta-sekin sekinlashadi | Subset yoki reference |
| Fayl yoki uzun logni hujjat ichida | 16 MB, chalg'ituvchi drayver xatosi | Obyekt ombori/GridFS + havola |
| Computed sonni faqat qo'shishda yangilash | O'chirishdan keyin son noto'g'ri | $inc: -1 ham, davriy tekshiruv |
| Tez o'zgaradigan maydonni nusxalash | Nusxalar bir-biridan farq qiladi | Reference |
| Versiyasiz sxema o'zgarishi | Eski hujjatlar o'qishda yiqitadi | v maydoni va $ifNull |
$push + $slice da $each yo'q |
Modifikatorlar ishlamaydi | $each: [...] majburiy |
13. Mashqlar
Mashqlar kurs/mashqlar/27/50-naqshlar/ da. 2- va 3-mashq yechimlari Node 24 da tekshirilgan (biz ishga tushirdik, bazasiz).
1-mashq (oson): qaysi naqsh?
- Sahifada faqat oxirgi 3 ta sharh kerak, qolganlari "Hammasi" tugmasida —
- Postda layklar sonini har safar sanamaslik —
- Har soniyadagi harorat o'lchovini ixcham saqlash —
- Buyurtmada mahsulot nomi va narxi nusxasi —
- Ba'zi hujjatlarda
telefon, ba'zilaridaaloqa.telefon—
Yechim
- subset — hujjatda N ta, hammasi alohida.
- computed —
$incbilan saqlangan son. - bucket — masalan, daqiqa yoki soat — bitta hujjat.
- extended reference — buyurtma paytidagi nom va narx; narx keyin o'zgarsa ham buyurtmada eskisi qolishi to'g'ri.
- schema versioning —
vmaydoni va o'qishda$ifNull.
2-mashq (o'rta): soatlik bucket
bucketla(hodisalar) yozing: daqiqalik hodisalar ({ vaqt: Date, amal }) massividan soatlik bucket'lar massivini qaytarsin: { soat: "HH", soni, bajarish, hodisalar: [{ d, amal }] }. Kanon ma'lumotini qayta tuzing: 2026-10-24 kuni 1 440 daqiqa, har uchinchi daqiqa (i % 3 === 0) — "bajarish", qolgani — "korish". Bucket'lar sonini, birinchi bucket'ning soni va bajarish ini va kunlik bajarish yig'indisini chiqaring.
Ishora: soat kaliti — getUTCHours(); Map bilan guruhlash.
Yechim
function bucketla(hodisalar) {
const m = new Map();
for (const h of hodisalar) {
const soat = String(h.vaqt.getUTCHours()).padStart(2, "0");
const b = m.get(soat)
?? { soat, soni: 0, bajarish: 0, hodisalar: [] };
b.hodisalar.push({ d: h.vaqt.getUTCMinutes(), amal: h.amal });
b.soni += 1;
b.bajarish += h.amal === "bajarish" ? 1 : 0;
m.set(soat, b);
}
return [...m.values()];
}
const kun = Array.from({ length: 1440 }, (_, i) => ({
vaqt: new Date(Date.UTC(2026, 9, 24, 0, i)),
amal: i % 3 ? "korish" : "bajarish",
}));
const b = bucketla(kun);
console.log("bucket'lar:", b.length);
console.log(b[0].soat, b[0].soni, b[0].bajarish,
JSON.stringify(b[0].hodisalar.slice(0, 2)));
console.log("kunlik bajarish:",
b.reduce((s, x) => s + x.bajarish, 0));
const alohida = JSON.stringify(kun).length;
const yigilgan = JSON.stringify(b).length;
const foiz = Math.round(yigilgan / alohida * 100);
console.log("JSON hajmi:", foiz + "%");Konsolda:
bucket'lar: 24
00 60 20 [{"d":0,"amal":"bajarish"},{"d":1,"amal":"korish"}]
kunlik bajarish: 480
JSON hajmi: 50%24 bucket va kunlik 480 — kanon bilan bir xil. Har soatda 60 hodisa, 20 tasi "bajarish". JSON hajmi 50% — kanondagi BSON nisbatiga (102 → 49 KB, ya'ni 48%) juda yaqin. Sabab bir xil: har hodisada to'liq sana va takrorlanadigan maydonlar yo'qoldi.
3-mashq (qiyin): $push + $each + $sort + $slice
subsetPush(massiv, yangilar, saralash, n) yozing — MongoDB'dagi $push: { $each, $sort, $slice } ning modeli: yangilarni qo'shib, saralash ({ maydon: 1 | -1 }) bo'yicha tartiblab, birinchi n tasini qaytarsin (asl massivga tegmasin). Kanon tajribasini takrorlang: 2 000 izoh bittadan qo'shiladi, har birida izohlarSoni ham oshadi. Oxirida izohlarSoni va oxirgi matnlarni chiqaring.
Ishora: toSorted — nusxa bilan o'zgartirish.
Yechim
function subsetPush(massiv, yangilar, saralash, n) {
const [[k, yon]] = Object.entries(saralash);
return [...massiv, ...yangilar]
.toSorted((a, b) => (a[k] - b[k]) * yon)
.slice(0, n);
}
let vazifa = { oxirgiIzohlar: [], izohlarSoni: 0 };
for (let i = 1; i <= 2000; i++) {
const izoh = {
matn: `Izoh raqami ${i}`,
vaqt: new Date(1.7e12 + i * 60000),
};
vazifa = {
oxirgiIzohlar: subsetPush(vazifa.oxirgiIzohlar, [izoh],
{ vaqt: -1 }, 5),
izohlarSoni: vazifa.izohlarSoni + 1,
};
}
console.log(JSON.stringify({
izohlarSoni: vazifa.izohlarSoni,
oxirgi: vazifa.oxirgiIzohlar.map((x) => x.matn),
}));Konsolda:
{"izohlarSoni":2000,"oxirgi":["Izoh raqami 2000","Izoh raqami 1999","Izoh raqami 1998","Izoh raqami 1997","Izoh raqami 1996"]}Kanon chiqishi bilan aynan bir xil. Massiv 2 000 qadam davomida hech qachon 5 tadan oshmadi. a[k] - b[k] — ikki Date ayirmasi millisekundlarda; * yon — -1 da teskari tartib. MongoDB'da bu bitta atomik updateOne ichida bajariladi.
14. Real ishda
- Ijtimoiy tarmoq va chat — subset (oxirgi xabarlar hujjatda), computed (layklar, javoblar soni), outlier (mashhur postlar).
- IoT va monitoring — bucket yoki time series kolleksiyalar; sensor har soniyada yozadi.
- E-commerce — extended reference (buyurtmada mahsulot nomi va narxi), attribute (turli xususiyatli mahsulotlar).
- Intervyu savollari: "Mongo'da bitta postga million izoh — qanday saqlaysiz?", "16 MB chegaraga qanday yetmaslik kerak?", "sxemani to'xtovsiz qanday o'zgartirasiz?".
Xulosa
- 16 MB — qattiq chegara; 17 MB hujjatni drayverning o'zi
RangeErrorbilan to'xtatdi. - Cheksiz massiv — eng xavfli anti-naqsh: 20 000 izoh — 1.85 MB,
findOne4 → 96 ms, xatosiz. - Subset (
$push+$each/$sort/$slice) + computed ($inc) —findOne2.40 ms; to'liq ro'yxat alohida, keyset bilan. - Bucket: 1 440 → 24 hujjat, 102 → 49 KB; extended reference: o'qish tez, 1 000 nusxa yangilash 44.9 ms.
- Outlier va attribute — istisnolar va turli maydonlar uchun; schema versioning —
vva$ifNullbilan sxema to'xtovsiz o'zgaradi.
Keyingi dars: Replica set, tranzaksiyalar va change streams — write concern narxi, bir nechta hujjatli tranzaksiya va o'zgarishlarni real vaqtda tinglash.
Manbalar
- MongoDB hujjati — Data Modeling: Schema Design Patterns: https://www.mongodb.com/docs/manual/data-modeling/design-patterns/
- MongoDB blogi — "Building with Patterns" turkumi: https://www.mongodb.com/blog/post/building-with-patterns-a-summary (2026-10-26 holatiga)
- MongoDB hujjati —
$pushmodifikatorlari ($each,$sort,$slice): https://www.mongodb.com/docs/manual/reference/operator/update/push/ - MongoDB hujjati — Time Series Collections, BSON Document Size Limit: https://www.mongodb.com/docs/manual/core/timeseries-collections/ , https://www.mongodb.com/docs/manual/reference/limits/
- Kanon o'lchovi (IlmHamroh, 2026-10): MongoDB 8.3.13 (Docker, WSL2), mongodb 7.7.0, Node 24.21.0, i5-12500H. Mashq yechimlari — biz ishga tushirdik (Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!