MongoDB’da agregatsiya: $match, $group, $lookup misollarda
Amaliyot4 daqInnovialabs tahririyati
Qisqacha
Aggregation pipeline — har biri oldingisining natijasini qayta ishlaydigan bosqichlar zanjiri; $match bilan imkon qadar erta filtrlang, $group bilan guruhlang, $lookup’ni esa kichik ma’lumotlar to‘plamida bajaring.
Aggregation pipeline qanday ishlaydi
Aggregation pipeline — bosqichlar massivi. Hujjatlar ulardan navbat bilan o‘tadi: har bir bosqich oldingisidan oqimni oladi va o‘zgartirilgan oqimni keyingisiga uzatadi. MongoDB’da hisobotlar shunday quriladi: oylar bo‘yicha tushum, eng yaxshi mijozlar, mahsulotlar bo‘yicha savdo.
Asosiy qoida: avval ma’lumotlar hajmini kamaytiring, keyin hisoblang. Filtr va saralash boshida, og‘ir amallar va birlashtirishlar esa oxirida, kichraygan to‘plamda bo‘lsin.
Quyidagi barcha misollar orders kolleksiyasida:
{
"_id": "...",
"customerId": "...",
"status": "paid",
"createdAt": "2026-01-15T10:00:00Z",
"total": 420,
"items": [{ "sku": "A-1", "qty": 2, "price": 150 }, { "sku": "B-7", "qty": 1, "price": 120 }]
}
Asosiy bosqichlar va ularning SQL’dagi analoglari
| Bosqich | Nima qiladi | SQL’dagi analogi |
|---|---|---|
$match | Hujjatlarni filtrlaydi | WHERE ($groupdan keyin — HAVING) |
$group | Guruhlaydi, yig‘indi, son, o‘rtachani hisoblaydi | GROUP BY + agregat funksiyalar |
$project | Maydonlarni tanlaydi, nomini o‘zgartiradi, hisoblaydi | SELECTdagi maydonlar ro‘yxati |
$sort, $limit | Saralash va cheklash | ORDER BY, LIMIT |
$unwind | Massivni yoyadi: har bir element uchun bitta hujjat | eng yaqini — bola jadval bilan JOIN |
$lookup | Boshqa kolleksiyadan hujjatlarni tortib oladi | LEFT OUTER JOIN |
1-misol: oy bo‘yicha top-10 mijoz
db.orders.aggregate([
{ $match: {
status: "paid",
createdAt: { $gte: ISODate("2026-01-01"), $lt: ISODate("2026-02-01") }
} },
{ $group: { _id: "$customerId", orders: { $sum: 1 }, revenue: { $sum: "$total" } } },
{ $sort: { revenue: -1 } },
{ $limit: 10 },
{ $lookup: { from: "customers", localField: "_id", foreignField: "_id", as: "customer" } },
{ $unwind: "$customer" },
{ $project: { _id: 0, name: "$customer.name", orders: 1, revenue: 1 } }
])
Tartibga e’tibor bering:
$matchbirinchi turadi va indeksdan foydalana oladi.$lookup$limitdan keyin bajariladi, ya’ni oyning barcha mijozlari uchun emas, faqat 10 ta hujjat uchun.- Bu yerda
$unwind$lookupqaytargancustomermassivini oddiy ichki obyektga aylantiradi.
2-misol: $unwind orqali mahsulotlar bo‘yicha savdo
Buyurtma pozitsiyalari items massivida saqlanadi. SKU bo‘yicha savdoni hisoblash uchun massivni yoyish kerak:
db.orders.aggregate([
{ $match: { status: "paid", createdAt: { $gte: ISODate("2026-01-01") } } },
{ $unwind: "$items" },
{ $group: {
_id: "$items.sku",
qty: { $sum: "$items.qty" },
revenue: { $sum: { $multiply: ["$items.qty", "$items.price"] } }
} },
{ $sort: { revenue: -1 } }
])
$unwind hujjatlar sonini ko‘paytiradi: besh pozitsiyali buyurtma beshta hujjatga aylanadi. Shuning uchun uni $matchdan oldin emas, keyin qo‘ying.
Pipeline’ga yordam beradigan indekslar
- Indeksdan faqat boshlang‘ich bosqichlar foydalanadi — pipeline boshidagi
$matchva$sort.$groupyoki$unwinddan keyin ma’lumotlar allaqachon xotirada bo‘ladi va kolleksiya indekslari ishlamaydi. - Yuqoridagi misollar uchun
{ status: 1, createdAt: 1 }tarkibiy indeksi mos keladi: avval aniq moslik maydoni, keyin diapazon. $lookupuchun ikkinchi kolleksiyadagiforeignFieldmaydonidagi indeks muhim._idda u doim bor, boshqa maydonlar uchun uni yarating, aks holda har bir birlashtirish kolleksiyani to‘liq skanerlaydi.- Rejani tekshiring:
db.orders.explain("executionStats").aggregate([...]). Birinchi bosqichdaCOLLSCANko‘rsangiz, indeks ishlatilmayapti.
Xotira cheklovlari
- Ma’lumotlarni xotirada ushlab turadigan bosqichlarda (
$group,$sort) har bir bosqich uchun xotira limiti bor. U oshib ketsa, so‘rov yo xato beradi, yo ma’lumotlarni diskka yozadi — buallowDiskUseopsiyasi bilan boshqariladi. Standart xatti-harakat versiyaga bog‘liq, shuning uchun o‘z MongoDB versiyangiz hujjatlarini tekshiring. - Natijadagi har bir hujjat 16 MB bilan cheklangan. Agar
$lookupminglab bog‘liq hujjatlarni bitta massivga yig‘sa, bu limitdan oshish oson. - Diskka yozish xatodan qutqaradi, lekin sekinroq ishlaydi. Eng yaxshi yechim — og‘ir bosqichlardan oldin
$matchva$projectbilan ma’lumotlarni kamaytirish.
Ko‘p uchraydigan xatolar
- Pipeline boshida butun kolleksiya uchun
$lookup— kerak bo‘lmagan joyda qimmat birlashtirish. - Filtrlashdan oldin
$unwind: ma’lumotlar hajmi bir necha barobar o‘sadi. - Hisoblangan maydon bo‘yicha filtr, holbuki uni asl maydon bo‘yicha ertaroq filtrlash mumkin edi.
- Ish vaqtida asosiy bazada og‘ir hisobotlar. Ular uchun secondary replika yoki
$mergeorqali alohida kolleksiyaga oldindan hisoblangan ma’lumotlar yaxshiroq.