Statics va Methods — model va instance metodlari
Muammodan boshlaylik
Bir xil so'rovlar va amallar loyiha bo'ylab takrorlanadi:
// 6 ta faylda
await User.findOne({ email: email.toLowerCase().trim() })
// 4 ta faylda
await Mahsulot.find({ faol: true, qoldiq: { $gt: 0 }, kategoriya })
// 3 ta faylda
user.oxirgiKirish = new Date()
user.kirishlarSoni += 1
await user.save()
Qoida o'zgarsa (masalan, "faol mahsulot" endi ochirilgan: false ni ham talab qiladi) — barcha joylarni topib o'zgartirish kerak.
Ikki xil metod
| Methods | Statics | |
|---|---|---|
| Kimga tegishli | Document ga (bitta yozuv) | Model ga (butun collection) |
| Chaqirish | user.parolTogrimi(p) |
User.emailBilanTop(e) |
this |
Document | Model |
| Misol | Parolni tekshirish, token yaratish | Qidiruv, statistika |
Hayotiy o'xshatma
Avtomobil zavodi. Methods — har bir mashinaning o'z funksiyalari: "dvigatelni yoq", "bakda qancha benzin bor" — aniq bitta mashina haqida. Statics — zavodning funksiyalari: "barcha qizil mashinalarni top", "bu oy nechta mashina chiqarildi" — butun ishlab chiqarish haqida.
Methods — document metodlari
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
userSchema.methods.parolTogrimi = function (kiritilgan) {
return bcrypt.compare(kiritilgan, this.parol)
}
userSchema.methods.tokenYarat = function () {
return jwt.sign({ sub: this._id, rol: this.rol }, process.env.JWT_SECRET, { expiresIn: '15m' })
}
userSchema.methods.kirishniQayd = function () {
this.oxirgiKirish = new Date()
this.kirishlarSoni += 1
return this.save()
}
userSchema.methods.ochiqMalumot = function () {
return { id: this._id, ism: this.ism, rasm: this.rasm }
}
const user = await User.findOne({ email }).select('+parol')
if (!user || !(await user.parolTogrimi(req.body.parol))) {
return res.status(401).json({ xato: "Email yoki parol noto'g'ri" })
}
await user.kirishniQayd()
res.json({ token: user.tokenYarat(), user: user.ochiqMalumot() })
Statics — model metodlari
userSchema.statics.emailBilanTop = function (email) {
return this.findOne({ email: email.toLowerCase().trim() })
}
mahsulotSchema.statics.sotuvdagilar = function (kategoriya) {
const filtr = { faol: true, qoldiq: { $gt: 0 } }
if (kategoriya) filtr.kategoriya = kategoriya
return this.find(filtr)
}
mahsulotSchema.statics.kategoriyaStatistikasi = function () {
return this.aggregate([
{ $match: { faol: true } },
{ $group: { _id: '$kategoriya', soni: { $sum: 1 }, ortachaNarx: { $avg: '$narx' } } },
{ $sort: { soni: -1 } },
])
}
const user = await User.emailBilanTop(req.body.email).select('+parol')
const telefonlar = await Mahsulot.sotuvdagilar('telefon').sort({ narx: 1 }).limit(20).lean()
const statistika = await Mahsulot.kategoriyaStatistikasi()
Statics Query qaytarsa — unga zanjirni (sort, limit, lean) davom ettirish mumkin.
Query helpers — zanjir uchun
mahsulotSchema.query.sotuvda = function () {
return this.where({ faol: true, qoldiq: { $gt: 0 } })
}
mahsulotSchema.query.narxOraligi = function (min, max) {
if (min != null) this.where('narx').gte(min)
if (max != null) this.where('narx').lte(max)
return this
}
await Mahsulot.find({ kategoriya: 'telefon' }).sotuvda().narxOraligi(1000000, 5000000).lean()
Query helper lar istalgan find zanjirida qayta ishlatiladigan "bo'laklar".
Schema ichida e'lon qilish
Alternativ — schema opsiyalarida:
const userSchema = new Schema({ ... }, {
methods: {
parolTogrimi(kiritilgan) {
return bcrypt.compare(kiritilgan, this.parol)
},
},
statics: {
emailBilanTop(email) {
return this.findOne({ email: email.toLowerCase().trim() })
},
},
})
Bu shakl TypeScript da turlarni avtomatik chiqarish uchun qulayroq.
[!WARNING] Metodlar va statics modeldan oldin (
mongoose.model(...)chaqirilishidan oldin) qo'shilishi kerak. Model yaratilgandan keyin schema ga qo'shilgan metodlar unga tushmaydi.
Qayerga nima
| Kod | Joy |
|---|---|
| Bitta documentga tegishli mantiq (parol, token, formatlash) | methods |
| Takrorlanuvchi so'rovlar, statistika | statics |
| Qayta ishlatiladigan filtr bo'laklari | query helpers |
| Hisoblangan qiymat (saqlanmaydigan) | virtual (45-dars) |
| Har doim avtomatik bajariladigan | middleware (46-dars) |
| Bir nechta model, tashqi xizmatlar, biznes jarayon | Service qatlami (Express kursi, 74-dars) |
[!TIP] Model ni "hamma narsa"ga aylantirmang. Email yuborish, to'lov tizimi bilan ishlash, bir nechta modelni o'zgartiradigan jarayonlar — service qatlamida. Model metodlari — faqat shu modelning o'z ma'lumotlari bilan bog'liq ish.
Tez-tez uchraydigan xatolar
Arrow function
// ❌ — this undefined
userSchema.methods.parolTogrimi = (p) => bcrypt.compare(p, this.parol)
lean() natijasida method chaqirish
const user = await User.findById(id).lean()
user.tokenYarat() // ❌ TypeError: not a function — oddiy obyekt
select: false maydonni ishlatadigan method
parolTogrimi — this.parol undefined, chunki parol so'rovda tanlanmagan. .select('+parol').
Xulosa
methods— document metodlari,this= document:user.parolTogrimi()statics— model metodlari,this= model:User.emailBilanTop()queryhelpers — zanjirli filtr bo'laklari:.sotuvda().narxOraligi()- Oddiy
function— arrow emas; model yaratilishidan oldin qo'shing lean()natijasida metodlar yo'q- Murakkab biznes jarayonlar — service qatlamida
Keyingi dars: 48-dars — Plugins — mongoose-paginate, mongoose-unique-validator