20-bo‘lim
Amaliy loyiha - onlayn do'kon tahlili
Sxema dizaynidan boshlab tekshiruv, agregatsiya hisobotlari, indekslar, tranzaksiya va zaxira bilan tugaydigan to'liq amaliy loyiha.
Ushbu bo‘lim mundarijasi
- Loyihaning sxemasi
- 1-vazifa: sxemani tekshiruv bilan mustahkamlash
- 2-vazifa: umumiy ko'rsatkichlar
- 3-vazifa: eng ko'p sotilgan kitoblar
- 4-vazifa: oylik dinamika
- 5-vazifa: shahar bo'yicha eng faol mijoz
- 6-vazifa: teglar bo'yicha kesim
- 7-vazifa: tugab qolgan kitoblar
- 8-vazifa: indekslar
- 9-vazifa: buyurtmani tranzaksiya bilan qabul qilish
- 10-vazifa: hisobotni saqlab qo'yish
- Keyingi qadamlar
- Xulosa
Yigirma bobda o'rgangan hamma narsani bitta ishchi loyihada birlashtiramiz. Loyiha - Namangandagi onlayn kitob do'koni uchun buyurtma va tahlil tizimi.
db.mijoz.insertMany([
{ _id: 1, ism: "Husanboy Qodirov", shahar: "Namangan",
manzil: { kocha: "Navoiy 12", indeks: "160100" },
royxatdan: ISODate("2025-11-04T00:00:00Z") },
{ _id: 2, ism: "Malika Tursunova", shahar: "Toshkent",
manzil: { kocha: "Amir Temur 5", indeks: "100000" },
royxatdan: ISODate("2025-12-19T00:00:00Z") },
{ _id: 3, ism: "Nodira Ismoilova", shahar: "Namangan",
manzil: { kocha: "Bobur 8", indeks: "160104" },
royxatdan: ISODate("2026-01-07T00:00:00Z") },
{ _id: 4, ism: "Aziza Rahimova", shahar: "Samarqand",
manzil: { kocha: "Registon 3", indeks: "140100" },
royxatdan: ISODate("2026-02-11T00:00:00Z") }
])
db.kitob.insertMany([
{ _id: "978-1", nom: "Otkan kunlar", muallif: "Abdulla Qodiriy",
narx: 85000, ombor: 24, teglar: ["roman", "tarixiy"] },
{ _id: "978-2", nom: "Mehrobdan chayon", muallif: "Abdulla Qodiriy",
narx: 72000, ombor: 11, teglar: ["roman", "tarixiy"] },
{ _id: "978-3", nom: "Kecha va kunduz", muallif: "Cho'lpon",
narx: 68000, ombor: 0, teglar: ["roman", "ijtimoiy"] },
{ _id: "978-4", nom: "Navoiy", muallif: "Oybek",
narx: 95000, ombor: 7, teglar: ["roman", "tarixiy"] },
{ _id: "978-5", nom: "Ufq", muallif: "Said Ahmad",
narx: 64000, ombor: 18, teglar: ["roman", "zamonaviy"] }
])
db.buyurtma.insertMany([
{ _id: 101, mijoz_id: 1, mijoz_ism: "Husanboy Qodirov",
sana: ISODate("2026-01-12T09:00:00Z"), holat: "yakunlandi",
qatorlar: [
{ kitob_id: "978-1", nom: "Otkan kunlar", soni: 1, narx: 85000 },
{ kitob_id: "978-4", nom: "Navoiy", soni: 2, narx: 95000 }
] },
{ _id: 102, mijoz_id: 2, mijoz_ism: "Malika Tursunova",
sana: ISODate("2026-01-28T14:30:00Z"), holat: "yakunlandi",
qatorlar: [
{ kitob_id: "978-5", nom: "Ufq", soni: 3, narx: 64000 }
] },
{ _id: 103, mijoz_id: 1, mijoz_ism: "Husanboy Qodirov",
sana: ISODate("2026-02-14T11:00:00Z"), holat: "yakunlandi",
qatorlar: [
{ kitob_id: "978-2", nom: "Mehrobdan chayon", soni: 1, narx: 72000 },
{ kitob_id: "978-3", nom: "Kecha va kunduz", soni: 1, narx: 68000 }
] },
{ _id: 104, mijoz_id: 3, mijoz_ism: "Nodira Ismoilova",
sana: ISODate("2026-02-27T16:45:00Z"), holat: "yolda",
qatorlar: [
{ kitob_id: "978-1", nom: "Otkan kunlar", soni: 3, narx: 85000 }
] },
{ _id: 105, mijoz_id: 4, mijoz_ism: "Aziza Rahimova",
sana: ISODate("2026-03-05T08:15:00Z"), holat: "bekor",
qatorlar: [
{ kitob_id: "978-4", nom: "Navoiy", soni: 1, narx: 95000 }
] },
{ _id: 106, mijoz_id: 1, mijoz_ism: "Husanboy Qodirov",
sana: ISODate("2026-03-22T19:20:00Z"), holat: "yangi",
qatorlar: [
{ kitob_id: "978-5", nom: "Ufq", soni: 1, narx: 64000 },
{ kitob_id: "978-2", nom: "Mehrobdan chayon", soni: 2, narx: 72000 }
] }
])
Loyihaning sxemasi #
1. _id sifatida ISBN. Kitobning tabiiy kaliti bor va u
o'zgarmaydi - shuning uchun alohida ObjectId kerak emas.
Bu bitta indeksni tejaydi.
2. Buyurtma qatorlari ichiga joylangan. Ular har doim buyurtma bilan birga o'qiladi va soni chekli. Alohida to'plam bo'lganda har buyurtma uchun ikkinchi so'rov kerak bo'lar edi.
3. mijoz_ism va qatorlardagi nom takrorlangan.
Buyurtmalar ro'yxatini chizish uchun $lookup kerak emas,
va eski chek kitob nomi o'zgarsa ham o'zgarmaydi.
1-vazifa: sxemani tekshiruv bilan mustahkamlash #
const r = db.runCommand({
collMod: "buyurtma",
validator: { $jsonSchema: {
bsonType: "object",
required: ["mijoz_id", "sana", "holat", "qatorlar"],
properties: {
holat: { enum: ["yangi", "yolda", "yakunlandi", "bekor"],
description: "holat faqat to'rt qiymatdan biri bo'lsin" },
qatorlar: { bsonType: "array", minItems: 1,
description: "buyurtmada kamida bitta qator bo'lsin" }
}
} },
validationLevel: "moderate",
validationAction: "error"
})
r.ok
db.buyurtma.insertOne({ _id: 199, mijoz_id: 1,
sana: ISODate("2026-03-25T00:00:00Z"), holat: "nomalum", qatorlar: [] })
1
Uncaught:
MongoServerError: Document failed validation
Additional information: {
failingDocumentId: 199,
details: {
operatorName: '$jsonSchema',
schemaRulesNotSatisfied: [
{
operatorName: 'properties',
propertiesNotSatisfied: [
{
propertyName: 'holat',
description: "holat faqat to'rt qiymatdan biri bo'lsin",
details: [
{
operatorName: 'enum',
specifiedAs: { enum: [ 'yangi', 'yolda', 'yakunlandi', 'bekor' ] },
reason: 'value was not found in enum',
consideredValue: 'nomalum'
}
]
},
{
propertyName: 'qatorlar',
description: "buyurtmada kamida bitta qator bo'lsin",
details: [
{
operatorName: 'minItems',
specifiedAs: { minItems: 1 },
reason: 'array did not match specified length',
consideredValue: [],
numberOfItems: 0
}
]
}
]
}
]
}
}
Noto'g'ri holat va bo'sh qatorlar ro'yxati rad etildi.
2-vazifa: umumiy ko'rsatkichlar #
db.buyurtma.aggregate([
{ $match: { holat: { $ne: "bekor" } } },
{ $unwind: "$qatorlar" },
{ $group: {
_id: null,
buyurtmalar: { $addToSet: "$_id" },
mijozlar: { $addToSet: "$mijoz_id" },
nusxa: { $sum: "$qatorlar.soni" },
tushum: { $sum: { $multiply: ["$qatorlar.soni", "$qatorlar.narx"] } }
} },
{ $project: {
_id: 0,
buyurtmalar: { $size: "$buyurtmalar" },
mijozlar: { $size: "$mijozlar" },
nusxa: 1, tushum: 1
} }
])
[ { nusxa: 14, tushum: 1070000, buyurtmalar: 5, mijozlar: 3 } ]
3-vazifa: eng ko'p sotilgan kitoblar #
db.buyurtma.aggregate([
{ $match: { holat: { $ne: "bekor" } } },
{ $unwind: "$qatorlar" },
{ $group: {
_id: "$qatorlar.kitob_id",
nom: { $first: "$qatorlar.nom" },
nusxa: { $sum: "$qatorlar.soni" },
tushum: { $sum: { $multiply: ["$qatorlar.soni", "$qatorlar.narx"] } }
} },
{ $sort: { nusxa: -1, _id: 1 } }
])
[
{ _id: '978-1', nom: 'Otkan kunlar', nusxa: 4, tushum: 340000 },
{ _id: '978-5', nom: 'Ufq', nusxa: 4, tushum: 256000 },
{ _id: '978-2', nom: 'Mehrobdan chayon', nusxa: 3, tushum: 216000 },
{ _id: '978-4', nom: 'Navoiy', nusxa: 2, tushum: 190000 },
{ _id: '978-3', nom: 'Kecha va kunduz', nusxa: 1, tushum: 68000 }
]
4-vazifa: oylik dinamika #
db.buyurtma.aggregate([
{ $match: { holat: { $ne: "bekor" } } },
{ $unwind: "$qatorlar" },
{ $group: {
_id: { $dateTrunc: { date: "$sana", unit: "month",
timezone: "Asia/Tashkent" } },
tushum: { $sum: { $multiply: ["$qatorlar.soni", "$qatorlar.narx"] } }
} },
{ $sort: { _id: 1 } },
{ $project: {
_id: 0,
oy: { $dateToString: { format: "%Y-%m", date: "$_id",
timezone: "Asia/Tashkent" } },
tushum: 1
} }
])
[
{ tushum: 467000, oy: '2026-01' },
{ tushum: 395000, oy: '2026-02' },
{ tushum: 208000, oy: '2026-03' }
]
timezone ochiq yozilgani muhim - usiz 1-yanvar soat
02:00 dagi buyurtma dekabrga tushib qolar edi.
5-vazifa: shahar bo'yicha eng faol mijoz #
db.buyurtma.aggregate([
{ $match: { holat: { $ne: "bekor" } } },
{ $unwind: "$qatorlar" },
{ $group: {
_id: "$mijoz_id",
ism: { $first: "$mijoz_ism" },
summa: { $sum: { $multiply: ["$qatorlar.soni", "$qatorlar.narx"] } }
} },
{ $lookup: { from: "mijoz", localField: "_id",
foreignField: "_id", as: "m" } },
{ $unwind: "$m" },
{ $group: {
_id: "$m.shahar",
ism: { $first: "$ism" },
summa: { $max: "$summa" }
} },
{ $sort: { summa: -1 } }
])
[
{ _id: 'Namangan', ism: 'Nodira Ismoilova', summa: 623000 },
{ _id: 'Toshkent', ism: 'Malika Tursunova', summa: 192000 }
]
6-vazifa: teglar bo'yicha kesim #
db.kitob.aggregate([
{ $unwind: "$teglar" },
{ $group: {
_id: "$teglar",
kitoblar: { $sum: 1 },
ortacha_narx: { $avg: "$narx" },
omborda: { $sum: "$ombor" }
} },
{ $sort: { kitoblar: -1, _id: 1 } }
])
[
{ _id: 'roman', kitoblar: 5, ortacha_narx: 76800, omborda: 60 },
{ _id: 'tarixiy', kitoblar: 3, ortacha_narx: 84000, omborda: 42 },
{ _id: 'ijtimoiy', kitoblar: 1, ortacha_narx: 68000, omborda: 0 },
{ _id: 'zamonaviy', kitoblar: 1, ortacha_narx: 64000, omborda: 18 }
]
7-vazifa: tugab qolgan kitoblar #
db.kitob.find({ ombor: 0 }, { nom: 1, muallif: 1 })
const p = { _id: 0, nom: 1, ombor: 1 }
const kam = db.kitob.find({ ombor: { $lte: 10 } }, p)
kam.sort({ ombor: 1 })
[ { _id: '978-3', nom: 'Kecha va kunduz', muallif: "Cho'lpon" } ]
[ { nom: 'Kecha va kunduz', ombor: 0 }, { nom: 'Navoiy', ombor: 7 } ]
8-vazifa: indekslar #
db.buyurtma.createIndex({ mijoz_id: 1, sana: -1 })
db.buyurtma.createIndex({ holat: 1, sana: -1 })
db.buyurtma.createIndex({ "qatorlar.kitob_id": 1 })
db.kitob.createIndex({ teglar: 1 })
db.kitob.createIndex(
{ ombor: 1 },
{ partialFilterExpression: { ombor: { $lte: 10 } } }
)
db.buyurtma.getIndexes().map(i => i.name)
mijoz_id_1_sana_-1
holat_1_sana_-1
qatorlar.kitob_id_1
teglar_1
ombor_1
[
'_id_',
'mijoz_id_1_sana_-1',
'holat_1_sana_-1',
'qatorlar.kitob_id_1'
]
| Indeks | Qaysi so'rov uchun |
|---|---|
{ mijoz_id: 1, sana: -1 } | "Mijozning oxirgi buyurtmalari" - ESR |
{ holat: 1, sana: -1 } | "Yo'ldagi buyurtmalar" |
{ "qatorlar.kitob_id": 1 } | "Bu kitob qaysi buyurtmalarda" |
{ teglar: 1 } | Teg bo'yicha filtr |
{ ombor: 1 } qisman | Faqat tugayotgan kitoblar |
9-vazifa: buyurtmani tranzaksiya bilan qabul qilish #
Buyurtma yozish va omborni kamaytirish birga bo'lishi kerak:
const s = db.getMongo().startSession()
s.withTransaction(() => {
const d = s.getDatabase("dokon")
d.buyurtma.insertOne({
_id: 107, mijoz_id: 3, mijoz_ism: "Nodira Ismoilova",
sana: ISODate("2026-03-28T10:00:00Z"), holat: "yangi",
qatorlar: [ { kitob_id: "978-5", nom: "Ufq", soni: 2, narx: 64000 } ]
})
d.kitob.updateOne({ _id: "978-5" }, { $inc: { ombor: -2 } })
})
s.endSession()
db.buyurtma.countDocuments()
db.kitob.findOne({ _id: "978-5" }, { nom: 1, ombor: 1 })
7
{ _id: '978-5', nom: 'Ufq', ombor: 16 }
Yuqoridagi kod omborni tekshirmasdan kamaytiradi. Ombor 2 ta bo'lsa-yu, uchta kitob buyurtma qilinsa, qoldiq manfiy bo'lib qoladi.
To'g'ri yozuv - shartni filtrga qo'yish:
const n = d.kitob.updateOne(
{ _id: "978-5", ombor: { $gte: 2 } },
{ $inc: { ombor: -2 } }
)
if (n.matchedCount === 0) { throw new Error("Omborda yetarli emas") }
Nima uchun bu to'g'ri: tekshirish va kamaytirish bitta
atomar amalda bajariladi. Alohida findOne bilan
tekshirsangiz, ikki so'rov orasida boshqa mijoz oxirgi
nusxani olib ketishi mumkin.
withTransaction ichida throw chaqirilsa, tranzaksiya
avtomatik bekor qilinadi - buyurtma ham yozilmaydi.
10-vazifa: hisobotni saqlab qo'yish #
db.buyurtma.aggregate([
{ $match: { holat: { $ne: "bekor" } } },
{ $unwind: "$qatorlar" },
{ $group: {
_id: "$qatorlar.kitob_id",
nom: { $first: "$qatorlar.nom" },
nusxa: { $sum: "$qatorlar.soni" }
} },
{ $merge: { into: "kitob_hisoboti", whenMatched: "replace",
whenNotMatched: "insert" } }
])
db.kitob_hisoboti.find().sort({ nusxa: -1, _id: 1 })
[
{ _id: '978-1', nom: 'Otkan kunlar', nusxa: 4 },
{ _id: '978-5', nom: 'Ufq', nusxa: 4 },
{ _id: '978-2', nom: 'Mehrobdan chayon', nusxa: 3 },
{ _id: '978-4', nom: 'Navoiy', nusxa: 2 },
{ _id: '978-3', nom: 'Kecha va kunduz', nusxa: 1 }
]
Endi bosh sahifadagi "eng ko'p sotilganlar" bo'limi tayyor to'plamdan o'qiladi - har safar agregatsiya ishlamaydi.
Keyingi qadamlar #
Loyihani o'zingiz kengaytirib ko'ring:
| Yo'nalish | Nima qilish |
|---|---|
| Qidiruv | { nom: "text", muallif: "text" } matn indeksi |
| Kuzatuv | watch() bilan yangi buyurtmaga xabar yuborish |
| Sahifalash | $facet bilan natija va jami sonni birga olish |
| Tozalash | Bekor qilingan buyurtmalarga TTL indeks |
| Ishonchlilik | mongodump va --nsTo bilan tiklashni sinash |
- Sxemani o'zingiz noldan tuzing va ma'lumot kiriting.
$jsonSchemabilan holat va qatorlarni cheklang.- Umumiy ko'rsatkichlar so'rovini yozing.
- Eng ko'p sotilgan uchta kitobni chiqaring.
- Oylik tushumni
timezonebilan hisoblang. - Har shahardagi eng faol mijozni toping.
- Teglar bo'yicha kesim tuzing.
- Kerakli indekslarni qo'ying va har birini izohlang.
- Buyurtmani tranzaksiya bilan qabul qiling.
- Ombor tekshiruvini filtrga ko'chiring va sinab ko'ring.
Xulosa #
- Sxema so'rovlardan boshlab loyihalanadi, normallashtirish qoidasidan emas.
- Tabiiy kalit bo'lsa (ISBN), uni to'g'ridan-to'g'ri
_idga qo'ying. - Birga o'qiladigan va soni chekli ma'lumot - ichiga joylanadi.
- Kengaytirilgan havola (
mijoz_id+mijoz_ism)$lookupni ortiqcha qiladi. $jsonSchemasxemani oxirgi himoya sifatida mustahkamlaydi.$unwind+$group- massivli ma'lumot bo'yicha hisobotning asosiy naqshi.- Sana bo'yicha guruhlashda
timezoneni har doim ochiq yozing. - Indekslar ESR tartibida tuziladi va har biri aniq so'rovga xizmat qiladi.
- Bog'liq yozuvlarni tranzaksiyada saqlang, lekin uni qisqa tuting.
- Shartni filtrga qo'ying - alohida tekshirish poyga holatiga olib keladi.
Darslik shu yerda tugadi. Endi qo'lingizda MongoDB bilan haqiqiy loyiha qurish uchun yetarli bilim bor - eng yaxshi davomi o'z bazangizni loyihalab ko'rishdir.
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.