IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis44/65-dars19 daqiqa
Mundarija (28)

Mongoose schema va model: sxemasiz MongoDB'ga tartib olib kirish

Qisqacha: Mongoose — MongoDB drayveri ustidagi kutubxona: schema hujjat shaklini va qoidalarini (tur, required, default, enum, validator) yozadi, model shu schema bilan kolleksiyaga ishlaydi. Tekshiruv bazada emas, ilovada bo'ladi — bitta create hamma xatoni birdan qaytaradi. Uchta tuzoq: model nomidan kolleksiya nomi inglizcha ko'plik bilan yasaladi (Vazifa → vazifas), sxemada yo'q maydon jim tashlanadi (strict), unique esa validator emas — uni bazadagi indeks tekshiradi (E11000).

Bu darsda

  • Mongoose nima uchun kerakligini va drayverdan farqini tushuntira olasiz.
  • Schema yozasiz: turlar, required, default, enum, min/max, trim/lowercase, custom validator, timestamps.
  • model() bilan model yaratasiz va kolleksiya nomi tuzog'ini chetlab o'tasiz.
  • Validatsiya xatolarini o'qiysiz: ValidationError, CastError, E11000.
  • strict rejimining jim xulqini va strict: "throw" ni bilasiz.

Oldin bilishingiz kerak: MongoDB native driver, MongoDB tuzilishi va vositalari, Validatsiya: nima uchun, qayerda va qanday, CREATE TABLE va cheklovlar.

1. Nega bu kerak?

MongoDB tuzilishi darsida kanon vazifalar kolleksiyasiga matn: 42 yozdi — Mongo indamadi. Native driver darsida esa replaceOne hujjatning yarmini jim o'chirdi. Ikkalasining ildizi bitta: bazada sxema yo'q.

Postgres'da bu ishni CREATE TABLE qilardi: text NOT NULL, CHECK (length(matn) <= 100), enum turi (CREATE TABLE va cheklovlar). Mongo'da bunday joy yo'q. Demak, tartibni ilova o'zi saqlashi kerak.

Mongoose — shu ishni qiladigan kutubxona. U MongoDB rasmiy drayveri ustiga qurilgan va uchta narsa qo'shadi:

  1. Schema — hujjat qanday ko'rinishi kerakligining tavsifi.
  2. Validatsiya — saqlashdan oldin qoidalarni tekshirish.
  3. Model — schema bilan bog'langan kolleksiya ustidagi qulay metodlar (keyingi darslarda: CRUD, populate, middleware).

O'xshatish: bo'sh daftar — har kim xohlagan narsani yozadi. Ish hujjati blankasi esa katakchalarga bo'lingan: "ism — majburiy", "sana — kun.oy.yil", "lavozim — ro'yxatdan tanlang". Mongoose — daftarga blanka chizib beradi. Daftar (Mongo) baribir hamma narsani qabul qiladi, lekin blankadan o'tmagan narsa unga yetib bormaydi.

Bu darsdagi chiqishlar ikki manbadan: kanon tajribasi (Node 24.21.0, mongoose 9.11.2, MongoDB 8.3.13 Docker'da) va bazasiz sinovlarimiz ("biz ishga tushirdik" — xuddi shu mongoose 9.11.2, Node 24.21.0, ulanishsiz).

2. Schema: hujjatning tavsifi

2.1 Birinchi schema

O'rnatish (o'z loyihangizda):

bash
npm i --save-exact mongoose@9.11.2

Kanon vazifalar uchun yozgan foydalanuvchi schema'si:

js
import mongoose from "mongoose";
const { Schema } = mongoose;

const FoydalanuvchiS = new Schema({
  email: {
    type: String, required: true, lowercase: true, trim: true,
    unique: true, match: /^[^@\s]+@[^@\s]+$/,
  },
  ism: {
    type: String, required: [true, "ism majburiy"],
    maxlength: 100,
  },
}, {
  timestamps: { createdAt: "yaratilgan", updatedAt: "yangilangan" },
});

