Migration — migrate-mongo bilan schema o'zgarishlarini boshqarish
Muammodan boshlaylik
Yangi talab: users dagi ism maydoni ism va familiya ga bo'linsin. Mongoose schema ni o'zgartirdingiz, deploy qildingiz:
- Yangi userlar — to'g'ri
- 200 000 ta eski user da hali
ism: "Lola Karimova"vafamiliyayo'q — sahifalarda "Lola Karimova undefined" - Hamkasbingizning lokal bazasida ham, staging da ham eski ma'lumotlar
- Kimdir mongosh da qo'lda
updateManyyozdi — lekin faqat production da. Staging da esa unutildi
"MongoDB schemaless — migratsiya kerak emas" — bu afsona. Schema kodda bor; ma'lumotlar unga mos bo'lishi kerak.
Migration nima?
Migration — ma'lumotlar yoki baza tuzilmasini o'zgartiradigan, versiyalangan va takrorlanadigan skript.
- Har bir o'zgarish — alohida fayl, git da
- Qaysi migratsiyalar bajarilgani bazada saqlanadi
- Har bir muhitda (lokal, staging, production) bir xil tartibda bajariladi
- Iloji bo'lsa — orqaga qaytarish (
down)
Hayotiy o'xshatma
Uy ta'mirining jurnali: "1-mart — elektr simlari almashtirildi", "15-mart — devorlar bo'yaldi". Yangi usta kelsa — jurnalga qarab, nima qilingan va nima qilinmaganini biladi. Xuddi shu ta'mirni boshqa xonadonda takrorlash kerak bo'lsa — jurnal bo'yicha qadamma-qadam bajaradi.
migrate-mongo
npm install -D migrate-mongo
npx migrate-mongo init
// migrate-mongo-config.js
const config = {
mongodb: {
url: process.env.MONGODB_URI,
},
migrationsDir: 'migrations',
changelogCollectionName: 'changelog',
migrationFileExtension: '.js',
useFileHash: false,
moduleSystem: 'esm',
}
export default config
Migratsiya yaratish
npx migrate-mongo create ism-familiyaga-bolish
# migrations/20261108120000-ism-familiyaga-bolish.js
// migrations/20261108120000-ism-familiyaga-bolish.js
export const up = async (db) => {
await db.collection('users').updateMany(
{ familiya: { $exists: false }, ism: { $type: 'string' } },
[
{ $set: {
_qismlar: { $split: [{ $trim: { input: '$ism' } }, ' '] },
} },
{ $set: {
ism: { $arrayElemAt: ['$_qismlar', 0] },
familiya: {
$reduce: {
input: { $slice: ['$_qismlar', 1, { $size: '$_qismlar' }] },
initialValue: '',
in: { $trim: { input: { $concat: ['$value', ' ', '$this'] } } },
},
},
} },
{ $unset: '_qismlar' },
]
)
}
export const down = async (db) => {
await db.collection('users').updateMany(
{ familiya: { $exists: true } },
[
{ $set: { ism: { $trim: { input: { $concat: ['$ism', ' ', '$familiya'] } } } } },
{ $unset: 'familiya' },
]
)
}
Update ichidagi aggregation pipeline (massiv) — bitta so'rovda har bir documentning o'z qiymatlaridan foydalanish. 200 000 ta documentni JavaScript ga yuklash shart emas.
Bajarish
npx migrate-mongo status # qaysilari bajarilgan, qaysilari kutmoqda
npx migrate-mongo up # kutayotganlarning hammasini bajarish
npx migrate-mongo down # oxirgisini qaytarish
┌─────────────────────────────────────────────┬──────────────────────────┐
│ Filename │ Applied At │
├─────────────────────────────────────────────┼──────────────────────────┤
│ 20261020093000-mahsulot-indexlari.js │ 2026-10-20T09:31:12.000Z │
│ 20261108120000-ism-familiyaga-bolish.js │ PENDING │
└─────────────────────────────────────────────┴──────────────────────────┘
changelog collection ida qaysi fayllar qachon bajarilgani saqlanadi.
Index migratsiyasi
Production da autoIndex: false (38-dars) — index lar migratsiya orqali:
export const up = async (db) => {
await db.collection('buyurtmalar').createIndex(
{ holat: 1, createdAt: -1 },
{ name: 'holat_sana' }
)
await db.collection('users').createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { deletedAt: null }, name: 'email_unique' }
)
}
export const down = async (db) => {
await db.collection('buyurtmalar').dropIndex('holat_sana')
await db.collection('users').dropIndex('email_unique')
}
Deploy ga qo'shish
{
"scripts": {
"migrate": "migrate-mongo up",
"migrate:status": "migrate-mongo status"
}
}
CI/CD da (Express kursi, 67-dars) — yangi kodni ishga tushirishdan oldin:
npm ci && npm run migrate && pm2 reload dokon-api
Xavfsiz migratsiya qoidalari
[!WARNING] Deploy vaqtida bir muddat eski kod va yangi ma'lumotlar (yoki aksincha) birga ishlaydi. Migratsiyalarni orqaga mos qiling: avval yangi maydon qo'shiladi va kod ikkala shaklni ham tushunadi; eski maydon esa keyingi deploy da, hamma narsa yangi shaklga o'tgandan keyin olib tashlanadi.
- Idempotent — ikki marta bajarilsa ham zarar bo'lmasin:
{ familiya: { $exists: false } }filtri shu uchun - Bo'laklab — millionlab document uchun bitta ulkan
updateManyemas, partiyalar bilan (masalan, 10 000 tadan) - Avval zaxira — katta o'zgarishdan oldin
mongodump(34-dars) - Staging da sinash — production nusxasiga yaqin ma'lumotlarda
- Kod bilan bir commit da — schema o'zgarishi va uning migratsiyasi birga ko'rib chiqiladi
Bo'laklab yangilash
export const up = async (db) => {
const col = db.collection('buyurtmalar')
let jami = 0
while (true) {
const idlar = await col.find({ valyuta: { $exists: false } }, { projection: { _id: 1 } })
.limit(10000).map((d) => d._id).toArray()
if (idlar.length === 0) break
const natija = await col.updateMany({ _id: { $in: idlar } }, { $set: { valyuta: 'UZS' } })
jami += natija.modifiedCount
console.log(`Yangilandi: ${jami}`)
}
}
Mongoose modellari bilan emas
Migratsiyada Mongoose model larini import qilmang — to'g'ridan-to'g'ri db.collection() bilan ishlang. Sabab: model kelajakda o'zgaradi (maydon o'chiriladi, validatsiya qo'shiladi), va eski migratsiya yangi model bilan buziladi. Migratsiya — o'z yozilgan paytidagi holatni ifodalashi kerak.
[!TIP] Kichik o'zgarishlar uchun migratsiya o'rniga "dangasa" (lazy) yondashuv ham bor: kod eski va yangi shaklni o'qiydi, document saqlanganda yangi shaklga o'tkaziladi. Bu juda katta collection larda qulay, lekin eski shakl uzoq vaqt qoladi va kod murakkablashadi.
Tez-tez uchraydigan xatolar
Production da qo'lda o'zgartirish
mongosh dagi updateMany — hech qayerda yozilmagan, staging va lokal bazalar farqlanib qoladi.
Migratsiya faylini bajarilgandan keyin tahrirlash
U allaqachon changelog da "bajarildi" deb belgilangan — o'zgarishlar boshqa muhitlarda ishlamaydi. Yangi migratsiya yarating.
down ni yozmaslik yoki sinamaslik
Muammo chiqsa — orqaga qaytish yo'li yo'q. Har doim qaytarib bo'lmasa ham (ma'lumot o'chirilgan bo'lsa), buni faylda aniq yozing.
Xulosa
- MongoDB da ham migratsiya kerak — schema kodda, ma'lumot unga mos bo'lishi kerak
migrate-mongo:create,up,down,status; holat —changelogcollection ida- Update pipeline — bitta so'rovda document qiymatlaridan foydalanish
- Index lar — migratsiya orqali (
autoIndex: false) - Deploy da — kod ishga tushishidan oldin
npm run migrate - Orqaga mos, idempotent, bo'laklab; avval zaxira; Mongoose model larini import qilmang
Keyingi dars: 60-dars — Real loyiha — to'liq schema loyihalash, Mongoose bilan yozish