Statics va Methods — model va instance metodlari

~15 daq · MongoDB · Mongoose — Ilg'or

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()
  • query helpers — 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