Schema — oddiy obyekt: kalit — maydon nomi, qiymat — qoidalar. Birinchi argument — maydonlar, ikkinchisi — schema sozlamalari (bu yerda timestamps).

2.2 SchemaTypes — turlar

Mongoose turi JavaScript qiymati Misol
String satr matn
Number son versiya
Boolean true/false bajarildi
Date Date muddat
Schema.Types.ObjectId ObjectId egasi
[String] satrlar massivi teglar
[IzohS] ichki hujjatlar massivi izohlar
Schema.Types.Decimal128 aniq kasr narx
Map, Schema.Types.Mixed erkin obyekt sozlamalar

Qisqa yozuv ham bor: ism: String — ism: { type: String } bilan bir xil. Qoida qo'shish kerak bo'lsa — to'liq shakl.

Mixed — "nima bo'lsa ham" turi: Mongoose uni tekshirmaydi. JSONB darsidagi erkin sozlamalar uchun mos, lekin ko'p ishlatsangiz — sxemaning foydasi yo'qoladi.

2.3 Vazifa schema'si: hamma qoida birga

Kanon vazifa schema'si (qatorlar telefon uchun bo'lingan, mazmuni aynan):

js
const IzohS = new Schema({
  muallif: { type: Schema.Types.ObjectId, ref: "Foydalanuvchi" },
  matn: { type: String, required: true, maxlength: 2000 },
}, { timestamps: { createdAt: "yaratilgan", updatedAt: false } });

const VazifaS = new Schema({
  egasi: {
    type: Schema.Types.ObjectId, ref: "Foydalanuvchi",
    required: true, index: true,
  },
  matn: {
    type: String, required: true, trim: true,
    minlength: 1, maxlength: 100,
    validate: {
      validator: (m) => !/\s{2,}/.test(m),
      message: (p) => `"${p.value}" — ketma-ket bo'shliq bo'lmasin`,
    },
  },
  bajarildi: { type: Boolean, default: false },
  ustuvorlik: {
    type: String,
    enum: {
      values: ["past", "orta", "yuqori"],
      message: "{VALUE} — ustuvorlik emas",
    },
    default: "orta",
  },
  teglar: { type: [String], default: [] },
  izohlar: { type: [IzohS], default: [] },
  versiya: { type: Number, default: 1, min: 1 },
}, {
  timestamps: { createdAt: "yaratilgan", updatedAt: "yangilangan" },
});

Har qoidani alohida ko'ramiz:

Qoida Nima qiladi Postgres o'xshashi
required: true bo'sh qolsa xato NOT NULL
default: "orta" berilmasa shu qiymat DEFAULT 'orta'
enum: [...] faqat ro'yxatdagilar enum turi yoki CHECK (… IN …)
min, max son chegarasi CHECK (versiya >= 1)
minlength, maxlength satr uzunligi CHECK (length(…) <= 100)
match: /…/ regex'ga mos bo'lsin CHECK (… ~ '…')
trim, lowercase saqlashdan oldin o'zgartiradi trigger (Triggerlar)
validate o'z funksiyangiz CHECK + funksiya
index: true, unique: true indeks yaratadi CREATE INDEX, UNIQUE

ref: "Foydalanuvchi" — "bu ObjectId Foydalanuvchi modelining hujjatiga ishora". Mongo buni tekshirmaydi — tashqi kalit emas. U populate uchun kerak.

izohlar: [IzohS] — ichki hujjat (subdocument): har izoh o'z schema'si bilan tekshiriladi va o'z _id sini oladi.

Xabarlar ikki shaklda: required: [true, "ism majburiy"] (massiv — qiymat va xabar) yoki enum: { values, message }. {VALUE} — Mongoose noto'g'ri qiymatni shu joyga qo'yadi. Custom validator'da message funksiya bo'lishi mumkin: p.value — tekshirilgan qiymat.

2.4 timestamps

timestamps: true bo'lsa, Mongoose har hujjatga createdAt va updatedAt qo'shadi va o'zi yangilaydi. Kanon o'zbekcha nomlarni berdi: { createdAt: "yaratilgan", updatedAt: "yangilangan" }. Izohda updatedAt: false — izoh tahrirlanmaydi, yangilanish vaqti kerak emas.

