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.

🕑 13 daqiqa o‘qish 📄 600 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Loyihaning sxemasi
  2. 1-vazifa: sxemani tekshiruv bilan mustahkamlash
  3. 2-vazifa: umumiy ko'rsatkichlar
  4. 3-vazifa: eng ko'p sotilgan kitoblar
  5. 4-vazifa: oylik dinamika
  6. 5-vazifa: shahar bo'yicha eng faol mijoz
  7. 6-vazifa: teglar bo'yicha kesim
  8. 7-vazifa: tugab qolgan kitoblar
  9. 8-vazifa: indekslar
  10. 9-vazifa: buyurtmani tranzaksiya bilan qabul qilish
  11. 10-vazifa: hisobotni saqlab qo'yish
  12. Keyingi qadamlar
  13. Xulosa

Yigirma bobda o'rgangan hamma narsani bitta ishchi loyihada birlashtiramiz. Loyiha - Namangandagi onlayn kitob do'koni uchun buyurtma va tahlil tizimi.

JavaScript
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 #

Uchta to'plam, ikki xil bog'lanish mijoz ism, shahar manzil - ichiga joylangan 4 hujjat kitob _id = ISBN teglar - massiv 5 hujjat buyurtma mijoz_id + mijoz_ism qatorlar - ichiga joylangan 6 hujjat mijoz_id kitob_id Buyurtmada mijoz ismi va kitob nomi TAKRORLANGAN - bu ataylab Chek o'zgarmasligi va ro'yxat $lookup siz chizilishi uchun
Kengaytirilgan havola naqshi: havola bor, lekin eng kerakli maydon yoniga ko'chirilgan
Bu sxemada qilingan uchta qaror

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 #

JavaScript
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: [] })
Natija
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 #

JavaScript
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
  } }
])
Natija
[ { nusxa: 14, tushum: 1070000, buyurtmalar: 5, mijozlar: 3 } ]

3-vazifa: eng ko'p sotilgan kitoblar #

JavaScript
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 } }
])
Natija
[
  { _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 #

JavaScript
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
  } }
])
Natija
[
  { 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 #

JavaScript
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 } }
])
Natija
[
  { _id: 'Namangan', ism: 'Nodira Ismoilova', summa: 623000 },
  { _id: 'Toshkent', ism: 'Malika Tursunova', summa: 192000 }
]

6-vazifa: teglar bo'yicha kesim #

JavaScript
db.kitob.aggregate([
  { $unwind: "$teglar" },
  { $group: {
      _id: "$teglar",
      kitoblar: { $sum: 1 },
      ortacha_narx: { $avg: "$narx" },
      omborda: { $sum: "$ombor" }
  } },
  { $sort: { kitoblar: -1, _id: 1 } }
])
Natija
[
  { _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 #

JavaScript
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 })
Natija
[ { _id: '978-3', nom: 'Kecha va kunduz', muallif: "Cho'lpon" } ]


[ { nom: 'Kecha va kunduz', ombor: 0 }, { nom: 'Navoiy', ombor: 7 } ]

8-vazifa: indekslar #

JavaScript
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)
Natija
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'
]
IndeksQaysi 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 } qismanFaqat tugayotgan kitoblar

9-vazifa: buyurtmani tranzaksiya bilan qabul qilish #

Buyurtma yozish va omborni kamaytirish birga bo'lishi kerak:

JavaScript
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 })
Natija
7
{ _id: '978-5', nom: 'Ufq', ombor: 16 }
Ombor tekshiruvini filtrga qo'ying

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:

Natija
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 #

JavaScript
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 })
Natija
[
  { _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'nalishNima qilish
Qidiruv{ nom: "text", muallif: "text" } matn indeksi
Kuzatuvwatch() bilan yangi buyurtmaga xabar yuborish
Sahifalash$facet bilan natija va jami sonni birga olish
TozalashBekor qilingan buyurtmalarga TTL indeks
Ishonchlilikmongodump va --nsTo bilan tiklashni sinash
Amaliy topshiriq
  1. Sxemani o'zingiz noldan tuzing va ma'lumot kiriting.
  2. $jsonSchema bilan holat va qatorlarni cheklang.
  3. Umumiy ko'rsatkichlar so'rovini yozing.
  4. Eng ko'p sotilgan uchta kitobni chiqaring.
  5. Oylik tushumni timezone bilan hisoblang.
  6. Har shahardagi eng faol mijozni toping.
  7. Teglar bo'yicha kesim tuzing.
  8. Kerakli indekslarni qo'ying va har birini izohlang.
  9. Buyurtmani tranzaksiya bilan qabul qiling.
  10. 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 _id ga qo'ying.
  • Birga o'qiladigan va soni chekli ma'lumot - ichiga joylanadi.
  • Kengaytirilgan havola (mijoz_id + mijoz_ism) $lookup ni ortiqcha qiladi.
  • $jsonSchema sxemani oxirgi himoya sifatida mustahkamlaydi.
  • $unwind + $group - massivli ma'lumot bo'yicha hisobotning asosiy naqshi.
  • Sana bo'yicha guruhlashda timezone ni 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.

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.