Discriminator — bir collection, bir nechta schema
Muammodan boshlaylik
Bildirishnomalar tizimi. Uch xil bildirishnoma:
- Buyurtma —
buyurtmaId,holat - Izoh —
postId,izohMatni,kimdan - Tizim —
daraja(info/warning),havola
Hammasida umumiy: user, oqildi, createdAt.
Variant 1 — uchta collection: "foydalanuvchining barcha bildirishnomalari, vaqt bo'yicha" — uchta so'rov va birlashtirish.
Variant 2 — bitta schema, barcha maydonlar ixtiyoriy: validatsiya yo'q, buyurtma bildirishnomasida postId bo'lishi ham mumkin.
Discriminator nima?
Discriminator — bitta collection da bir nechta schema. Har bir document qaysi turga tegishli ekanini maxsus maydon (__t yoki o'zingiz bergan nom) belgilaydi.
bildirishnomalar collection
├── { tur: "BuyurtmaBildirishnoma", user, oqildi, buyurtmaId, holat }
├── { tur: "IzohBildirishnoma", user, oqildi, postId, izohMatni, kimdan }
└── { tur: "TizimBildirishnoma", user, oqildi, daraja, havola }
- Umumiy maydonlar — asosiy schema da
- Har bir turning o'z maydonlari va validatsiyasi
- Bitta so'rov bilan hammasini olish mumkin
Hayotiy o'xshatma
Pochta bo'limi. Xatlar, posilkalar va telegrammalar bitta javonda saqlanadi — hammasida qabul qiluvchi va sana bor. Lekin har birida o'z blankasi: posilkada og'irlik, telegrammada so'zlar soni. Har bir buyumga yopishtirilgan rangli yorliq (discriminator kaliti) — uning turini bildiradi.
Yaratish
import mongoose from 'mongoose'
const { Schema } = mongoose
// 1. Asosiy schema
const bildirishnomaSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User', required: true, index: true },
oqildi: { type: Boolean, default: false },
}, {
timestamps: true,
discriminatorKey: 'tur',
collection: 'bildirishnomalar',
})
export const Bildirishnoma = mongoose.model('Bildirishnoma', bildirishnomaSchema)
// 2. Turlar
export const BuyurtmaBildirishnoma = Bildirishnoma.discriminator('BuyurtmaBildirishnoma', new Schema({
buyurtmaId: { type: Schema.Types.ObjectId, ref: 'Buyurtma', required: true },
holat: { type: String, enum: ['qabul', 'yolda', 'yetkazildi'], required: true },
}))
export const IzohBildirishnoma = Bildirishnoma.discriminator('IzohBildirishnoma', new Schema({
postId: { type: Schema.Types.ObjectId, ref: 'Post', required: true },
kimdan: { type: Schema.Types.ObjectId, ref: 'User', required: true },
izohMatni: { type: String, maxlength: 200 },
}))
export const TizimBildirishnoma = Bildirishnoma.discriminator('TizimBildirishnoma', new Schema({
daraja: { type: String, enum: ['info', 'warning'], default: 'info' },
matn: { type: String, required: true },
havola: String,
}))
discriminatorKey: 'tur' — standart __t o'rniga tushunarli nom.
Ishlatish
Yaratish
await BuyurtmaBildirishnoma.create({ user: mijozId, buyurtmaId, holat: 'yolda' })
// bazada: { tur: "BuyurtmaBildirishnoma", user, buyurtmaId, holat: "yolda", oqildi: false, ... }
await BuyurtmaBildirishnoma.create({ user: mijozId, holat: 'yolda' })
// ValidationError: buyurtmaId: Path `buyurtmaId` is required.
Har bir tur o'z validatsiyasiga ega.
O'qish
// barcha turlar — asosiy model orqali
const hammasi = await Bildirishnoma.find({ user: userId }).sort({ createdAt: -1 }).limit(20).lean()
// faqat bitta tur — avtomatik { tur: "IzohBildirishnoma" } filtri qo'shiladi
const izohlar = await IzohBildirishnoma.find({ user: userId })
// frontend da tur bo'yicha ko'rsatish
for (const b of hammasi) {
switch (b.tur) {
case 'BuyurtmaBildirishnoma': /* ... */ break
case 'IzohBildirishnoma': /* ... */ break
}
}
Umumiy amallar
// barcha turlarni o'qilgan deb belgilash — bitta so'rov
await Bildirishnoma.updateMany({ user: userId, oqildi: false }, { oqildi: true })
// o'qilmaganlar soni
await Bildirishnoma.countDocuments({ user: userId, oqildi: false })
Turga xos metodlar va middleware
const izohSchema = new Schema({ ... })
izohSchema.methods.xabarMatni = function () {
return `Postingizga yangi izoh: "${this.izohMatni}"`
}
izohSchema.pre('save', function () {
if (this.izohMatni?.length > 200) this.izohMatni = this.izohMatni.slice(0, 197) + '...'
})
export const IzohBildirishnoma = Bildirishnoma.discriminator('IzohBildirishnoma', izohSchema)
Asosiy schema dagi middleware va metodlar barcha turlarga meros bo'lib o'tadi.
[!INFO] Ichki massivlar uchun ham discriminator bor: masalan, sahifa tarkibi
bloklar: [blokSchema]— matn bloki, rasm bloki, video bloki.schema.path('bloklar').discriminator('Rasm', rasmSchema).
Qachon discriminator
✅ Mos:
- Turlar umumiy maydonlarga ega va ko'pincha birga so'raladi (lenta, bildirishnomalar, to'lovlar, hodisalar)
- Turlar soni cheklangan va barqaror
❌ Mos emas:
- Turlar butunlay har xil va hech qachon birga so'ralmaydi — alohida collection lar oddiyroq
- Har tur uchun index lar juda farq qiladi va collection ulkan
[!TIP] Index lar butun collection uchun umumiy. Faqat bitta turga tegishli maydonga index kerak bo'lsa —
partialFilterExpression: { tur: 'IzohBildirishnoma' }bilan partial index (27-dars) yarating.
Tez-tez uchraydigan xatolar
Turni asosiy model orqali yaratish
// ❌ — turga xos maydonlar schema da yo'q, jim tashlab yuboriladi
await Bildirishnoma.create({ tur: 'IzohBildirishnoma', postId, izohMatni })
// ✅
await IzohBildirishnoma.create({ postId, izohMatni, ... })
Discriminator kalitini qo'lda o'zgartirish
tur ni update da o'zgartirish — document boshqa schema ga "o'tadi" va eski turga xos maydonlar noto'g'ri bo'lib qoladi. Tur o'zgarmas deb hisoblang.
Turlar uchun alohida collection opsiyasi
Discriminator har doim asosiy model collection ida — turga alohida collection berib bo'lmaydi.
Xulosa
- Discriminator — bitta collection da bir nechta schema; tur maxsus kalit bilan belgilanadi
- Asosiy schema — umumiy maydonlar;
Model.discriminator('Nom', schema)— turlar discriminatorKey—__to'rniga tushunarli nom- Asosiy model — barcha turlar; tur modeli — avtomatik filtr va o'z validatsiyasi
- Middleware va metodlar meros bo'ladi; turga xos index — partial index
- Birga so'raladigan, umumiy maydonli turlar uchun
Keyingi dars: 52-dars — MongoDB Atlas — cloud database, cluster yaratish