Kanon Azizni yaratib, hujjat kalitlarini chiqardi:

js
const aziz = await Foydalanuvchi.create({
  email: "  AZIZ@Example.com ", ism: "Aziz Karimov",
});
console.log("lowercase+trim:", aziz.email, "| timestamps:",
  Object.keys(aziz.toObject()).join(", "));
text
lowercase+trim: aziz@example.com | timestamps: email, ism, _id, yaratilgan, yangilangan, __v

Ikki narsa: " AZIZ@Example.com " saqlashdan oldin aziz@example.com ga aylandi (trim + lowercase). Va hujjatda biz bermagan to'rtta maydon paydo bo'ldi: _id (ObjectId), yaratilgan, yangilangan va __v. __v — Mongoose'ning versiya kaliti (versionKey): massiv o'zgarishlarida ziddiyatni sezish uchun. Uni Mongoose ilg'or darsida ko'ramiz.

Tekshirib ko'ring: Schema'da email uchun lowercase: true bor. Foydalanuvchi Aziz@EXAMPLE.com bilan login qilmoqchi. Qidiruv filtrida nima qilish kerak?

Javob

Mongoose orqali — hech narsa qilish shart emas: u trim va lowercase ni filtrga ham qo'llaydi. Biz bazasiz tekshirdik (mongoose 9.11.2): find({ email: " Aziz@EXAMPLE.com " }) ning filtri {"email":"aziz@example.com"} ga aylandi. Lekin xuddi shu filtr drayver yoki mongosh orqali yuborilsa — o'zgarmaydi va hech narsa topilmaydi. Shuning uchun ishonchli yo'l — kirishni o'zingiz normallashtirish (email.trim().toLowerCase()): qaysi vosita bilan qidirmang, natija bir xil.

3. Model: schema + kolleksiya

3.1 mongoose.model()

js
await mongoose.connect(MONGO_URL);
const Foydalanuvchi = mongoose.model("Foydalanuvchi", FoydalanuvchiS);
const Vazifa = mongoose.model("Vazifa", VazifaS);
await Foydalanuvchi.init();

mongoose.connect(url) — bitta umumiy ulanish (ichida drayverning MongoClient va hovuzi — oldingi darsdagi "bitta client" qoidasi Mongoose'da o'zi bajariladi). model(nom, schema) — model sinfi: new Vazifa({...}) hujjat yaratadi, Vazifa.find(...) qidiradi.

Foydalanuvchi.init() — kanon izohi bilan: "unique indeks yaratilishini kutish (aks holda birinchi takror o'tib ketishi mumkin)". Mongoose indekslarni fonda yaratadi. Indeks tayyor bo'lmasdan takror email yozilsa — baza uni tekshira olmaydi.

3.2 Tuzoq: kolleksiya nomi

Kanon model yaratgandan keyin kolleksiya nomlarini chiqardi:

text
to'plam nomlari (model nomidan, ko'plik + kichik harf): foydalanuvchis vazifas

foydalanuvchis! Mongoose model nomini kichik harfga o'tkazib, inglizcha ko'plik qo'shimchasini qo'shadi. U o'zbek tilini bilmaydi. Biz bazasiz sinab ko'rdik — model nomidan kolleksiya nomi:

js
const s = () => new Schema({ nom: String });
const nomlar = [
  model("Vazifa", s()),
  model("Foydalanuvchi", s()),
  model("Person", s()),
  model("Izoh", s(), "izohlar"),
].map((M) => `${M.modelName} → ${M.collection.collectionName}`);
console.log(nomlar.join("\n"));

Natija (biz ishga tushirdik: mongoose 9.11.2, Node 24.21.0, ulanishsiz):

text
Vazifa → vazifas
Foydalanuvchi → foydalanuvchis
Person → people
Izoh → izohlar

Person → people — inglizcha qoidalar lug'ati ishladi. O'zbekcha nomlarga esa shunchaki s qo'shildi. Oxirgisida uchinchi argument — kolleksiya nomini aniq berdik.

