Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ikki usul: embed va reference
- 2.1 Hujjat shaklida
- 2.2 Bitta hujjat — atomiklik birligi
- 3. populate: reference'ni ochish
- 3.1 ref va populate
- 3.2 Necha so'rov ketdi?
- 3.3 $lookup — bazadagi birlashtirish
- 4. O'lchov: embed, populate, $lookup
- 5. Qaror qoidasi
- 5.1 Bog'lanish soni: few, many, squillions
- 5.2 Savollar ketma-ketligi
- 5.3 O'qish va yozish nisbati
- 6. vazifalar uchun qaror — kanon shakli
- 7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): embed yoki reference?
- 2-mashq (o'rta): qo'lda populate
- 3-mashq (qiyin): ichma-ich yo'l
- 10. Real ishda
- Xulosa
- Manbalar
Mongoose populate va embed vs reference: MongoDB'da bog'lanishni to'g'ri tanlash
Qisqacha: MongoDB'da bog'liq ma'lumot ikki xil saqlanadi: embed — hujjat ichiga joylash (vazifa ichida teglar va izohlar), reference — boshqa hujjatning
_idsini saqlash (vazifadaegasi). Mongoosepopulatereference'ni "ochadi", lekin JOIN qilmaydi — har yo'l uchun alohida so'rov yuboradi: kanonda bitta vazifa uchun 3 so'rov. 200 vazifada: embed 6.59 ms, populate 12.46 ms,$lookup26.82 ms. Qaror qoidasi: birga o'qiladigan, soni chegaralangan narsa — embed; mustaqil yashaydigan yoki cheksiz o'sadigan narsa — reference.
Bu darsda
- Embed va reference'ni hujjat shaklida farqlaysiz.
refvapopulatebilan bog'liq hujjatni yuklaysiz, ichma-ich yo'l (izohlar.muallif) bilan ham.populatenechta so'rov yuborishini debug logida ko'rasiz va$lookupbilan solishtirasiz.- Bog'lanish sonini (one-to-few, one-to-many, one-to-squillions) va o'qish/yozish nisbatini hisobga olib qaror qilasiz.
Oldin bilishingiz kerak: Mongoose CRUD va update operatorlari, Bog'lanishlar: 1:1, 1:N, N:M, Denormalizatsiya va hisoblangan maydonlar, N+1 muammosi.
1. Nega bu kerak?
Postgres'da bog'lanish haqida o'ylash shart emasdi: har narsa alohida jadvalda, tashqi kalit bilan, va JOIN ularni o'qishda birlashtiradi (Bog'lanishlar: 1:1, 1:N, N:M). Yagona to'g'ri shakl — normalizatsiya (Normalizatsiya).
Mongo'da esa har bog'lanish uchun tanlov qilasiz. Kanon vazifa hujjatida ikkala usul bor edi:
teglarvaizohlar— vazifaning ichida (embed).egasi— faqat ObjectId, foydalanuvchi alohida kolleksiyada (reference).
Nega bunday? Va qachon teskarisi to'g'ri? Bu Mongo'da modellashning eng muhim savoli. Noto'g'ri tanlov oylar o'tib chiqadi: hujjat 16 MB ga yetadi, yoki har sahifa o'nlab so'rov yuboradi.
O'xshatish: telefoningizdagi kontaktlar. Yaqin oila a'zolarining raqami eslab qolingan — kerak bo'lsa darhol terasiz (embed). Butun shahar telefon kitobi esa alohida — kerak bo'lganda qidirib topasiz (reference). Hamma raqamni yodlash mumkin emas, va oilangiz raqamini har safar kitobdan qidirish — vaqtni behuda sarflash.
Chiqishlar — kanon o'lchovi (IlmHamroh, 2026-10): Node 24.21.0, mongoose 9.11.2, MongoDB 8.3.13. Kolleksiya nomlari — vazifas, foydalanuvchis (Mongoose schema darsidagi tuzoq).
2. Ikki usul: embed va reference
2.1 Hujjat shaklida
Bitta vazifa ikki usulda:
// Embed: izohlar vazifaning ichida
{
_id: ObjectId("…a1"),
matn: "Non olish #uy",
izohlar: [
{ muallif: ObjectId("…f1"), matn: "Tez" },
{ muallif: ObjectId("…f2"), matn: "Men ham" },
],
}
// Reference: izohlar alohida kolleksiyada
// vazifalar:
{ _id: ObjectId("…a1"), matn: "Non olish #uy" }
// izohlar:
{ _id: ObjectId("…i1"), vazifa: ObjectId("…a1"), matn: "Tez" }
{ _id: ObjectId("…i2"), vazifa: ObjectId("…a1"), matn: "Men ham" }| Embed | Reference | |
|---|---|---|
| O'qish | bitta so'rov — hammasi birga | ikki so'rov yoki $lookup |
| Yozish | bitta hujjatni atomik o'zgartirish | ikki kolleksiyaga |
| Alohida o'qish | qiyin (hujjat ichidan qidirish) | oson — o'z kolleksiyasi |
| O'sish | hujjat kattalashadi (16 MB chegara) | cheklovsiz |
| Takrorlanish | bir ma'lumot ko'p nusxada bo'lishi mumkin | bitta nusxa |
Embed — denormalizatsiyaning Mongo'dagi tabiiy shakli. Reference — Postgres'dagi tashqi kalitga o'xshaydi, lekin baza uni tekshirmaydi: o'chirilgan foydalanuvchiga ishora qilib turgan "yetim" ObjectId'ni hech kim to'xtatmaydi (yetim qator muammosi qaytadi).
2.2 Bitta hujjat — atomiklik birligi
MongoDB'da bitta hujjatga yozish doim atomik: ichidagi bir nechta maydon birga o'zgaradi yoki hech biri. Kanon native driver darsida bitta updateOne da $set, $inc va $addToSet ni birga qo'lladi — oraliq holat hech kimga ko'rinmadi.
Bu embed'ning katta yutug'i. Vazifa va uning izohlari bitta hujjatda bo'lsa, "izoh qo'shish + izohlar sonini oshirish" tranzaksiyasiz atomik. Reference'da esa bu ikki kolleksiyaga yozish — atomiklik uchun tranzaksiya kerak (Replica set, tranzaksiyalar va change streams).
Tekshirib ko'ring: Do'kon buyurtmasida yetkazish manzili bor. Mijoz keyinroq profilidagi manzilni o'zgartirdi. Eski buyurtmada qaysi manzil ko'rinishi kerak? Bu embed'ga yoki reference'ga dalilmi?
Javob
Eski buyurtmada buyurtma paytidagi manzil ko'rinishi kerak — tovar o'sha joyga ketgan. Shuning uchun manzilni buyurtma ichiga nusxalash (embed) to'g'ri. Bu yerda takrorlanish xato emas, balki tarixiy fakt. Reference qilsangiz, eski buyurtma yangi manzilni ko'rsatib, hisobotni buzadi.
3. populate: reference'ni ochish
3.1 ref va populate
Mongoose schema darsida egasi va izoh muallif iga ref: "Foydalanuvchi" yozgan edik. Mongo uchun bu hech narsa emas, Mongoose uchun esa ko'rsatma: "bu ObjectId — Foydalanuvchi modelining hujjati".
populate(yo'l, maydonlar) shu ObjectId'larni haqiqiy hujjatlar bilan almashtiradi. Kanon Dilnozani yaratib, vazifaga uning izohini qo'shdi, keyin ikkala reference'ni ochdi:
const dilnoza = await Foydalanuvchi.create({
email: "dilnoza@example.com", ism: "Dilnoza",
});
await Vazifa.updateOne({ _id: v1._id }, {
$push: { izohlar: { muallif: dilnoza._id, matn: "Men ham" } },
});
const pop = await Vazifa.findById(v1._id)
.populate("egasi", "ism")
.populate("izohlar.muallif", "ism")
.lean();
console.log("populate natijasi:", pop.egasi.ism, "|",
pop.izohlar.map((i) => `${i.muallif.ism}: ${i.matn}`).join("; "));populate natijasi: Aziz Karimov | Aziz Karimov: Tez; Dilnoza: Men hampop.egasi endi ObjectId emas — { _id, ism } obyekti. Ikkinchi argument "ism" — projection: foydalanuvchining faqat ismi kerak (email, parol hash va boshqalar tortilmaydi). "izohlar.muallif" — ichma-ich yo'l: massivdagi har izohning muallifi.
3.2 Necha so'rov ketdi?
Kanon so'rov oldidan Mongoose'ning debug rejimini yoqdi — har bazaga ketgan so'rov logga yoziladi:
mongoose.set("debug", (col, metod, sorov) =>
console.log(` [debug] ${col}.${metod}(${JSON.stringify(sorov)})`));Logdagi har qator — bazaga ketgan bitta so'rov. Qatorlarni sanang va $in ichidagi id'larga qarang:
[debug] vazifas.findOne({"_id":"6add9ac27f3d15aa25a380b5"})
[debug] foydalanuvchis.find({"_id":{"$in":["6add9ac27f3d15aa25a380b1"]}})
[debug] foydalanuvchis.find({"_id":{"$in":["6add9ac27f3d15aa25a380b1","6add9ac37f3d15aa25a380bf"]}})Uchta so'rov:
- Vazifaning o'zi.
egasiuchun — Azizning id'si bilan.izohlar.muallifuchun — ikki muallif id'si$inbilan bitta so'rovda.
Demak populate — JOIN emas. U N+1 muammosi darsidagi batch usulining o'zi: avval asosiy hujjatlar, keyin har yo'l uchun hamma id'larni yig'ib { _id: { $in: [...] } } bilan bitta so'rov. 100 ta vazifani populate qilsangiz ham — 3 so'rov, 101 emas. N+1 dan yaxshi, lekin har populate yo'li — qo'shimcha tarmoq aylanishi.
E'tibor bering: Aziz ikki marta so'raldi — 2- va 3-so'rovda. Mongoose har yo'lni alohida yuklaydi va ular orasida keshni bo'lishmaydi.
sequenceDiagram
participant I as Ilova
participant M as Mongoose
participant D as MongoDB
I->>M: findById().populate(egasi).populate(izohlar.muallif)
M->>D: vazifas.findOne(_id)
D-->>M: vazifa (egasi, muallif id'lari)
M->>D: foydalanuvchis.find(_id $in [egasi])
M->>D: foydalanuvchis.find(_id $in [mualliflar])
D-->>M: foydalanuvchilar
M-->>I: id'lar o'rnida hujjatlarDiagrammaga qarang: birinchi javob kelmaguncha qolgan so'rovlarni yuborib bo'lmaydi — Mongoose qaysi id'larni so'rashni bilmaydi. Shuning uchun populate vaqti kamida ikki ketma-ket aylanish.
3.3 $lookup — bazadagi birlashtirish
Mongo'ning o'zida ham JOIN'ga o'xshash narsa bor — aggregation bosqichi $lookup. U boshqa kolleksiyadan mos hujjatlarni server ichida biriktiradi. Kanon variant (qatorlar bo'lingan):
await Vazifa.aggregate([
{ $match: { matn: /^Ommaviy/ } },
{ $lookup: {
from: "foydalanuvchis",
localField: "egasi",
foreignField: "_id",
as: "egasi",
pipeline: [{ $project: { ism: 1 } }],
} },
]);from — kolleksiya nomi (model nomi emas — shuning uchun foydalanuvchis!), localField/foreignField — qaysi maydonlar teng bo'lishi kerak, as — natija qaysi maydonga massiv bo'lib tushsin. $lookup natijasi doim massiv: bitta egasi bo'lsa ham [{ … }]. Aggregation'ni keyingi-keyingi darsda to'liq ko'ramiz.
4. O'lchov: embed, populate, $lookup
Kanon 200 ta vazifa yaratdi (egasi — Aziz yoki Dilnoza, har birida bitta izoh) va uch usulda o'qidi — har biri 15 marta, mediana:
const tEmbed = await olcha(15, () =>
Vazifa.find({ matn: /^Ommaviy/ }).lean());
const tPop = await olcha(15, () =>
Vazifa.find({ matn: /^Ommaviy/ }).populate("egasi", "ism").lean());
// tLookup — yuqoridagi aggregate200 vazifa: embed (faqat o'zi) 6.59 ms, populate egasi 12.46 ms, $lookup 26.82 ms (mediana 15)- Faqat vazifalar (embed)6,59 ms
- populate egasi12,46 ms
- $lookup egasi26,82 ms
Manba: IlmHamroh kanon o'lchovi, 2026-10-25: Node 24.21.0, mongoose 9.11.2, MongoDB 8.3.13 (Docker, WSL2), i5-12500H; 15 marta, mediana
Uch usulni yonma-yon qo'yamiz:
| Usul | Bazaga so'rovlar | Birlashtirish qayerda | Kanon, 200 vazifa |
|---|---|---|---|
| Embed (faqat hujjat) | 1 | birlashtirish yo'q | 6.59 ms |
populate("egasi") |
2 (vazifalar + $in) |
Node'da, Mongoose | 12.46 ms |
$lookup |
1 (aggregate) | MongoDB serverida | 26.82 ms |
Nimani ko'ryapmiz:
- Embed — eng tez: bitta so'rov, qo'shimcha ish yo'q. (Bu yerda "embed" — egasi ismisiz o'qish; egasi ismi hujjat ichida nusxa bo'lganda ham xuddi shu vaqt bo'lardi — bu extended reference naqshi.)
- populate — ikki baravar: qo'shimcha so'rov va Mongoose'ning JavaScript'dagi biriktirish ishi.
$lookup— bu o'lchovda eng sekin. Kutilmagan natija: "baza ichida birlashtirish tezroq bo'lishi kerak" deb o'ylash tabiiy. Kanon$lookupni$matchbilan, lekinmatnindeksisiz o'lchadi;$lookuphar hujjat uchunfoydalanuvchisdan qidiradi. Bitta o'lchov, bitta mashina — umumiy qonun emas.
Xulosa: "baza ichida — doim tez" degan intuitsiyaga ishonmang, o'lchang. Va eng tez yo'l — umuman birlashtirmaslik: kerakli ma'lumot allaqachon hujjatda bo'lsa.
Postgres bilan solishtirish uchun: N+1 muammosi darsida 50 vazifa izoh va teglari bilan bitta so'rovda 5.0 ms edi. Bu boshqa o'lchov va boshqa hajm — to'g'ridan solishtirib bo'lmaydi. Lekin JOIN Postgres'ning kuchli tomoni ekanini eslatadi.
Tekshirib ko'ring: Sahifada 20 ta vazifa, har biri egasi ismi va izoh mualliflari bilan.
populate("egasi").populate("izohlar.muallif")necha so'rov yuboradi?
Javob
3 ta: vazifalar uchun bitta, egasilar uchun bitta ($in bilan 20 tagacha id), mualliflar uchun bitta. Vazifalar soni 20 mi yoki 200 mi — so'rovlar soni o'zgarmaydi; faqat $in ro'yxati uzayadi. Kanon debug logida bitta vazifa uchun ham aynan 3 ta edi.
5. Qaror qoidasi
5.1 Bog'lanish soni: few, many, squillions
MongoDB jamoasi tavsiya qiladigan amaliy bo'linish (MongoDB blogi va hujjatidagi "6 Rules of Thumb" maqolasi — hujjat bo'yicha):
| Tur | Misol | Tavsiya |
|---|---|---|
| one-to-few (bir necha, o'nlab) | vazifa → teglar, foydalanuvchi → manzillar | embed |
| one-to-many (yuzlab, minglab) | mahsulot → ehtiyot qismlar | ota hujjatda id'lar massivi yoki bolada ota id'si |
| one-to-squillions (cheksiz) | server → log yozuvlari | bolada ota id'si (vazifa: ObjectId) |
"Squillions" — inglizcha hazil so'z: "son-sanoqsiz". Vazifa izohlari qaysi turga kiradi? Odatda bir nechta — embed. Lekin mashhur vazifa (yoki ijtimoiy tarmoqdagi post) minglab izoh yig'ishi mumkin. Kanon buni o'lchadi: 20 000 izohli hujjatda findOne 4 ms dan 96 ms ga sekinlashdi — sxema dizayn naqshlari darsida to'liq raqamlar bilan.
5.2 Savollar ketma-ketligi
flowchart TD
A["Bog'liq ma'lumot"] --> B{"O'zi alohida so'raladimi?"}
B -->|ha| R["reference"]
B -->|yo'q| C{"Soni chegaralanganmi?"}
C -->|"yo'q, cheksiz o'sadi"| R
C -->|ha| D{"Ko'p joyda takrorlanib, tez-tez o'zgaradimi?"}
D -->|ha| R
D -->|yo'q| E["embed"]Diagrammaga qarang — uchta savol:
- Alohida so'raladimi? "Dilnozaning hamma izohlari" sahifasi bo'lsa, izohlar vazifa ichida bo'lishi noqulay: har vazifani ochib qidirish kerak. Reference.
- Chegaralanganmi? Teglar — 3–5 ta. Izohlar — bilmaymiz. Cheksiz o'sadigan massiv — reference.
- Takrorlanib, o'zgaradimi? Foydalanuvchi ismi 1 000 ta vazifada nusxa bo'lsa va ism tez-tez o'zgarsa — 1 000 ta nusxani yangilash kerak. Kanon buni ham o'lchadi: 1 000 nusxa
updateMany44.9 ms (sxema dizayn naqshlari). Ism kamdan-kam o'zgarsa — nusxa (embed) arzon; tez-tez — reference.
5.3 O'qish va yozish nisbati
Oxirgi omil — ish rejimi. Ko'p ilovalarda o'qish yozishdan o'nlab marta ko'p: vazifalar ro'yxati har sahifa ochilganda o'qiladi, egasi ismi esa yiliga bir marta o'zgaradi. Bunday holatda nusxalash (embed) o'qishni tez qiladi, yozishni biroz qimmat qiladi — va bu yaxshi savdo.
Teskari holat — tez-tez o'zgaradigan narsa (mahsulot qoldig'i, narx) — ko'p joyda nusxalanmasin: har o'zgarishda ko'p hujjat yangilanadi va nusxalar bir-biridan farq qilib qolish xavfi bor.
Mongo'da sxemani so'rovlardan boshlab loyihalaysiz: "ilova qanday savollar beradi?" Postgres'da esa avval ma'lumotni normal shaklga keltirib, savollarni keyin JOIN bilan yozasiz. Bu ikki bazaning falsafiy farqi.
6. vazifalar uchun qaror — kanon shakli
| Bog'lanish | Tanlov | Sabab |
|---|---|---|
| vazifa → teglar | embed (["ish", "uy"]) |
3–5 ta, doim vazifa bilan o'qiladi |
| vazifa → izohlar | embed (kanon) | odatda bir necha; lekin cheksizlik xavfi — naqshlar darsida |
| vazifa → egasi | reference | foydalanuvchi alohida yashaydi, ko'p vazifada |
| izoh → muallif | reference | xuddi shu |
Kanon ko'chirishda (native driver) shu shaklni tanladi. Postgres'dagi teglar + vazifa_teglari jadvallari — Mongo'da bitta satrlar massivi. Teglarning o'zi ma'lumot (rang, tavsif) bo'lganda — ular alohida kolleksiyaga chiqardi.
7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Postgres'da: u yerda bog'lanish — tashqi kalit va JOIN, populate muammosi yo'q. Kanon Mongo'da sinagani:
populate("egasi", "ism")va ichma-ichpopulate("izohlar.muallif", "ism").- Debug logida 3 so'rov — populate'ning batch tabiati.
- 200 vazifa: embed 6.59 ms, populate 12.46 ms,
$lookup26.82 ms.
8. Ko'p uchraydigan xatolar
| Xato | Oqibat | To'g'risi |
|---|---|---|
| Hamma narsani embed qilish | Hujjat cheksiz o'sadi, 16 MB, sekin o'qish | Cheksiz bog'lanish — reference |
| Hamma narsani reference qilish ("Postgres'dagidek") | Har sahifa ko'p populate, sekin | Birga o'qiladigan kichik ma'lumot — embed |
populate siz projection |
Foydalanuvchining parol hash'i ham javobga tushadi | populate("egasi", "ism") |
$lookup da model nomi (Foydalanuvchi) |
Bo'sh massiv, xato yo'q | Kolleksiya nomi (foydalanuvchis) |
| O'chirilgan hujjatga ishora qolishi | populate null beradi, pop.egasi.ism → TypeError |
O'chirishda bog'liqlarni ham tozalash yoki ?. |
populate = JOIN deb o'ylash |
Qo'shimcha so'rovlar e'tiborsiz qoladi | Debug log bilan sanang |
9. Mashqlar
Mashqlar kurs/mashqlar/27/46-populate/ da. 2- va 3-mashq yechimlari Node 24 da tekshirilgan (biz ishga tushirdik — populate'ning kichik modeli, bazasiz).
1-mashq (oson): embed yoki reference?
Har holat uchun yozing: yoki reference.
- Foydalanuvchining 2–3 ta yetkazish manzili —
- Foydalanuvchi va uning yillar davomidagi hamma buyurtmalari —
- Vazifaning teglari —
- Telegram kanaldagi post va uning ko'rishlar logi —
Yechim
- embed — bir nechta, doim profil bilan o'qiladi.
- reference — cheksiz o'sadi, alohida sahifada so'raladi (buyurtmada
mijoz: ObjectId). - embed — 3–5 ta satr, vazifa bilan birga.
- reference — one-to-squillions: har ko'rish — alohida hujjat (yoki sanoq — bucket naqshi).
2-mashq (o'rta): qo'lda populate
populate(hujjatlar, maydon, yukla) yozing: hujjatlar dagi maydon id'larini yig'ib, takrorlarsiz, yukla(idlar) ni bir marta chaqirsin va id o'rniga topilgan obyektni qo'ysin (topilmasa null). Asl hujjatlarni o'zgartirmasin. So'rovlar sonini sanang.
Ishora: new Set bilan takrorsiz, Map bilan id → hujjat (N+1 muammosi darsidagi batch).
Yechim
const foydalanuvchilar = [
{ _id: "f1", ism: "Aziz Karimov" },
{ _id: "f2", ism: "Dilnoza" },
];
let sorovlar = 0;
async function yukla(idlar) {
sorovlar++;
return foydalanuvchilar.filter((f) => idlar.includes(f._id));
}
async function populate(hujjatlar, maydon, yukla) {
const idlar = [...new Set(hujjatlar.map((h) => h[maydon]))];
const topildi = new Map(
(await yukla(idlar)).map((x) => [x._id, x]),
);
return hujjatlar.map((h) => ({
...h,
[maydon]: topildi.get(h[maydon]) ?? null,
}));
}
const vazifalar = [
{ matn: "Non olish", egasi: "f1" },
{ matn: "Hisobot", egasi: "f1" },
{ matn: "Kitob", egasi: "f2" },
{ matn: "Eski", egasi: "f9" },
];
const natija = await populate(vazifalar, "egasi", yukla);
for (const v of natija) {
console.log(`${v.matn}: ${v.egasi?.ism ?? "(egasi yo'q)"}`);
}
console.log("so'rovlar:", sorovlar, "| asl:", vazifalar[0].egasi);Konsolda:
Non olish: Aziz Karimov
Hisobot: Aziz Karimov
Kitob: Dilnoza
Eski: (egasi yo'q)
so'rovlar: 1 | asl: f1To'rt vazifa — bitta so'rov. f9 — o'chirilgan foydalanuvchiga "yetim" ishora: populate null qaytaradi va ?. usiz v.egasi.ism TypeError bilan yiqilardi. Mongoose populate ham topilmaganda null qo'yadi.
3-mashq (qiyin): ichma-ich yo'l
populateIchki(hujjatlar, massiv, maydon, yukla) yozing: har hujjatning massiv (masalan izohlar) elementlaridagi maydon (muallif) id'larini hamma hujjatlardan yig'ib, bitta so'rov bilan to'ldirsin. Kanon misolini qayta tuzing: bitta vazifa, egasi Aziz, ikki izoh (Aziz — "Tez", Dilnoza — "Men ham"); egasi va mualliflar uchun ikkita alohida populate. Natijani kanon formatida chiqaring va so'rovlarni sanang.
Ishora: flatMap — flat va flatMap.
Yechim
const foydalanuvchilar = [
{ _id: "f1", ism: "Aziz Karimov" },
{ _id: "f2", ism: "Dilnoza" },
];
const log = [];
async function yukla(idlar) {
log.push(`foydalanuvchis.find(_id $in ${JSON.stringify(idlar)})`);
return foydalanuvchilar.filter((f) => idlar.includes(f._id));
}
async function xarita(idlar) {
return new Map((await yukla(idlar)).map((x) => [x._id, x]));
}
async function populate(hujjatlar, maydon) {
const idlar = hujjatlar.map((h) => h[maydon]);
const m = await xarita([...new Set(idlar)]);
return hujjatlar.map((h) => ({
...h, [maydon]: m.get(h[maydon]) ?? null,
}));
}
async function populateIchki(hujjatlar, massiv, maydon) {
const idlar = hujjatlar
.flatMap((h) => h[massiv].map((e) => e[maydon]));
const m = await xarita([...new Set(idlar)]);
return hujjatlar.map((h) => ({
...h,
[massiv]: h[massiv].map((e) => ({
...e, [maydon]: m.get(e[maydon]) ?? null,
})),
}));
}
const vazifalar = [{
matn: "Non olish", egasi: "f1",
izohlar: [
{ muallif: "f1", matn: "Tez" },
{ muallif: "f2", matn: "Men ham" },
],
}];
log.push("vazifas.findOne(...)");
let v = await populate(vazifalar, "egasi");
v = await populateIchki(v, "izohlar", "muallif");
const [pop] = v;
console.log("populate natijasi:", pop.egasi.ism, "|",
pop.izohlar.map((i) => `${i.muallif.ism}: ${i.matn}`).join("; "));
console.log(log.join("\n"));Konsolda:
populate natijasi: Aziz Karimov | Aziz Karimov: Tez; Dilnoza: Men ham
vazifas.findOne(...)
foydalanuvchis.find(_id $in ["f1"])
foydalanuvchis.find(_id $in ["f1","f2"])Natija va so'rovlar tartibi kanon debug logi bilan bir xil: 3 so'rov, Aziz ikki marta so'raladi — har populate yo'li o'z so'rovini yuboradi. Yaxshilash g'oyasi: ikkala yo'lning id'larini bitta to'plamga yig'ib, 2 so'rovga tushirish. Mongoose buni o'zi qilmaydi.
10. Real ishda
- Sxema loyihalash suhbati — Mongo'li jamoada har yangi xususiyat uchun "embed yoki reference?" muhokamasi bo'ladi; qaror so'rovlar ro'yxati va kutilgan hajmdan chiqadi.
- populate — Express + Mongoose loyihalarida eng ko'p uchraydigan sekinlik manbalaridan biri: ichma-ich bir nechta populate bitta sahifada o'nlab so'rovga aylanadi. Debug log bilan sanash — birinchi qadam.
- GraphQL serverlari Mongo bilan DataLoader orqali xuddi shu batch g'oyasini ishlatadi.
- Intervyu savollari: "embed va reference qachon?", "populate JOINmi?", "16 MB chegara modellashga qanday ta'sir qiladi?".
Xulosa
- Mongo'da bog'lanish — tanlov: embed (hujjat ichida) yoki reference (boshqa hujjatning
_idsi); baza reference'ni tekshirmaydi. - Bitta hujjatga yozish atomik — embed'ning katta yutug'i; reference'da atomiklik uchun tranzaksiya kerak.
populateJOIN emas: har yo'l — alohida$inso'rov (kanonda bitta vazifa uchun 3 so'rov); projection bilan faqat kerakli maydonlarni oling.- Kanon o'lchovi (200 vazifa): embed 6.59, populate 12.46,
$lookup26.82 ms — intuitsiyaga emas, o'lchovga ishoning. - Qaror: alohida so'raladimi? chegaralanganmi? takrorlanib tez o'zgaradimi? — va o'qish/yozish nisbati.
Keyingi dars: Mongoose ilg'or — middleware (Mongoose 9 da next() siz), virtual maydonlar, metodlar, discriminator, lean() va TypeScript turlari.
Manbalar
- Mongoose 9 hujjati — Populate: https://mongoosejs.com/docs/populate.html
- MongoDB hujjati — Data Modeling (embed va reference): https://www.mongodb.com/docs/manual/data-modeling/
- MongoDB hujjati —
$lookup: https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/ - MongoDB blogi — "6 Rules of Thumb for MongoDB Schema Design": https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design (2026-10-26 holatiga)
- Kanon o'lchovi (IlmHamroh, 2026-10): 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!