Plugins — mongoose-paginate, mongoose-unique-validator

~15 daq · MongoDB · Mongoose — Ilg'or

Muammodan boshlaylik

Loyihada 12 ta model. Har birida bir xil narsalar:

  • Sahifalash: skip, limit, countDocuments, sahifalar hisoblash — 12 marta nusxalangan
  • createdBy, updatedBy maydonlari va ularni to'ldirish
  • Soft delete: ochirilgan maydoni va find dagi filtr

Bitta joyda xato tuzatilsa — qolgan 11 tasida qoladi.


Plugin nima?

Plugin — schema ga maydonlar, metodlar, middleware va boshqa imkoniyatlarni qayta ishlatiladigan tarzda qo'shuvchi funksiya.

function plugin(schema, options) {
  // schema ni kengaytirish
}

mahsulotSchema.plugin(plugin, { ...opsiyalar })

Hayotiy o'xshatma

Telefon uchun g'ilof va qo'shimcha batareya. Telefon (schema) o'zi ishlaydi, lekin unga tayyor qo'shimcha kiyib, yangi imkoniyat olasiz. Bir xil g'ilofni bir xil modeldagi har qanday telefonga kiyish mumkin — har biri uchun alohida yasash shart emas.


O'z plaginingiz

Vaqt va muallif

// src/plugins/muallif.js
export function muallifPlugin(schema) {
  schema.add({
    yaratdi: { type: schema.base.Schema.Types.ObjectId, ref: 'User', immutable: true },
    ozgartirdi: { type: schema.base.Schema.Types.ObjectId, ref: 'User' },
  })

  schema.methods.muallifniBelgila = function (userId) {
    if (this.isNew) this.yaratdi = userId
    this.ozgartirdi = userId
    return this
  }
}
import { muallifPlugin } from '../plugins/muallif.js'

mahsulotSchema.plugin(muallifPlugin)
postSchema.plugin(muallifPlugin)
const m = new Mahsulot(malumot)
await m.muallifniBelgila(req.user.id).save()

Sahifalash

// src/plugins/sahifalash.js
export function sahifalashPlugin(schema, { maxHajm = 50 } = {}) {
  schema.statics.sahifala = async function (filtr = {}, { sahifa = 1, hajm = 20, sort = { _id: -1 }, select } = {}) {
    sahifa = Math.max(1, Number(sahifa) || 1)
    hajm = Math.min(maxHajm, Math.max(1, Number(hajm) || 20))

    const [royxat, jami] = await Promise.all([
      this.find(filtr).select(select).sort(sort).skip((sahifa - 1) * hajm).limit(hajm).lean(),
      this.countDocuments(filtr),
    ])

    return {
      royxat,
      jami,
      sahifa,
      hajm,
      sahifalar: Math.ceil(jami / hajm),
      keyingisiBor: sahifa * hajm < jami,
    }
  }
}
mahsulotSchema.plugin(sahifalashPlugin, { maxHajm: 100 })

const natija = await Mahsulot.sahifala({ kategoriya: 'telefon' }, { sahifa: req.query.sahifa, sort: { narx: 1 } })

Global plugin

Barcha schema larga:

import mongoose from 'mongoose'
import { sahifalashPlugin } from './plugins/sahifalash.js'

mongoose.plugin(sahifalashPlugin)   // modellar yaratilishidan OLDIN

[!WARNING] mongoose.plugin() faqat undan keyin yaratilgan modellarga ta'sir qiladi. Uni ilovaning eng boshida, modellar import qilinishidan oldin chaqiring.


mongoose-paginate-v2

Tayyor sahifalash plagini:

npm install mongoose-paginate-v2
import mongoosePaginate from 'mongoose-paginate-v2'

mahsulotSchema.plugin(mongoosePaginate)
const natija = await Mahsulot.paginate(
  { kategoriya: 'telefon' },
  {
    page: 2,
    limit: 20,
    sort: { narx: 1 },
    select: 'nomi narx rasm',
    populate: { path: 'sotuvchi', select: 'ism' },
    lean: true,
  }
)
{
  docs: [ ... ],
  totalDocs: 348,
  limit: 20,
  page: 2,
  totalPages: 18,
  hasNextPage: true,
  hasPrevPage: true,
  nextPage: 3,
  prevPage: 1
}

Javob maydonlarini o'zbekchalashtirish — customLabels: { docs: 'royxat', totalDocs: 'jami' }. Aggregation uchun — mongoose-aggregate-paginate-v2.


mongoose-unique-validator

unique: true xatosi (E11000) — ValidationError emas va boshqa validatsiya xatolari bilan birga kelmaydi (41-dars). Plagin uni oddiy validatsiya xatosiga aylantiradi:

npm install mongoose-unique-validator
import uniqueValidator from 'mongoose-unique-validator'

userSchema.plugin(uniqueValidator, { message: '{PATH} "{VALUE}" allaqachon band' })
// ValidationError: email: email "lola@mail.uz" allaqachon band

[!INFO] Plagin saqlashdan oldin qo'shimcha so'rov bilan tekshiradi — bu poyga holatidan (race condition) himoya qilmaydi. Index baribir kerak va 11000 xatosi baribir kelishi mumkin. Plagin faqat qulay xabar uchun. Plaginning Mongoose versiyangizga mosligini package.json dagi peerDependencies dan tekshiring.


Boshqa foydali plaginlar

Plagin Vazifasi
mongoose-lean-virtuals lean() da virtual lar (45-dars)
mongoose-delete Soft delete (57-dars)
mongoose-autopopulate ref larni avtomatik populate
mongoose-sequence Avtomatik o'suvchi raqam (buyurtmaRaqami: 1001)
mongoose-slug-updater Slug yasash

[!TIP] Har bir plagin — qo'shimcha bog'liqlik va "yashirin" xatti-harakat. Oddiy narsalar uchun (sahifalash, muallif) o'z 20 qatorli plaginingiz ko'pincha yaxshiroq: siz uni to'liq tushunasiz va Mongoose yangilanganda u buzilmaydi.


Tez-tez uchraydigan xatolar

Plagin ni modeldan keyin ulash

const Mahsulot = mongoose.model('Mahsulot', mahsulotSchema)
mahsulotSchema.plugin(mongoosePaginate)   // ❌ kech

autopopulate bilan cheksiz sikl

User → Post → User... avtomatik populate zanjiri. Chuqurlikni cheklang yoki aniq populate ishlating.

Eskirgan plaginlar

Ko'p eski plaginlar yangi Mongoose bilan ishlamaydi (remove hook, callback lar). O'rnatishdan oldin oxirgi yangilanish sanasini tekshiring.


Xulosa

  • Plugin — schema ni kengaytiruvchi qayta ishlatiladigan funksiya: schema.plugin(fn, opts)
  • Ichida: schema.add, schema.methods, schema.statics, schema.pre
  • Global — mongoose.plugin(), modellardan oldin
  • mongoose-paginate-v2 — tayyor sahifalash: Model.paginate(filtr, opts)
  • mongoose-unique-validator — 11000 ni chiroyli xabarga; index baribir kerak
  • Oddiy ehtiyojlar uchun — o'z plaginingiz

Keyingi dars: 49-dars — Mongoose bilan Aggregation Pipeline