Nega bu muhim? Native driver darsida kanon ma'lumotni vazifalar kolleksiyasiga yozgan edi. Mongoose modeli esa vazifas ni qidiradi — bo'sh kolleksiyani. Xato yo'q, natija bo'sh. Bir loyihada drayver va Mongoose aralash ishlatilsa — ikki xil kolleksiya paydo bo'ladi.

Yechim — nomni har doim aniq bering:

js
const Vazifa = mongoose.model("Vazifa", VazifaS, "vazifalar");
// yoki schema sozlamasida:
new Schema({ /* … */ }, { collection: "vazifalar" });

Diqqat: Bu darsdagi kanon tajribasi alohida mongoose bazasida ishladi va nomni ataylab bermadi — tuzoqni ko'rsatish uchun. Keyingi darslardagi kanon chiqishlarida vazifas, foydalanuvchis nomlarini ko'rasiz — shu sababdan.

4. Validatsiya: xatolarni o'qish

4.1 Hamma xato birdan

Kanon ataylab uchta qoidani buzgan vazifa yaratdi: ikki bo'shliqli matn, noma'lum ustuvorlik, versiya: 0:

js
try {
  await Vazifa.create({
    egasi: aziz._id, matn: "a  b", ustuvorlik: "juda", versiya: 0,
  });
} catch (x) {
  console.log(x.name, "→", Object.entries(x.errors)
    .map(([k, e]) => `${k}: ${e.message}`).join(" | "));
}
text
ValidationError → matn: "a  b" — ketma-ket bo'shliq bo'lmasin | ustuvorlik: juda — ustuvorlik emas | versiya: Path `versiya` (0) is less than minimum allowed value (1).

Bitta ValidationError, ichida errors obyekti — har buzilgan maydon uchun bittadan. Ikkitasida bizning o'zbekcha xabarlarimiz, uchinchisida (min) — Mongoose'ning sukut inglizcha xabari: "versiya yo'li (0) ruxsat etilgan eng kichik qiymatdan (1) kichik". Hujjat bazaga umuman yuborilmadi.

Bu API uchun qulay: foydalanuvchiga birinchi xatoni emas, hammasini birdan ko'rsatasiz (Zod dagi issues ro'yxati kabi).

Ikkinchi sinov — majburiy maydonlarsiz:

js
try { await Vazifa.create({ matn: "" }); }
catch (x) {
  console.log(x.name, "→", Object.keys(x.errors).sort().join(", "));
}
text
ValidationError → egasi, matn

matn: "" — bo'sh satr ham required ni buzadi.

4.2 Bazasiz validatsiya

Mongoose validatsiyasi ilovada ishlaydi — bazaga ulanmasdan ham tekshirish mumkin. Biz shuni sinadik: ulanishsiz, validate() bilan (qisqartirilgan schema):

js
const yomon = new Vazifa({
  matn: "  Non olish  ",
  ustuvorlik: "juda",
  versiya: 0,
  nomalum: "yo'qoladi",
});
try {
  await yomon.validate();
} catch (xato) {
  console.log(xato.name);
  for (const [yol, e] of Object.entries(xato.errors)) {
    console.log(`- ${yol}: ${e.message}`);
  }
}
console.log(yomon.matn, "|", yomon.nomalum);

Natija (biz ishga tushirdik: mongoose 9.11.2, Node 24.21.0, ulanishsiz):

text
ValidationError
- egasi: Path `egasi` is required.
- ustuvorlik: `juda` is not a valid enum value for path `ustuvorlik`.
- versiya: Path `versiya` (0) is less than minimum allowed value (1).
Non olish | undefined

Uchta kuzatuv:

  1. enum uchun o'z xabarimizni bermadik — Mongoose sukut xabari: "juda — ustuvorlik yo'li uchun ruxsat etilgan enum qiymati emas".
  2. matn hujjat yaratilgandayoq "Non olish" bo'ldi — trim qiymat berilganda ishlaydi, saqlashda emas.
  3. nomalum — undefined. Sxemada yo'q maydon jim tashlandi (bu strict rejim — pastda).

