Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. relations() va db.query
- 2.1 Bog'lanishni e'lon qilish
- 2.2 with — ichma-ich o'qish
- 2.3 Ko'p-bir tomoni
- 2.4 Drizzle 1.0 haqida
- 3. Tranzaksiyalar
- 3.1 Hammasi yoki hech narsa
- 3.2 Savepoint
- 3.3 Izolyatsiya va o'qish rejimi
- 4. drizzle-kit: migratsiyalar
- 4.1 drizzle.config.ts
- 4.2 generate — sxemadan SQL fayl
- 4.3 migrate — qo'llash
- 4.4 O'zgarish: yangi ustun
- 4.5 Tuzoq: nom o'zgarishi va TTY
- 4.6 push — faylsiz
- 4.7 pull — mavjud bazadan sxema
- 4.8 Studio
- 5. Ko'p uchraydigan xatolar
- 6. Mashqlar
- 1-mashq (oson): qaysi buyruq?
- 2-mashq (o'rta): qaysi migratsiyalar qo'llanadi?
- 3-mashq (qiyin): vazifa, egasi va izohlar soni
- 7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 8. Real ishda
- Xulosa
- Manbalar
Drizzle relations va drizzle-kit: db.query, tranzaksiya, generate, migrate va push
Qisqacha: Drizzle'da bog'lanishlar
relations()bilan e'lon qilinadi vadb.query.foydalanuvchilar.findFirst({ with: { vazifalar: … } })— Prisma'dagiincludega o'xshash, lekin bitta SQL (LEFT JOIN LATERAL+json_agg). Tranzaksiya —db.transaction(async (tx) => …), ichma-ichi — savepoint. Migratsiya vositasi drizzle-kit:generatesxema farqidan SQL fayl yasaydi,migrateuni qo'llaydi,pushfaylsiz to'g'ridan qo'llaydi. Biz topgan tuzoq: ustun nomi o'zgarsa,generate"nomi o'zgardimi yoki yangimi?" deb so'raydi va TTY'siz (CI'da) xato bilan to'xtaydi.
Bu darsda
relations()bilan bir-ko'p va ko'p-bir bog'lanishlarni e'lon qilasiz.db.query…findMany({ with })bilan ichma-ich ma'lumot olasiz va uning SQL'ini o'qiysiz.- Tranzaksiya, savepoint va izolyatsiya darajasini Drizzle'da yozasiz.
drizzle.config.tsni yozib,generate,migrate,push,pullni ishlatasiz.generatevapushfarqini, rename tuzog'ini tushuntirasiz.
Oldin bilishingiz kerak: Drizzle ORM 1: sxema va so'rovlar, Prisma: nested write va tranzaksiyalar, Subquery va LATERAL, Bog'lanishlar: 1-1, 1-N va N-M.
1. Nega bu kerak?
Oldingi darsda Drizzle bilan bitta jadvaldan o'qidik va JOIN yozdik. JOIN natijasi esa tekis qatorlar: Azizning 3 ta izohli vazifasi bo'lsa, JOIN har izoh uchun alohida qator beradi va ularni "vazifa → izohlar ro'yxati" ko'rinishiga JavaScript'da o'zingiz yig'asiz.
API'ga esa ichma-ich JSON kerak: { ism, vazifalar: [{ matn, izohlar: [...] }] }. Prisma'da bu include edi. Drizzle'da — relational queries (db.query): siz bog'lanishni bir marta e'lon qilasiz, Drizzle ichma-ich natijani bitta SQL bilan yig'adi.
Ikkinchi savol — sxema o'zgarishi. Drizzle sxemasi .ts faylda. Yangi ustun qo'shsangiz, bazaga ALTER TABLE kim yozadi? drizzle-kit — Drizzle'ning migratsiya vositasi.
O'xshatish: oila daraxti. Har odamning "otasi" va "bolalari" bor — buni bir marta chizasiz (relations), keyin "Azizning hamma nevaralarini ko'rsat" degan savolga daraxtning o'zi javob beradi.
Chiqishlarni biz ishga tushirdik: PostgreSQL 17.6, drizzle-orm 0.45.4, drizzle-kit 0.31.11, pg 8.23.1, Node 24.21.0. Sxema — oldingi darsdagi sxema.ts.
2. relations() va db.query
2.1 Bog'lanishni e'lon qilish
import { relations } from "drizzle-orm";
const foydalanuvchiRel = relations(foydalanuvchilar, ({ many }) => ({
vazifalar: many(vazifalar),
}));
const vazifaRel = relations(vazifalar, ({ one, many }) => ({
egasi: one(foydalanuvchilar, {
fields: [vazifalar.egasiId], references: [foydalanuvchilar.id],
}),
izohlar: many(izohlar),
}));
const izohRel = relations(izohlar, ({ one }) => ({
vazifa: one(vazifalar, {
fields: [izohlar.vazifaId], references: [vazifalar.id],
}),
}));one — bitta bog'langan yozuv (vazifaning egasi), many — ko'p (foydalanuvchining vazifalari). fields/references faqat one tomonda: "qaysi ustun qaysiga ishora qiladi". many tomoni juftini o'zi topadi.
Muhim: relations() — bazadagi tashqi kalit emas. U faqat Drizzle'ning so'rov quruvchisi uchun xarita. FK cheklovi bazada .references() yoki SQL migratsiya bilan yaratiladi (Foreign key).
Klientga sxema va relation'lar beriladi:
const db = drizzle(pool, {
schema: {
foydalanuvchilar, vazifalar, izohlar,
foydalanuvchiRel, vazifaRel, izohRel,
},
});2.2 with — ichma-ich o'qish
const r = await db.query.foydalanuvchilar.findFirst({
where: eq(foydalanuvchilar.id, 1),
columns: { ism: true },
with: {
vazifalar: {
columns: { matn: true },
where: and(isNull(vazifalar.ochirilganVaqt),
sql`${vazifalar.izohlarSoni} > 0`),
orderBy: [asc(vazifalar.id)],
limit: 2,
with: { izohlar: { columns: { matn: true } } },
},
},
});{
"ism": "Aziz Karimov",
"vazifalar": [
{
"matn": "JavaScript kursining 26-qismini boshlash #o'qish",
"izohlar": [
{ "matn": "Docker darslaridan boshladim" },
{ "matn": "psql buyruqlari esda qolmayapti — \\? yordam beradi" },
{ "matn": "JOIN tushunarli bo'ldi" }
]
},
{
"matn": "Postgres'ni Docker'da ko'tarish #o'qish #docker",
"izohlar": [{ "matn": "Port 5432 band ekan, 5499 ga o'tdim" }]
}
]
}(Bo'sh qatorlar qisqartirildi.) Uch daraja: foydalanuvchi → vazifalar → izohlar. SQL — bitta. U uzun; faqat ikki marta uchraydigan left join lateral va json_agg so'zlarini toping:
SQL: select "foydalanuvchilar"."ism", "foydalanuvchilar_vazifalar"."data" as "vazifalar" from "foydalanuvchilar" "foydalanuvchilar" left join lateral (select coalesce(json_agg(json_build_array("foydalanuvchilar_vazifalar"."matn", "foydalanuvchilar_vazifalar_izohlar"."data") order by "foydalanuvchilar_vazifalar"."id" asc), '[]'::json) as "data" from (select * from "vazifalar" "foydalanuvchilar_vazifalar" where (… "ochirilgan_vaqt" is null and … "izohlar_soni" > 0)) order by … limit $1) "foydalanuvchilar_vazifalar" left join lateral (select coalesce(json_agg(json_build_array("foydalanuvchilar_vazifalar_izohlar"."matn")), '[]'::json) as "data" from "izohlar" … where … "vazifa_id" = "foydalanuvchilar_vazifalar"."id") "foydalanuvchilar_vazifalar_izohlar" on true) "foydalanuvchilar_vazifalar" on true where "foydalanuvchilar"."id" = $2 limit $3 -- [2,1,1]Nima bo'ldi? Har with darajasi — bitta LEFT JOIN LATERAL (Subquery va LATERAL), natija Postgres'da json_agg bilan yig'iladi. Prisma'ning relationLoadStrategy: "join" (preview) bilan qiladigan ishi Drizzle'da sukut (Prisma prodakshnda). Kanon o'lchovida (50 vazifa + izohlari) Drizzle bitta SQL bilan 1.61 ms oldi; pg qo'lda json_agg bilan — 1.34 ms.
db.query da where, orderBy, limit, columns — har darajada alohida. columns — Prisma'dagi select.
2.3 Ko'p-bir tomoni
await db.query.vazifalar.findMany({
where: eq(vazifalar.id, 16),
columns: { matn: true },
with: { egasi: { columns: { ism: true } } },
});[ { matn: 'Bozordan meva olish #bozor', egasi: { ism: 'Dilnoza Rahimova' } } ]one — obyekt (yoki null, agar bog'lanish ixtiyoriy bo'lsa), many — massiv.
flowchart LR
F["foydalanuvchilar"] -- "many: vazifalar" --> V["vazifalar"]
V -- "one: egasi" --> F
V -- "many: izohlar" --> I["izohlar"]
I -- "one: vazifa" --> VDiagrammaga qarang: har bog'lanish ikki tomondan e'lon qilinadi — many va unga juft one. Shunda with ikkala yo'nalishda ishlaydi.
Tekshirib ko'ring:
relations()e'lonini qo'shdingiz, lekin bazada FK yo'q.withishlaydimi? Bazaga yetim izoh yozish mumkinmi?
Javob
with ishlaydi — u faqat fields/references bo'yicha JOIN yasaydi. Yetim izohni yozish ham mumkin bo'ladi: relations() hech narsani tekshirmaydi. Butunlikni faqat bazadagi FK cheklovi kafolatlaydi (Foreign key).
2.4 Drizzle 1.0 haqida
Drizzle 1.0 (2026-10-25 holatida RC) relation e'lonining yangi shaklini olib keladi — defineRelations (relational queries v2): hamma bog'lanishlar bitta joyda, where obyekt ko'rinishida. Biz barqaror 0.45.4 ni o'rgandik; 1.0 barqaror bo'lgach, g'oya o'sha qoladi — sintaksis o'zgaradi. Yangi loyihada versiyani package.json da aniq yozing.
3. Tranzaksiyalar
3.1 Hammasi yoki hech narsa
await db.transaction(async (tx) => {
await tx.insert(vazifalar).values({ egasiId: 2, matn: "Tx 1" });
await tx.insert(izohlar)
.values({ vazifaId: 999999, matn: "yo'q vazifaga izoh" });
});xato: 23503 izohlar_vazifa_id_fkey
vazifalar soni o'zgardimi: false
SQL: begin -- []
SQL: insert into "vazifalar" (…) values (default, $1, $2, default, …) -- [2,"Tx 1"]
SQL: insert into "izohlar" ("id", "vazifa_id", "muallif_id", "matn") values (default, $1, default, $2) -- [999999,"yo'q vazifaga izoh"]
SQL: rollback -- []Ikkinchi INSERT FK'ni buzdi (23503) — birinchisi ham qaytdi. Prisma'ning interaktiv $transaction i bilan bir xil (Prisma: nested write va tranzaksiyalar); farqi — Drizzle'da timeout yo'q, tx — oddiy pg ulanishi ustidagi tranzaksiya. Prisma'dagi BEGIN ko'rinmagan log'dan farqli o'laroq, bu yerda begin ham log'da.
3.2 Savepoint
await db.transaction(async (tx) => {
await tx.insert(vazifalar).values({ egasiId: 2, matn: "Tashqi" });
await tx.transaction(async (ichki) => {
await ichki.insert(vazifalar)
.values({ egasiId: 2, matn: "Ichki" });
ichki.rollback();
}).catch(() => console.log("ichki bekor qilindi"));
});ichki bekor qilindi
[ { matn: 'Tashqi' } ]
SQL: begin -- []
SQL: insert into "vazifalar" (…) -- [2,"Tashqi"]
SQL: savepoint sp1 -- []
SQL: insert into "vazifalar" (…) -- [2,"Ichki"]
SQL: rollback to savepoint sp1 -- []
SQL: commit -- []Log'ni qadamlarga bo'lib o'qiymiz:
| Qadam | Tashqi tx |
Ichki (sp1) |
Bazada |
|---|---|---|---|
| 1 | begin, "Tashqi" yozildi |
— | hali hech kim ko'rmaydi |
| 2 | — | savepoint sp1, "Ichki" yozildi |
ikkalasi tranzaksiya ichida |
| 3 | — | rollback to savepoint sp1 |
"Ichki" bekor, "Tashqi" qoldi |
| 4 | commit |
— | faqat "Tashqi" saqlandi |
Ichma-ich transaction — SAVEPOINT (Tranzaksiya asoslari): ichki qism bekor bo'ldi, tashqisi saqlandi. ichki.rollback() maxsus xato otadi — uni .catch bilan ushladik, aks holda tashqi tranzaksiya ham bekor bo'lardi.
3.3 Izolyatsiya va o'qish rejimi
await db.transaction(async (tx) => { await tx.$count(vazifalar); }, {
isolationLevel: "serializable", accessMode: "read only",
});SQL: begin isolation level serializable read only -- []
SQL: select count(*) as count from "vazifalar"; -- []
SQL: commit -- []Drizzle sozlamani to'g'ridan BEGIN ga yozdi. SERIALIZABLE bilan 40001 kelsa, butun funksiyani qayta urinish — sizning ishingiz (Deadlock va serialization failure).
4. drizzle-kit: migratsiyalar
4.1 drizzle.config.ts
drizzle-kit'ni yangi, bo'sh bazada sinaymiz — kichik "eslatmalar" ilovasi (kit/sxema.ts):
export const foydalanuvchilar = pgTable("foydalanuvchilar", {
id: bigint("id", { mode: "number" })
.primaryKey().generatedAlwaysAsIdentity(),
email: text("email").notNull().unique(),
ism: text("ism").notNull(),
});
export const eslatmalar = pgTable("eslatmalar", {
id: bigint("id", { mode: "number" })
.primaryKey().generatedAlwaysAsIdentity(),
egasiId: bigint("egasi_id", { mode: "number" }).notNull()
.references(() => foydalanuvchilar.id, { onDelete: "cascade" }),
matn: text("matn").notNull(),
vaqt: timestamp("vaqt", { withTimezone: true }).notNull(),
}, (t) => [index("eslatmalar_egasi_vaqt_idx").on(t.egasiId, t.vaqt)]);Konfiguratsiya fayli loyiha ildizida:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./kit/sxema.ts",
out: "./kit/drizzle",
dbCredentials: { url: process.env.DATABASE_URL! },
});schema — sxema fayli (yoki papka), out — migratsiyalar papkasi, dbCredentials — ulanish (faqat bazaga ulanadigan buyruqlar uchun). DATABASE_URL — muhit o'zgaruvchisidan, kodda parol yo'q.
4.2 generate — sxemadan SQL fayl
npx drizzle-kit generate --name boshlangich2 tables
eslatmalar 4 columns 1 indexes 1 fks
foydalanuvchilar 3 columns 0 indexes 0 fks
[✓] Your SQL migration file ➜ kit\drizzle\0000_boshlangich.sql 🚀Yasalgan fayl (0000_boshlangich.sql):
CREATE TABLE "eslatmalar" (
"id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "eslatmalar_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
"egasi_id" bigint NOT NULL,
"matn" text NOT NULL,
"vaqt" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "foydalanuvchilar" (
"id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "foydalanuvchilar_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1),
"email" text NOT NULL,
"ism" text NOT NULL,
CONSTRAINT "foydalanuvchilar_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "eslatmalar" ADD CONSTRAINT "eslatmalar_egasi_id_foydalanuvchilar_id_fk" FOREIGN KEY ("egasi_id") REFERENCES "public"."foydalanuvchilar"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "eslatmalar_egasi_vaqt_idx" ON "eslatmalar" USING btree ("egasi_id","vaqt");(drizzle-kit chiqishi aynan; uzun qatorlar telefonda gorizontal aylantiriladi.) Oddiy, o'qiladigan SQL. --> statement-breakpoint — buyruqlar chegarasi. FK jadvallardan keyin alohida ALTER TABLE bilan qo'shildi — jadvallar tartibi muhim bo'lmasin. Fayl yonida meta/0000_snapshot.json (snapshot — sxemaning o'sha paytdagi to'liq nusxasi, JSON'da) va meta/_journal.json (migratsiyalar ro'yxati) paydo bo'ldi. Keyingi generate yangi sxemani oxirgi snapshot bilan solishtiradi — bazaga ulanmaydi.
4.3 migrate — qo'llash
npx drizzle-kit migrate[✓] migrations applied successfully! Schema | Name | Type
--------------------+--------------------------+-------------
drizzle | __drizzle_migrations | table
id | hash | created_at
----+--------------+---------------
1 | c908513d87d1 | 1792921190959(psql chiqishidan qisqartirilgan.) drizzle-kit qo'llangan migratsiyalarni alohida drizzle sxemasidagi __drizzle_migrations jadvalida saqlaydi: fayl mazmunining hash'i va vaqti (millisekundlarda). Prisma'ning _prisma_migrations jadvalining o'rni (Prisma Migrate chuqur).
4.4 O'zgarish: yangi ustun
Sxemaga muhim: boolean("muhim").notNull().default(false) qo'shdik:
npx drizzle-kit generate --name muhim
npx drizzle-kit migrate
npx drizzle-kit generate[✓] Your SQL migration file ➜ kit\drizzle\0001_muhim.sql 🚀
ALTER TABLE "eslatmalar" ADD COLUMN "muhim" boolean DEFAULT false NOT NULL;
[✓] migrations applied successfully!
No schema changes, nothing to migrate 😴Ikkinchi generate — o'zgarish yo'q. ADD COLUMN … DEFAULT false NOT NULL — PG 11+ da katta jadvalda ham tez (metadata), lekin volatile default (now() kabi funksiya) jadvalni qayta yozadi — Xavfli migratsiyalar darsida kanon 11.8 s to'siqni o'lchagan.
4.5 Tuzoq: nom o'zgarishi va TTY
Ustun nomini matn → sarlavha qildik. Sxemada bu "bitta ustun yo'qoldi, bitta yangi paydo bo'ldi" — drizzle-kit nima qilganingizni bilmaydi: nomini o'zgartirdingizmi (RENAME COLUMN, ma'lumot saqlanadi) yoki eskisini o'chirib yangisini qo'shdingizmi (DROP + ADD, ma'lumot yo'qoladi)? U sizdan interaktiv so'raydi. CI'dagidek, terminalsiz ishga tushirdik:
Error: Interactive prompts require a TTY terminal (process.stdin.isTTY or process.stdout.isTTY is false). This can happen when running in CI, piped input, or non-interactive shells.Bu — yaxshi xato: jim DROP COLUMN dan ko'ra to'xtash yaxshiroq. Qoida: generate ni dasturchi o'z terminalida ishga tushiradi, savolga javob beradi, yasalgan SQL'ni o'qiydi va commit qiladi. CI va deploy faqat tayyor fayllarni migrate qiladi. Prod'da ustun nomini o'zgartirishning o'zi ham xavfli — eski ilova nusxalari eski nomni kutadi (Zero-downtime: expand-contract).
4.6 push — faylsiz
DATABASE_URL=…/drzpush npx drizzle-kit push[✓] Pulling schema from database...
[✓] Changes appliedpush bazani o'qiydi, sxema bilan solishtiradi va farqni to'g'ridan qo'llaydi — fayl yo'q, tarix yo'q, review yo'q. Lokal prototip va test bazasi uchun qulay; prod uchun — generate + migrate.
| Buyruq | Bazaga ulanadimi | Fayl yasaydimi | Qayerda |
|---|---|---|---|
generate |
yo'q (snapshot bilan) | ha, SQL | dasturchi terminali |
migrate |
ha | yo'q, qo'llaydi | deploy / CI |
push |
ha | yo'q | lokal prototip |
pull |
ha | ha, schema.ts |
mavjud bazadan boshlash |
4.7 pull — mavjud bazadan sxema
Kanon drizzle-kit pull ni bizning vazifalar bazasiga ishlatgan (holat 11 sxemasi):
pgTable(: 7
check(: 10
pgPolicy(: 5
pgView(: 1
pgMaterializedView(: 1
generatedAlwaysAs: 6
index(: 12
uniqueIndex(: 1
exclude: 0
trigger: 0
function: 0Drizzle Prisma'dan ko'p narsani ko'rdi: CHECK'lar, RLS siyosatlari, view, materialized view, generated ustun. Lekin EXCLUDE cheklovi, trigger va funksiyalar — yo'q. Kanon yana bir xatoni topdi: indeks op-class'lari noto'g'ri o'qilgan (egasi_id bigint uchun int4_ops), tsvector turi — unknown. Ya'ni pull natijasi — boshlang'ich qoralama, uni qo'lda tekshirish kerak.
4.8 Studio
npx drizzle-kit studio — ma'lumotlarni brauzerda ko'rish interfeysi (Drizzle hujjati bo'yicha local.drizzle.studio sahifasi lokal serveringizga ulanadi). Biz uni ochmadik — sinalmagan. Prisma Studio'dagidek: prod bazaga ulamang.
5. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
db.query.vazifalar — undefined |
drizzle(pool, { schema }) berilmagan |
schema ga jadval va relation'lar |
with da relation topilmadi |
ikki tomondan e'lon qilinmagan | many + juft one |
Interactive prompts require a TTY |
generate rename savoli CI'da |
lokal terminalda generate |
prod'da kutilmagan DROP COLUMN |
push ishlatilgan |
prod — faqat generate + migrate |
ichki rollback() hammasini bekor qildi |
maxsus xato ushlanmagan | .catch yoki xatoni ataylab otish |
pull dagi indeks noto'g'ri |
op-class xato o'qilgan | qo'lda tekshirish |
6. Mashqlar
Mashqlar kurs/mashqlar/27/30-drizzle-kit/ da. 2-mashq yechimi Node 24 da tekshirilgan (bazasiz); 3-mashqni biz bazada ishga tushirdik.
1-mashq (oson): qaysi buyruq?
- Sxema farqidan SQL fayl yasash —
drizzle-kit - Fayllarni bazaga qo'llash —
drizzle-kit - Faylsiz to'g'ridan qo'llash (faqat lokal) —
drizzle-kit - Mavjud bazadan
schema.tsyasash —drizzle-kit
Yechim
- generate — snapshot bilan solishtiradi, bazaga ulanmaydi.
- migrate —
__drizzle_migrationsga yozadi. - push — tarixsiz.
- pull — introspection; natijani tekshiring.
2-mashq (o'rta): qaysi migratsiyalar qo'llanadi?
drizzle-orm manba kodida (pg-core/dialect.js, 0.45.4) migrate shunday ishlaydi: __drizzle_migrations dan oxirgi qo'llangan created_at ni oladi va faqat when (fayl vaqti) undan katta migratsiyalarni qo'llaydi. Ikki funksiya yozing (bazasiz): drizzleQoidasi(journal, qollangan) — aynan shu qoida; toplamQoidasi(journal, qollangan) — "ro'yxatda bor, bazada yo'q" hammasi. Ikkalasi tag lar ro'yxatini idx tartibida qaytarsin. Jamoadagi holatni sinang: Ali 0002 ni qo'lladi, keyin Malikaning ertaroq vaqtli 0001 migratsiyasi main ga qo'shildi.
Yechim
const tartibla = (journal) =>
journal.entries.toSorted((a, b) => a.idx - b.idx);
function drizzleQoidasi(journal, qollangan) {
const oxirgi = Math.max(0, ...qollangan.map(Number));
return tartibla(journal)
.filter((e) => e.when > oxirgi)
.map((e) => e.tag);
}
function toplamQoidasi(journal, qollangan) {
const bor = new Set(qollangan.map(Number));
return tartibla(journal)
.filter((e) => !bor.has(e.when))
.map((e) => e.tag);
}
const journal = {
entries: [
{ idx: 0, when: 1000, tag: "0000_boshlangich" },
{ idx: 1, when: 1500, tag: "0001_malika_teg" },
{ idx: 2, when: 2000, tag: "0002_ali_muhim" },
],
};
const qollangan = ["1000", "2000"]; // 0001 hali qo'llanmagan
console.log(drizzleQoidasi(journal, qollangan));
console.log(toplamQoidasi(journal, qollangan));Konsolda:
[]
[ '0001_malika_teg' ]Oxirgi qo'llangan vaqt 2000 — Malikaning 1500 vaqtli migratsiyasi undan kichik, shuning uchun "oxirgidan keyingilar" qoidasi uni jim tashlab ketadi: bazada uning jadvali hech qachon paydo bo'lmaydi va xato ham chiqmaydi. To'plam qoidasi uni topadi. created_at bazadan satr bo'lib kelishi mumkin (bigint) — Number bilan solishtiramiz.
Amaliy xulosa: Drizzle bilan ishlaganda migratsiyalarni main ga vaqt tartibida qo'shing — boshqa branch'dagi eski migratsiyani merge qilgandan keyin uni generate bilan qayta yasang. Kanon holat 40 da (Migratsiyani deploy'da qo'llash) Prisma uchun to'plam qoidasi bilan tekshiruvni server ishga tushishiga qo'ygan.
3-mashq (qiyin): vazifa, egasi va izohlar soni
Sardor (3) ning birinchi uchta vazifasini (id bo'yicha) db.query bilan oling: har birida egasining ismi va izohlar. Chiqish: Ism: matn — N izoh. SQL soni 1 bo'lsin.
Yechim
const r = await db.query.vazifalar.findMany({
where: eq(vazifalar.egasiId, 3),
columns: { matn: true },
orderBy: [asc(vazifalar.id)],
limit: 3,
with: {
egasi: { columns: { ism: true } },
izohlar: { columns: { matn: true } },
},
});
for (const v of r) {
console.log(`${v.egasi.ism}: ${v.matn} — ${v.izohlar.length} izoh`);
}Biz ishga tushirdik (PostgreSQL 17.6, drizzle-orm 0.45.4; SQL'larni logger bilan sanadik):
Sardor Tursunov: Mashinani texnik ko'rikka olib borish #mashina — 2 izoh
Sardor Tursunov: Moy almashtirish #mashina — 0 izoh
Sardor Tursunov: Hovlidagi daraxtlarni oqlash #uy — 0 izoh
SQL soni: 1Ikki with (egasi — one, izohlar — many) — bitta SQL'da ikki LEFT JOIN LATERAL. v.egasi.ism turi string — egasiId NOT NULL bo'lgani uchun egasi null bo'lmaydi. Izohlar soni faqat kerak bo'lsa, butun izohlarni olish o'rniga extras bilan sql subquery yoki bazadagi izohlar_soni (counter cache) arzonroq.
7. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Drizzle'ga va drizzle-kit'ga ko'chmaydi. Sabablar:
- Sxema manbai — SQL migratsiyalar. Loyihaning sxemasida trigger'lar, funksiyalar, EXCLUDE cheklovi, RLS bor. Kanon
pullbilan ko'rsatdi: drizzle-kit EXCLUDE, trigger va funksiyalarni ko'rmaydi —generateular haqida hech narsa bilmaydi. Prisma ham ularni ko'rmaydi. Shuning uchun kanonda migratsiya — qo'lda yozilgan SQL, Prisma Migrate esa ularni faqat qo'llaydi (Prisma Migrate chuqur, holat 36). drizzle-kit bilan ham aynan shunday bo'lardi —generatedan foyda kam. - Bitta ORM — kanon qarori: Prisma asosiy, Drizzle — o'rganish uchun tajriba (ORM tanlash qo'llanmasi).
Lekin agar yangi loyihani noldan Drizzle bilan boshlasangiz va sxemangiz oddiy jadval, indeks va FK'lardan iborat bo'lsa — drizzle-kit'ning generate + migrate oqimi to'liq yetadi. Qo'shimcha SQL (trigger, CHECK) kerak bo'lsa, drizzle-kit generate --custom bo'sh SQL fayl yaratadi va unga o'zingiz yozasiz (Drizzle hujjati, sinalmagan).
8. Real ishda
- Drizzle loyihalarida odatiy tuzilma:
db/sxema.ts(jadvallar),db/relations.ts,drizzle.config.ts,drizzle/(migratsiyalar, git'da). PR'da sxema o'zgarishi bilan birga yasalgan SQL fayl ham ko'rib chiqiladi. - NestJS'da Drizzle rasmiy modulsiz — custom provider bilan ulanadi (Drizzle va Kysely bilan NestJS, 30-qism).
- Intervyuda: "
pushvamigratefarqi?", "drizzle-kit rename'ni qanday aniqlaydi?", "Relational queries qanday SQL beradi?"
Xulosa
relations()— so'rov quruvchisi uchun xarita (FK emas);one+manyikki tomondan.db.query…findMany({ with })— ichma-ich natija bitta SQL'da (LEFT JOIN LATERAL+json_agg).db.transaction— hammasi yoki hech narsa; ichma-ichi — savepoint;isolationLevel,accessModeto'g'ridanBEGINga.- drizzle-kit:
generate(snapshot → SQL fayl),migrate(__drizzle_migrations),push(faylsiz, faqat lokal),pull(qoralama). - Rename — interaktiv savol; TTY'siz xato.
generate— dasturchida,migrate— deploy'da.
Keyingi dars: Kysely va Knex query builder'lari — ORM'siz, lekin SQL satrlarisiz: tipli (Kysely) va klassik (Knex).
Manbalar
- Drizzle hujjati — Relational queries: https://orm.drizzle.team/docs/rqb (2026-10-25)
- Drizzle hujjati — Transactions: https://orm.drizzle.team/docs/transactions
- Drizzle hujjati — drizzle-kit overview (generate, migrate, push, pull, studio): https://orm.drizzle.team/docs/kit-overview
- Drizzle hujjati — drizzle.config.ts: https://orm.drizzle.team/docs/drizzle-config-file
- Chiqishlar — biz ishga tushirdik: PostgreSQL 17.6, drizzle-orm 0.45.4, drizzle-kit 0.31.11, Node 24.21.0 (2026-10-25);
pull— kanon (PostgreSQL 18.6).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!