Maslahat: Sinovimizda validateSync() ni ham chaqirdik — Mongoose 9.11.2 ogohlantirdi: `Document.prototype.validateSync()` is deprecated and will be removed in Mongoose 10. Use `Document.prototype.validate()` instead. Ya'ni sinxron variant eskirgan, keyingi katta versiyada o'chiriladi. Yangi kodda await doc.validate().

4.3 unique — validator emas

Takror email bilan foydalanuvchi:

js
try {
  await Foydalanuvchi.create({
    email: "aziz@example.com", ism: "Takror",
  });
} catch (x) {
  console.log("unique — bu VALIDATOR emas, Mongo xatosi:",
    x.name, x.code);
}
text
unique — bu VALIDATOR emas, Mongo xatosi: MongoServerError 11000

Ko'ryapsizmi: ValidationError emas, MongoServerError kodi 11000 (E11000 duplicate key error). unique: true Mongoose'ga "bu maydonga noyob indeks yarat" degan buyruq, xolos. Tekshiruvni bazadagi indeks qiladi — hujjat serverga borgandan keyin.

flowchart LR
  A["create(obyekt)"] --> B["cast: tur"]
  B --> C["trim, lowercase, default"]
  C --> D{"validate"}
  D -- xato --> E["ValidationError"]
  D -- ok --> F["MongoDB: insert"]
  F -- takror --> G["E11000 (unique indeks)"]
  F -- ok --> H["saqlandi"]

Diagrammaga qarang: chap qism — ilova ichida, o'ng qism — serverda. Xatolar ham ikki joydan keladi, demak API'dagi xato ishlovchida ikkalasini ushlash kerak: ValidationError → 400, code === 11000 → 409 (Global error handler).

4.4 CastError — tur mos kelmasa

js
await Vazifa.findById("123").catch((x) =>
  `${x.name}: ${x.message.slice(0, 80)}`);
text
cast xatosi: CastError: Cast to ObjectId failed for value "123" (type string) at path "_id" for model "V

(Kanon xabarni 80 belgida kesgan.) Tarjima: "_id yo'lida "123" qiymatini ObjectId'ga aylantirib bo'lmadi". Mongoose filtr qiymatlarini ham schema turiga aylantiradi (cast): "6add9a83…" kabi 24 belgili satr ObjectId bo'ladi, "123" — bo'lmaydi.

Amaliy natija: GET /vazifalar/123 ga 500 emas, 400 yoki 404 qaytarish kerak. CastError ni ushlang yoki id'ni oldindan tekshiring (mongoose.isValidObjectId(id)).

5. strict: sxemada yo'q maydon

5.1 Sukut — jim tashlash

js
const v1 = await Vazifa.create({
  egasi: aziz._id, matn: "Non olish #uy #Bozor",
  nomalum: "yo'qoladi",
});
await Vazifa.collection.findOne(
  { _id: v1._id },
  { projection: { _id: 0, matn: 1, teglar: 1, nomalum: 1 } },
);

Vazifa.collection — Mongoose'ni chetlab, drayverning o'z kolleksiyasi: bazada haqiqatan nima borligini ko'rish uchun.

text
saqlangan: {"matn":"Non olish #uy #Bozor","teglar":["uy","bozor"]}

nomalum yo'q — xato ham yo'q. Bu sukut strict: true rejimi: "sxemada yo'q maydonni bazaga yozma". Himoya sifatida yaxshi (foydalanuvchi isAdmin: true yuborsa, u yozilmaydi). Lekin xatoni ham yashiradi: maydon nomida xato qilsangiz (ustuvorlk), ma'lumot jim yo'qoladi.

(teglar matndan avtomatik ajratildi — bu kanonning pre("save") middleware'i, Mongoose ilg'or darsida.)

5.2 strict: "throw"

js
const StrictThrow = mongoose.model("StrictThrow",
  new Schema({ a: String }, { strict: "throw" }));
await StrictThrow.create({ a: "x", b: 1 })
  .then(() => "o'tdi", (x) => x.name);
text
strict: 'throw' → StrictModeError

Endi noma'lum maydon xato beradi. Qaysi biri yaxshi? Ichki API'da, kirish allaqachon Zod bilan tekshirilgan bo'lsa — throw dasturchi xatosini darhol ko'rsatadi. Tashqi kirishni to'g'ridan modelga bermang — avval validatsiya (Validatsiya: nima uchun, qayerda), keyin faqat kerakli maydonlar.

Tekshirib ko'ring: Foydalanuvchi profilini yangilash so'roviga hujumchi {"ism": "Ali", "rol": "admin"} yubordi. Schema'da rol yo'q, strict sukut. Nima bo'ladi? Agar schema'da rol bo'lsa-chi?

Javob

rol sxemada bo'lmasa — jim tashlanadi, faqat ism yoziladi. Lekin schema'da rol bor bo'lsa (foydalanuvchida haqiqatan rol maydoni bor), strict himoya qilmaydi — rol: "admin" yoziladi! Bu mass assignment zaifligi. To'g'risi: so'rov tanasidan faqat ruxsat etilgan maydonlarni ajratib olish ({ ism } = body) yoki Zod schema'si bilan qat'iy shakl.

6. Indekslar va autoIndex

Schema'dagi index: true va unique: true — indeks e'lonlari. Mongoose ularni model birinchi ishlatilganda yaratadi (autoIndex). Kanon vazifalar kolleksiyasining indekslarini o'qidi:

text
{"_id":1} {"egasi":1} | __v: 0

_id — har doim bor, egasi — index: true dan. Qulay, lekin prodakshnda xavfli: katta kolleksiyada indeks yaratish ancha vaqt va resurs oladi, va u ilova ishga tushganda kutilmaganda boshlanadi. Mongoose hujjati prodakshnda autoIndex: false qilib, indekslarni alohida (migratsiya kabi) yaratishni tavsiya qiladi (Migratsiya tushunchasi bilan bir xil g'oya). Indekslarning o'zi — MongoDB indekslari darsida.

7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi

vazifalar-api Postgres'da qoladi va unda validatsiyani Zod qiladi (25-qism). Commit yo'q. Kanon bu darsda (kanon o'lchovi, IlmHamroh, 2026-10):

  1. Foydalanuvchi va vazifa schema'larini vazifalar qoidalari bilan yozdi (matn ≤ 100, ustuvorlik enum'i, versiya ≥ 1).
  2. Kolleksiya nomi tuzog'ini qayd qildi: foydalanuvchis, vazifas.
  3. Bitta create da hamma validatsiya xatosi, unique → 11000, CastError, strict jim va throw.

Biz bunga bazasiz ikki sinov qo'shdik: kolleksiya nomlari (Person → people) va validate(), hamda validateSync() eskirgani haqidagi ogohlantirish.

Postgres bilan solishtiring: u yerda bu qoidalarning hammasi bazada (CHECK, NOT NULL, UNIQUE) va har qanday mijozni — psql'ni ham — to'xtatadi. Mongoose qoidalari esa faqat Mongoose orqali yozilganda ishlaydi. mongosh yoki drayver bilan yozilgan hujjat ularni chetlab o'tadi.

8. Ko'p uchraydigan xatolar

Xato Oqibat To'g'risi
Kolleksiya nomini bermaslik vazifas — mavjud ma'lumot "yo'qoladi" 3-argument yoki collection sozlamasi
unique ni validator deb o'ylash 11000 xatosi ushlanmaydi, 500 code === 11000 → 409
Maydon nomida xato (ustuvorlk) strict jim tashlaydi strict: "throw" yoki Zod
findById ga noto'g'ri id CastError → 500 isValidObjectId yoki 400
Mongoose qoidalarini "bazadagi" deb hisoblash mongosh/drayver bilan yozilgan yomon hujjat Kritik qoidaga $jsonSchema yoki faqat Mongoose orqali yozish
validateSync() Mongoose 10 da o'chadi await doc.validate()
init() siz unique test Birinchi takror o'tib ketishi mumkin await Model.init()

9. Mashqlar

Mashqlar kurs/mashqlar/27/44-mongoose-schema/ da. 2- va 3-mashq yechimlari Node 24 da tekshirilgan (biz ishga tushirdik — Mongoose qoidalarining kichik modeli, kutubxonasiz).

1-mashq (oson): to'ldiring

  • Berilmasa qo'yiladigan qiymat —
  • Faqat ro'yxatdagi qiymatlarga ruxsat —
  • createdAt/updatedAt ni qo'shadigan sozlama —
  • Takror email xatosining kodi —
  • model("Vazifa", schema) qaysi kolleksiyani ishlatadi?
Yechim
  • default — default: "orta".
  • enum — enum: { values: [...], message }.
  • timestamps — nomlarini o'zgartirish mumkin: { createdAt: "yaratilgan" }.
  • 11000 — MongoServerError, unique indeksdan; validator emas.
  • vazifas — kichik harf + inglizcha ko'plik; 3-argument bilan vazifalar qiling.

2-mashq (o'rta): hamma xato birdan

Mongoose validatsiyasining kichik modelini yozing. tekshir(qiymatlar, sxema) — xatolar obyektini qaytarsin ({ maydon: xabar }), xato bo'lmasa null. Qoidalar: required, enum, min, maxlength. Bitta maydonda bir nechta qoida buzilsa — birinchisi yetadi, lekin hamma maydonlar tekshirilsin. Xabarlar — Mongoose sukut xabarlari kabi.

Ishora: for…of Object.entries(sxema) va har maydonda qoidalarni tartib bilan tekshirish.

Yechim
js
function tekshir(q, sxema) {
  const xatolar = {};
  for (const [yol, r] of Object.entries(sxema)) {
    const v = q[yol];
    if (v === undefined || v === null || v === "") {
      if (r.required) xatolar[yol] = `Path \`${yol}\` is required.`;
      continue;
    }
    if (r.enum && !r.enum.includes(v)) {
      xatolar[yol] =
        `\`${v}\` is not a valid enum value for path \`${yol}\`.`;
    } else if (r.min !== undefined && v < r.min) {
      xatolar[yol] = `Path \`${yol}\` (${v}) is less than `
        + `minimum allowed value (${r.min}).`;
    } else if (r.maxlength !== undefined && v.length > r.maxlength) {
      xatolar[yol] = `Path \`${yol}\` (\`${v}\`, length ${v.length})`
        + ` is longer than the maximum allowed length`
        + ` (${r.maxlength}).`;
    }
  }
  return Object.keys(xatolar).length ? xatolar : null;
}

const sxema = {
  egasi: { required: true },
  matn: { required: true, maxlength: 100 },
  ustuvorlik: { enum: ["past", "orta", "yuqori"] },
  versiya: { min: 1 },
};
const xatolar = tekshir(
  { matn: "Non olish", ustuvorlik: "juda", versiya: 0 }, sxema);
for (const [yol, x] of Object.entries(xatolar)) {
  console.log(`- ${yol}: ${x}`);
}
const qisqa = { matn: { maxlength: 5 } };
console.log(tekshir({ matn: "Bozorga" }, qisqa).matn);
console.log(tekshir({ egasi: "a1", matn: "Non" }, sxema));

Konsolda:

text
- egasi: Path `egasi` is required.
- ustuvorlik: `juda` is not a valid enum value for path `ustuvorlik`.
- versiya: Path `versiya` (0) is less than minimum allowed value (1).
Path `matn` (`Bozorga`, length 7) is longer than the maximum allowed length (5).
null

Birinchi to'rt qator haqiqiy Mongoose xabarlari bilan aynan bir xil: uchtasi — «Bazasiz validatsiya» bo'limidagi chiqish, to'rtinchisini biz alohida tekshirdik (mongoose 9.11.2, maxlength: 5, qiymat "Bozorga"). Asosiy g'oya: tekshiruv birinchi xatoda to'xtamaydi — hamma maydon aylanib chiqiladi.

3-mashq (qiyin): setter, default va strict

tayyorla(kirish, sxema, strict) funksiyasini yozing — Mongoose hujjat yaratishdan oldin qiladigan ishning modeli:

  1. trim: true — satrni qirqish, lowercase: true — kichik harf.
  2. Berilmagan maydonga default (funksiya bo'lsa — chaqirib).
  3. Sxemada yo'q maydon: strict === true — tashlash; strict === "throw" — StrictModeError nomli xato tashlash.

Kanon misoli bilan sinang: " AZIZ@Example.com " → aziz@example.com.

Ishora: xato nomini berish uchun const x = new Error(…); x.name = "StrictModeError";.

Yechim
js
function tayyorla(kirish, sxema, strict = true) {
  const natija = {};
  for (const [k, v] of Object.entries(kirish)) {
    if (!(k in sxema)) {
      if (strict === "throw") {
        const x = new Error(`Field \`${k}\` is not in schema`);
        x.name = "StrictModeError";
        throw x;
      }
      continue;
    }
    let q = v;
    if (typeof q === "string" && sxema[k].trim) q = q.trim();
    if (typeof q === "string" && sxema[k].lowercase) {
      q = q.toLowerCase();
    }
    natija[k] = q;
  }
  for (const [k, r] of Object.entries(sxema)) {
    if (natija[k] === undefined && "default" in r) {
      natija[k] = typeof r.default === "function"
        ? r.default() : r.default;
    }
  }
  return natija;
}

const sxema = {
  email: { trim: true, lowercase: true },
  ism: { trim: true },
  rol: { default: "oddiy" },
  teglar: { default: () => [] },
};
console.log(tayyorla({
  email: "  AZIZ@Example.com ", ism: " Aziz ", nomalum: 1,
}, sxema));
try {
  tayyorla({ email: "a@example.com", b: 1 }, sxema, "throw");
} catch (x) {
  console.log(x.name, "—", x.message);
}

Konsolda:

text
{ email: 'aziz@example.com', ism: 'Aziz', rol: 'oddiy', teglar: [] }
StrictModeError — Field `b` is not in schema

teglar uchun default — funksiya: aks holda hamma hujjat bitta massivni bo'lishib qolardi (havola semantikasi). Mongoose ham massiv va obyekt default'larini har hujjat uchun alohida yaratadi.

10. Real ishda

  • Mongoose — Node'dagi eng ko'p ishlatiladigan MongoDB kutubxonasi; Express + Mongo loyihalarining aksariyatida schema va modellar shu yerda yoziladi. NestJS'da ham @nestjs/mongoose orqali (Mongoose bilan NestJS darsida).
  • Xato ishlovchida ikki manba: ValidationError (400) va MongoServerError 11000 (409) — ko'p loyihalarda aynan ikkinchisi unutiladi.
  • Kolleksiya nomi — eski loyihalarda users, people kabi avtomatik nomlar ko'p uchraydi. O'zbekcha model nomida doim aniq nom bering.
  • Intervyu savollari: "Mongoose validatsiyasi bazadami yoki ilovadami?", "unique validatormi?", "strict nima qiladi?".

Xulosa

  • Mongoose — drayver ustidagi qavat: schema (shakl va qoidalar) + model (kolleksiya bilan ishlash); kanon versiyasi 9.11.2.
  • Qoidalar: required, default, enum, min/max, minlength/maxlength, match, trim/lowercase, custom validate, timestamps.
  • Validatsiya ilovada: bitta ValidationError ichida hamma xato; CastError — tur mos kelmasa; validateSync() eskirgan — validate().
  • unique — indeks, validator emas: takror → MongoServerError 11000, Model.init() bilan indeksni kuting.
  • Model nomidan kolleksiya nomi inglizcha ko'plik (vazifas) — doim aniq nom bering.
  • strict sukut — noma'lum maydon jim tashlanadi; strict: "throw" — StrictModeError.

Keyingi dars: Mongoose CRUD va update operatorlari — find, findOneAndUpdate va returnDocument: 'after', hamda update'da validatsiya nega ishlamaydi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Mongoose schema va model: sxemasiz MongoDB'ga tartib olib kirish — IlmHamroh